Skip to content

Commit 0b55bb7

Browse files
authored
docs: Fix documentation mismatches with actual client behavior (#958)
Fixes 12 documentation issues found by an engineering audit, where docs, examples, or docstrings contradict what the client actually does: - The custom HTTP client guide example (`HttpxClient`) now raises `ApifyApiError` for error responses instead of returning them raw, and the guide documents this part of the `call` contract (resource clients rely on it, e.g. to translate a 404 into a `None` return value). - The streaming concepts page no longer claims all three streaming methods yield a raw `impit.Response` — it now describes the actual yielded value per method (`stream_record` yields a `dict` with the response under `value`, `stream` and `stream_record` may yield `None`). - The pagination concepts page no longer lists `ListOfRequests` among page models exposing `total`/`offset`/`count`; it now explains its cursor-based pagination via `next_cursor`. - The logging formatter example no longer references `%(status_code)s` (absent on most records, causing logging errors) and no longer attaches a duplicate handler; the page notes which properties are present on every record. - The upgrading-to-v3 guide cross-links now include the site baseUrl (`/api/client/python/...`), fixing 6 links that 404ed on the published site. - The conda instruction for the brotli extra installs `brotli-python` (the Python bindings) instead of `brotli` (the C library). - `RunClient.resurrect` docstrings cite the real `SUCCEEDED` status instead of the nonexistent `FINISHED`. - `wait_for_finish` docstrings in `RunClient` and `BuildClient` spell the terminal status as `TIMED-OUT` (the real literal) instead of `TIMED_OUT`. - The quick-start page refers to the `Run` model's `default_dataset_id` attribute instead of the v2-era run dictionary with `defaultDatasetId`. - The README dataset example passes `fields` as `list[str]` per the signature instead of a comma-separated string. - The README quick-start examples handle the `Run | None` return of `call()` instead of accessing attributes on a possible `None`. - The timeouts concepts page states that `no_timeout` is capped at 24 hours by the default client instead of claiming it disables the timeout entirely. *✍️ Drafted by Claude Code*
1 parent 87c77ae commit 0b55bb7

14 files changed

Lines changed: 72 additions & 38 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ client = ApifyClient('MY-APIFY-TOKEN')
7979
run = client.actor('apify/hello-world').call(
8080
run_input={'message': 'Hello, Apify!'},
8181
)
82+
if run is None:
83+
raise RuntimeError('Actor run was not found.')
8284
8385
# Iterate items from the run's default dataset.
8486
for item in client.dataset(run.default_dataset_id).iterate_items():
@@ -99,6 +101,8 @@ async def main() -> None:
99101
run = await client.actor('apify/hello-world').call(
100102
run_input={'message': 'Hello, Apify!'},
101103
)
104+
if run is None:
105+
raise RuntimeError('Actor run was not found.')
102106
103107
# Iterate items from the run's default dataset.
104108
async for item in client.dataset(run.default_dataset_id).iterate_items():
@@ -158,7 +162,7 @@ record = store.get_record('greeting')
158162
### Iterate dataset items with automatic pagination
159163
160164
```python
161-
for item in client.dataset('DATASET-ID').iterate_items(fields='title,url'):
165+
for item in client.dataset('DATASET-ID').iterate_items(fields=['title', 'url']):
162166
process(item)
163167
```
164168

docs/01_introduction/index.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,17 @@ For better request-body compression, opt in to `brotli`, which compresses better
5656
</TabItem>
5757
<TabItem value="conda-forge" label="conda-forge">
5858
```bash
59-
conda install conda-forge::apify-client conda-forge::brotli
59+
conda install conda-forge::apify-client conda-forge::brotli-python
6060
```
6161
</TabItem>
6262
</Tabs>
6363

64+
:::note Extras on conda-forge
65+
66+
Conda doesn't support optional extras. On conda-forge, install the `brotli-python` package directly alongside the client. It provides the same `brotli` module as the PyPI extra.
67+
68+
:::
69+
6470
For details, see [HTTP compression](../02_concepts/13_http_compression.mdx).
6571

6672
## Quick example

docs/01_introduction/quick-start.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ To define the Actor's input, pass a dictionary to the <ApiLink to="class/ActorCl
6565

6666
## Step 3: Get results from the dataset
6767

68-
To get the results from the dataset, call the <ApiLink to="class/DatasetClient">`apify_client.dataset()`</ApiLink> method with the dataset ID, then call <ApiLink to="class/DatasetClient#list_items">`list_items()`</ApiLink> to retrieve the data. You can get the dataset ID from the Actor's run dictionary (represented by `defaultDatasetId`).
68+
To get the results from the dataset, call the <ApiLink to="class/DatasetClient">`apify_client.dataset()`</ApiLink> method with the dataset ID, then call <ApiLink to="class/DatasetClient#list_items">`list_items()`</ApiLink> to retrieve the data. You can get the dataset ID from the `default_dataset_id` attribute of the <ApiLink to="class/Run">`Run`</ApiLink> object returned by `call()`.
6969

7070
<Tabs>
7171
<TabItem value="AsyncExample" label="Async client" default>

docs/02_concepts/06_logging.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ The library logs useful debug information to the `apify_client` logger whenever
2020
The log records include additional properties, provided via the extra argument, which can be helpful for debugging. Some of these properties are:
2121

2222
- `attempt` - Number of retry attempts for the request.
23-
- `status_code` - HTTP status code of the response.
23+
- `status_code` - HTTP status code of the response. Only present on records about a request's outcome.
2424
- `url` - URL of the API endpoint being called.
2525
- `client_method` - Method name of the client that initiated the request.
2626
- `resource_id` - Identifier of the resource being accessed.
2727

28-
To display these additional properties in the log output, you need to use a custom log formatter. Here's a basic example:
28+
To display these additional properties in the log output, you need to use a custom log formatter. Reference only the properties present on every record, since a `%`-style formatter raises an error for records that lack a referenced property. Here's a basic example:
2929

3030
<CodeBlock className="language-python">
3131
{LoggingFormatterExample}

docs/02_concepts/08_pagination.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ import IterateItemsSyncExample from '!!raw-loader!./code/08_iterate_items_sync.p
1717
import IterateCollectionAsyncExample from '!!raw-loader!./code/08_iterate_collection_async.py';
1818
import IterateCollectionSyncExample from '!!raw-loader!./code/08_iterate_collection_sync.py';
1919

20-
Most methods named `list` or `list_something` in the Apify client return a page model — a [Pydantic](https://docs.pydantic.dev/latest/) model such as <ApiLink to="class/ListOfActors">`ListOfActors`</ApiLink>, <ApiLink to="class/ListOfDatasets">`ListOfDatasets`</ApiLink>, or <ApiLink to="class/ListOfRequests">`ListOfRequests`</ApiLink>. Unstructured dataset items use the <ApiLink to="class/DatasetItemsPage">`DatasetItemsPage`</ApiLink> dataclass instead. All page models share a consistent interface for working with paginated data and expose the following fields:
20+
Most methods named `list` or `list_something` in the Apify client return a page model — a [Pydantic](https://docs.pydantic.dev/latest/) model such as <ApiLink to="class/ListOfActors">`ListOfActors`</ApiLink> or <ApiLink to="class/ListOfDatasets">`ListOfDatasets`</ApiLink>. Unstructured dataset items use the <ApiLink to="class/DatasetItemsPage">`DatasetItemsPage`</ApiLink> dataclass instead. All page models share a consistent interface for working with paginated data and expose the following fields:
2121

2222
- `items` - The main results you're looking for.
2323
- `total` - The total number of items available.
2424
- `offset` - The starting point of the current page.
2525
- `count` - The number of items in the current page.
2626
- `limit` - The maximum number of items per page.
2727

28-
Some methods, such as `list_keys` or `list_head`, paginate differently. Regardless, the primary results are always stored under the `items` field, and the `limit` field can be used to control the number of results returned.
28+
Some methods paginate differently. For example, <ApiLink to="class/RequestQueueClient#list_requests">`RequestQueueClient.list_requests`</ApiLink> returns a cursor-based <ApiLink to="class/ListOfRequests">`ListOfRequests`</ApiLink> without the `total`, `offset`, and `count` fields. To fetch the next page, pass its `next_cursor` value back as the `cursor` parameter. Other examples include `list_keys` and `list_head`. Regardless, the primary results are always stored under the `items` field, and the `limit` field can be used to control the number of results returned.
2929

3030
The following example shows how to fetch all items from a dataset using pagination:
3131

docs/02_concepts/09_streaming.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ Certain resources, such as dataset items, key-value store records, and logs, sup
1717

1818
Supported streaming methods:
1919

20-
- <ApiLink to="class/DatasetClient#stream_items">`DatasetClient.stream_items`</ApiLink> - Stream dataset items incrementally.
21-
- <ApiLink to="class/KeyValueStoreClient#stream_record">`KeyValueStoreClient.stream_record`</ApiLink> - Stream key-value store records as raw data.
22-
- <ApiLink to="class/LogClient#stream">`LogClient.stream`</ApiLink> - Stream logs in real time.
20+
- <ApiLink to="class/DatasetClient#stream_items">`DatasetClient.stream_items`</ApiLink> - Stream dataset items incrementally. Yields a raw streaming <ApiLink to="class/HttpResponse">`HttpResponse`</ApiLink>.
21+
- <ApiLink to="class/KeyValueStoreClient#stream_record">`KeyValueStoreClient.stream_record`</ApiLink> - Stream a key-value store record as raw data. Yields a `dict` with the `key`, `value`, and `content_type` fields, where `value` holds the raw streaming response, or `None` when the record doesn't exist.
22+
- <ApiLink to="class/LogClient#stream">`LogClient.stream`</ApiLink> - Stream logs in real time. Yields a raw streaming <ApiLink to="class/HttpResponse">`HttpResponse`</ApiLink>, or `None` when the log doesn't exist.
2323

24-
These methods return a raw, context-managed `impit.Response` object. The response must be consumed within a with block to ensure that the connection is closed automatically, preventing memory leaks or unclosed connections.
24+
All three methods are context managers. Consume the streamed data within a `with` block to ensure that the connection is closed automatically, preventing memory leaks or unclosed connections.
2525

2626
The following example shows how to stream the logs of an Actor run incrementally:
2727

docs/02_concepts/11_timeouts.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The Apify client uses a tiered timeout system to set appropriate time limits for
1919
| `short` | 5 seconds | Fast CRUD operations (get, update, delete) |
2020
| `medium` | 30 seconds | Batch, list, and data transfer operations |
2121
| `long` | 360 seconds | Long-polling, streaming, and heavy operations |
22-
| `no_timeout` || Disables the timeout entirely |
22+
| `no_timeout` || Effectively disables the timeout (the default client caps it at 24 hours) |
2323

2424
Every client method has a pre-assigned tier that matches the expected duration of the underlying API call. You generally don't need to change these unless you're working with unusually large payloads or slow network conditions.
2525

@@ -53,7 +53,7 @@ client.dataset('id').list_items(timeout=timedelta(seconds=120))
5353
# Switch to a different tier.
5454
client.dataset('id').list_items(timeout='long')
5555

56-
# Disable the timeout entirely.
56+
# Effectively disable the timeout (capped at 24 hours by the default client).
5757
client.dataset('id').list_items(timeout='no_timeout')
5858
```
5959

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import logging
22

3-
# Configure the Apify client logger
4-
apify_client_logger = logging.getLogger('apify_client')
5-
apify_client_logger.setLevel(logging.DEBUG)
6-
apify_client_logger.addHandler(logging.StreamHandler())
7-
8-
# Create a custom logging formatter
3+
# Create a custom logging formatter. Reference only the properties present
4+
# on every record. Properties attached to some records only, such as
5+
# `status_code`, would raise an error for the records that lack them.
96
formatter = logging.Formatter(
107
'%(asctime)s - %(name)s - %(levelname)s - %(message)s - '
11-
'%(attempt)s - %(status_code)s - %(url)s'
8+
'%(client_method)s - %(attempt)s - %(url)s'
129
)
10+
1311
handler = logging.StreamHandler()
1412
handler.setFormatter(formatter)
13+
14+
# Configure the Apify client logger to use the custom formatter
15+
apify_client_logger = logging.getLogger('apify_client')
16+
apify_client_logger.setLevel(logging.DEBUG)
1517
apify_client_logger.addHandler(handler)

docs/03_guides/05_custom_http_client.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ The `call` method receives parameters like `method`, `url`, `headers`, `params`,
3333

3434
A convenient property of HTTPX is that its `httpx.Response` object already satisfies the <ApiLink to="class/HttpResponse">`HttpResponse`</ApiLink> protocol, so you can return it directly without wrapping.
3535

36+
One part of the contract isn't visible in the method signature: `call` must raise <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> for error responses instead of returning them. The resource clients rely on that error to work correctly. For example, `get` methods translate a 404 raised this way into a `None` return value, and user code handling `ApifyApiError` keeps working.
37+
3638
<Tabs>
3739
<TabItem value="AsyncExample" label="Async client" default>
3840
<CodeBlock className="language-python">

docs/03_guides/code/05_custom_http_client_async.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from __future__ import annotations
22

33
import asyncio
4+
from http import HTTPStatus
45
from typing import TYPE_CHECKING, Any
56

67
import httpx
78

89
from apify_client import ApifyClientAsync
10+
from apify_client.errors import ApifyApiError
911
from apify_client.http_clients import HttpClientAsync, HttpResponse
1012

1113
if TYPE_CHECKING:
@@ -39,9 +41,7 @@ async def call(
3941
# with the per-request ones.
4042
headers = self._merge_headers(self._headers, headers)
4143

42-
# httpx.Response satisfies the HttpResponse protocol,
43-
# so it can be returned directly.
44-
return await self._client.request(
44+
response = await self._client.request(
4545
method=method,
4646
url=url,
4747
headers=headers,
@@ -51,6 +51,16 @@ async def call(
5151
timeout=timeout_secs,
5252
)
5353

54+
# Raising `ApifyApiError` for error responses is part of the `call`
55+
# contract. The resource clients rely on it, e.g. to translate a 404
56+
# into a `None` return value of `get` methods.
57+
if response.status_code >= HTTPStatus.BAD_REQUEST:
58+
raise ApifyApiError(response, attempt=1, method=method)
59+
60+
# httpx.Response satisfies the HttpResponse protocol,
61+
# so it can be returned directly.
62+
return response
63+
5464

5565
async def main() -> None:
5666
client = ApifyClientAsync.with_custom_http_client(

0 commit comments

Comments
 (0)