Skip to content

Commit 6a36533

Browse files
authored
docs: document v3 error subclasses, fix pagination, flag internal modules (#801)
## Summary Addresses review feedback on the v3 docs: - **Error handling concept page** — adds an *Error subclasses* section with the status-to-class table and updates the code examples to demonstrate catching a specific subclass (`NotFoundError`) alongside the generic `ApifyApiError`. - **Pagination concept page** — removes stale references to the v2 `ListPage` class; list methods now return Pydantic page models like `ListOfActors` / `ListOfDatasets` / `ListOfRequests` (or the `DatasetItemsPage` dataclass for unstructured items). - **v3 upgrade guide** — adds a warning that `apify_client._models`, `apify_client._typeddicts`, and `apify_client._literals` are internal modules with no public-API stability guarantee, and replaces the duplicated error-subclass table with a cross-link to the concept page so the mapping has a single source of truth.
1 parent fadf62a commit 6a36533

5 files changed

Lines changed: 41 additions & 21 deletions

File tree

docs/02_concepts/04_error_handling.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ import ErrorSyncExample from '!!raw-loader!./code/04_error_sync.py';
1414

1515
When you use the Apify client, it automatically extracts all relevant data from the endpoint and returns it in the expected format. Date strings, for instance, are seamlessly converted to Python `datetime.datetime` objects. If an error occurs, the client raises an <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink>. This exception wraps the raw JSON errors returned by the API and provides additional context, making it easier to debug any issues that arise.
1616

17+
## Error subclasses
18+
19+
The Apify client provides dedicated error subclasses based on the HTTP status code of the failed response, so you can branch on error kind without inspecting `status_code` or `type`:
20+
21+
| Status | Subclass |
22+
|---|---|
23+
| 400 | <ApiLink to="class/InvalidRequestError">`InvalidRequestError`</ApiLink> |
24+
| 401 | <ApiLink to="class/UnauthorizedError">`UnauthorizedError`</ApiLink> |
25+
| 403 | <ApiLink to="class/ForbiddenError">`ForbiddenError`</ApiLink> |
26+
| 404 | <ApiLink to="class/NotFoundError">`NotFoundError`</ApiLink> |
27+
| 409 | <ApiLink to="class/ConflictError">`ConflictError`</ApiLink> |
28+
| 429 | <ApiLink to="class/RateLimitError">`RateLimitError`</ApiLink> |
29+
| 5xx | <ApiLink to="class/ServerError">`ServerError`</ApiLink> |
30+
31+
All subclasses inherit from <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink>, so an existing `except ApifyApiError` handler still catches every API error. Catch a specific subclass when you want to react differently to, for example, a missing resource or a rate-limit:
32+
1733
<Tabs>
1834
<TabItem value="AsyncExample" label="Async client" default>
1935
<CodeBlock className="language-python">

docs/02_concepts/08_pagination.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: pagination
33
title: Pagination
4-
description: Paginate through large result sets using ListPage or generator-based iteration.
4+
description: Paginate through large result sets using page models or generator-based iteration.
55
---
66

77
import Tabs from '@theme/Tabs';
@@ -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 <ApiLink to="class/ListPage">`ListPage`</ApiLink> object. This object provides a consistent interface for working with paginated data and includes the following properties:
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:
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 property, and the limit property can be used to control the number of results returned.
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.
2929

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

@@ -42,7 +42,7 @@ The following example shows how to fetch all items from a dataset using paginati
4242
</TabItem>
4343
</Tabs>
4444

45-
The <ApiLink to="class/ListPage">`ListPage`</ApiLink> interface offers several key benefits. Its consistent structure ensures predictable results for most `list` methods, providing a uniform way to work with paginated data. It also offers flexibility, allowing you to customize the `limit` and `offset` parameters to control data fetching according to your needs. Additionally, it provides scalability, enabling you to efficiently handle large datasets through pagination. This approach ensures efficient data retrieval while keeping memory usage under control, making it ideal for managing and processing large collections.
45+
The page model interface offers several key benefits. Its consistent structure ensures predictable results for most `list` methods, providing a uniform way to work with paginated data. It also offers flexibility, allowing you to customize the `limit` and `offset` parameters to control data fetching according to your needs. Additionally, it provides scalability, enabling you to efficiently handle large datasets through pagination. This approach ensures efficient data retrieval while keeping memory usage under control, making it ideal for managing and processing large collections.
4646

4747
## Generator-based iteration
4848

docs/02_concepts/code/04_error_async.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import asyncio
22

33
from apify_client import ApifyClientAsync
4-
from apify_client.errors import ApifyApiError
4+
from apify_client.errors import ApifyApiError, NotFoundError
55

66
TOKEN = 'MY-APIFY-TOKEN'
77

@@ -13,9 +13,15 @@ async def main() -> None:
1313
# Try to list items from a non-existing dataset.
1414
dataset_client = apify_client.dataset('non-existing-dataset-id')
1515
dataset_items = (await dataset_client.list_items()).items
16+
except NotFoundError:
17+
# 404 — branch on a specific subclass when you want to react to it.
18+
dataset_items = []
1619
except ApifyApiError as err:
17-
# The client raises ApifyApiError for API errors.
20+
# Catch-all for every other API error.
1821
print(f'API error: {err}')
22+
dataset_items = []
23+
24+
print(f'Fetched {len(dataset_items)} items.')
1925

2026

2127
if __name__ == '__main__':

docs/02_concepts/code/04_error_sync.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from apify_client import ApifyClient
2-
from apify_client.errors import ApifyApiError
2+
from apify_client.errors import ApifyApiError, NotFoundError
33

44
TOKEN = 'MY-APIFY-TOKEN'
55

@@ -11,9 +11,15 @@ def main() -> None:
1111
# Try to list items from a non-existing dataset.
1212
dataset_client = apify_client.dataset('non-existing-dataset-id')
1313
dataset_items = dataset_client.list_items().items
14+
except NotFoundError:
15+
# 404 — branch on a specific subclass when you want to react to it.
16+
dataset_items = []
1417
except ApifyApiError as err:
15-
# The client raises ApifyApiError for API errors.
18+
# Catch-all for every other API error.
1619
print(f'API error: {err}')
20+
dataset_items = []
21+
22+
print(f'Fetched {len(dataset_items)} items.')
1723

1824

1925
if __name__ == '__main__':

docs/04_upgrading/upgrading_to_v3.mdx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ status = run.status
4646

4747
All model classes are generated from the Apify OpenAPI specification and live in `apify_client._models` module. They are configured with `extra='allow'`, so any new fields added to the API in the future are preserved on the model instance. Fields are accessed using their Python snake_case names:
4848

49+
:::warning
50+
The `apify_client._models`, `apify_client._typeddicts`, and `apify_client._literals` modules are internal. The examples below import from them for convenience, but use them at your own risk — their contents are regenerated from the OpenAPI specification and the public API of these modules is not stable across minor versions of `apify-client`.
51+
:::
52+
4953
```python
5054
run.default_dataset_id # ✓ use snake_case attribute names
5155
run.id
@@ -191,19 +195,7 @@ If your code relied on the previous global timeout behavior, review the timeout
191195

192196
## Exception subclasses for API errors
193197

194-
<ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> now dispatches to a dedicated subclass based on the HTTP status code of the failed response. Instantiating `ApifyApiError` directly still works — it returns the most specific subclass for the status — so existing `except ApifyApiError` handlers are unaffected.
195-
196-
The following subclasses are available:
197-
198-
| Status | Subclass |
199-
|---|---|
200-
| 400 | <ApiLink to="class/InvalidRequestError">`InvalidRequestError`</ApiLink> |
201-
| 401 | <ApiLink to="class/UnauthorizedError">`UnauthorizedError`</ApiLink> |
202-
| 403 | <ApiLink to="class/ForbiddenError">`ForbiddenError`</ApiLink> |
203-
| 404 | <ApiLink to="class/NotFoundError">`NotFoundError`</ApiLink> |
204-
| 409 | <ApiLink to="class/ConflictError">`ConflictError`</ApiLink> |
205-
| 429 | <ApiLink to="class/RateLimitError">`RateLimitError`</ApiLink> |
206-
| 5xx | <ApiLink to="class/ServerError">`ServerError`</ApiLink> |
198+
The Apify client now provides dedicated error subclasses based on the HTTP status code of the failed response. Instantiating <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> directly still works — it returns the most specific subclass for the status — so existing `except ApifyApiError` handlers are unaffected. See the [Error handling](/docs/concepts/error-handling#error-subclasses) concept page for the full status-to-subclass mapping.
207199

208200
You can now branch on error kind without inspecting `status_code` or `type`:
209201

0 commit comments

Comments
 (0)