From 877d20c3d8efbdc85afe001a29da440b8b03a620 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 29 May 2026 19:20:08 +0200 Subject: [PATCH 1/4] docs: fix inaccuracies and expand SDK documentation Correct several factual errors verified against the codebase (Python 3.11 requirement, web server config properties, Actor event payload types, a stale env var, and an empty API link), fix a latent runtime bug in the events snippet, and fill gaps by adding a Storage clients page, documenting Actor Standby, and expanding the Introduction and Configuration pages. --- docs/01_introduction/index.mdx | 13 +++- docs/01_introduction/quick-start.mdx | 1 + docs/02_concepts/01_actor_lifecycle.mdx | 2 +- docs/02_concepts/03_storages.mdx | 2 +- docs/02_concepts/04_actor_events.mdx | 28 ++++---- docs/02_concepts/10_configuration.mdx | 21 +++++- docs/02_concepts/12_storage_clients.mdx | 64 +++++++++++++++++++ docs/02_concepts/code/04_actor_events.py | 9 +-- .../code/12_custom_storage_client.py | 22 +++++++ .../code/12_shared_request_queue.py | 24 +++++++ docs/03_guides/07_running_webserver.mdx | 12 +++- 11 files changed, 171 insertions(+), 27 deletions(-) create mode 100644 docs/02_concepts/12_storage_clients.mdx create mode 100644 docs/02_concepts/code/12_custom_storage_client.py create mode 100644 docs/02_concepts/code/12_shared_request_queue.py diff --git a/docs/01_introduction/index.mdx b/docs/01_introduction/index.mdx index 2e803979..deaaaaef 100644 --- a/docs/01_introduction/index.mdx +++ b/docs/01_introduction/index.mdx @@ -9,7 +9,16 @@ import CodeBlock from '@theme/CodeBlock'; import IntroductionExample from '!!raw-loader!./code/01_introduction.py'; -The Apify SDK for Python is the official library for creating [Apify Actors](https://docs.apify.com/platform/actors) in Python. It provides useful features like Actor lifecycle management, local storage emulation, and Actor event handling. +The Apify SDK for Python is the official library for creating [Apify Actors](https://docs.apify.com/platform/actors) in Python. It gives you everything you need to build an Actor and run it both locally and on the [Apify platform](https://docs.apify.com/platform), including: + +- **Actor lifecycle management** — initialization, graceful shutdown, status messages, rebooting, and metamorphing. +- **Storage access** — datasets, key-value stores, and request queues, with automatic local emulation when running outside the platform. +- **Actor input** — convenient access to the Actor input, including automatic decryption of secret fields. +- **Events & state persistence** — react to platform events (system info, migration, abort) and persist state across migrations and restarts. +- **Proxy management** — Apify Proxy and custom proxies, with session and tiered-proxy support. +- **Platform interaction** — start, call, and abort other Actors and tasks, create webhooks, and reach the full Apify API client. +- **Monetization** — charge users with the pay-per-event pricing model. +- **Framework integrations** — first-class support for [Crawlee](../guides/crawlee) and [Scrapy](../guides/scrapy). {IntroductionExample} @@ -29,7 +38,7 @@ Explore the Guides section in the sidebar for a deeper understanding of the SDK' ## Installation -The Apify SDK for Python requires Python version 3.10 or above. It is typically installed when you create a new Actor project using the [Apify CLI](https://docs.apify.com/cli). To install it manually in an existing project, use: +The Apify SDK for Python requires Python version 3.11 or above. It is typically installed when you create a new Actor project using the [Apify CLI](https://docs.apify.com/cli). To install it manually in an existing project, use: ```bash pip install apify diff --git a/docs/01_introduction/quick-start.mdx b/docs/01_introduction/quick-start.mdx index da166da9..f0f0887d 100644 --- a/docs/01_introduction/quick-start.mdx +++ b/docs/01_introduction/quick-start.mdx @@ -86,6 +86,7 @@ To learn more about the features of the Apify SDK and how to use them, check out - [Actor lifecycle](../concepts/actor-lifecycle) - [Actor input](../concepts/actor-input) - [Working with storages](../concepts/storages) +- [Storage clients](../concepts/storage-clients) - [Actor events & state persistence](../concepts/actor-events) - [Proxy management](../concepts/proxy-management) - [Interacting with other Actors](../concepts/interacting-with-other-actors) diff --git a/docs/02_concepts/01_actor_lifecycle.mdx b/docs/02_concepts/01_actor_lifecycle.mdx index 40826305..a89f8dd0 100644 --- a/docs/02_concepts/01_actor_lifecycle.mdx +++ b/docs/02_concepts/01_actor_lifecycle.mdx @@ -106,4 +106,4 @@ Update the status only when the user's understanding of progress changes - avoid ## Conclusion -This page has presented the full Actor lifecycle: initialization, execution, error handling, rebooting, shutdown and status messages. You've seen how the SDK supports both context-based and manual control patterns. For deeper dives, explore the reference docs, [guides](https://docs.apify.com/sdk/python/docs/guides/beautifulsoup-httpx), and [platform documentation](https://docs.apify.com/platform). +This page has presented the full Actor lifecycle: initialization, execution, error handling, rebooting, shutdown and status messages. You've seen how the SDK supports both context-based and manual control patterns. For deeper dives, explore the `Actor` API reference, [guides](https://docs.apify.com/sdk/python/docs/guides/beautifulsoup-httpx), and [platform documentation](https://docs.apify.com/platform). diff --git a/docs/02_concepts/03_storages.mdx b/docs/02_concepts/03_storages.mdx index 059682b2..7470fa95 100644 --- a/docs/02_concepts/03_storages.mdx +++ b/docs/02_concepts/03_storages.mdx @@ -183,6 +183,6 @@ To check if all the requests in the queue are handled, you can use the @@ -25,25 +27,23 @@ During its runtime, the Actor receives Actor events sent by the Apify platform o SYSTEM_INFO -
{`{
-  "created_at": datetime,
-  "cpu_current_usage": float,
-  "mem_current_bytes": int,
-  "is_cpu_overloaded": bool
-}`}
-            
+ EventSystemInfoData -

This event is emitted regularly and it indicates the current resource usage of the Actor.

- The is_cpu_overloaded argument indicates whether the current CPU usage is higher than Config.max_used_cpu_ratio +

Emitted regularly to report the Actor's current resource usage. The + cpu_info.used_ratio field reports the fraction of CPU currently in use + (a float between 0.0 and 1.0), and memory_info.current_size + reports the current memory usage. Compare cpu_info.used_ratio against + Configuration.max_used_cpu_ratio to detect CPU overload.

MIGRATING - None + EventMigratingData

Emitted when the Actor running on the Apify platform is going to be migrated - {' '}to another worker server soon.

+ {' '}to another worker server soon. The time_remaining field reports how much time + the Actor has left before it is force-migrated.

You can use it to persist the state of the Actor so that once it is executed again on the new server, it doesn't have to start over from the beginning. Once you have persisted the state of your Actor, you can call `Actor.reboot` @@ -52,7 +52,7 @@ During its runtime, the Actor receives Actor events sent by the Apify platform o ABORTING - None + EventAbortingData When a user aborts an Actor run on the Apify platform, they can choose to abort gracefully to allow the Actor some time before getting killed. @@ -61,7 +61,7 @@ During its runtime, the Actor receives Actor events sent by the Apify platform o PERSIST_STATE -
{`{ "is_migrating": bool }`}
+ EventPersistStateData

Emitted in regular intervals (by default 60 seconds) to notify the Actor that it should persist its state, in order to avoid repeating all work when the Actor restarts.

@@ -73,7 +73,7 @@ During its runtime, the Actor receives Actor events sent by the Apify platform o EXIT - None + EventExitData Emitted by the SDK (not the platform) when the Actor is about to exit. You can use this event to perform final cleanup tasks, such as closing external connections or sending notifications, before the Actor shuts down. diff --git a/docs/02_concepts/10_configuration.mdx b/docs/02_concepts/10_configuration.mdx index 6296b864..b064f10b 100644 --- a/docs/02_concepts/10_configuration.mdx +++ b/docs/02_concepts/10_configuration.mdx @@ -27,14 +27,29 @@ This will cause the Actor to persist its state every 10 seconds: ## Configuring via environment variables -All the configuration options can be set via environment variables. The environment variables are prefixed with `APIFY_`, and the configuration options are in uppercase, with underscores as separators. See the `Configuration` API reference for the full list of configuration options. +All configuration options can also be set via environment variables. Most options are read from an environment variable named after the option in uppercase; many options accept several aliases — commonly with an `APIFY_`, `ACTOR_`, or `CRAWLEE_` prefix. See the `Configuration` API reference for the full list of configuration options. -This Actor run will not persist its local storages to the filesystem: +For example, this Actor run will keep the contents of its local storages instead of purging them on start: ```bash -APIFY_PERSIST_STORAGE=0 apify run +APIFY_PURGE_ON_START=0 apify run ``` +### Commonly used options + +The table below lists a few options you are most likely to set yourself. When running on the Apify platform or via the Apify CLI, the platform-related options are populated automatically. + +| Option | Environment variable | Default | Description | +| --- | --- | --- | --- | +| `token` | `APIFY_TOKEN` | `None` | API token used to authenticate calls to the Apify API. | +| `proxy_password` | `APIFY_PROXY_PASSWORD` | `None` | Password for [Apify Proxy](https://docs.apify.com/proxy). | +| `purge_on_start` | `APIFY_PURGE_ON_START` | `True` | Whether to purge local storages when the Actor starts. | +| `persist_state_interval` | `APIFY_PERSIST_STATE_INTERVAL_MILLIS` | `1 min` | How often the `PERSIST_STATE` event is emitted (the variable is in milliseconds). | +| `log_level` | `APIFY_LOG_LEVEL` | `'INFO'` | Minimum severity of log messages that are printed. | +| `headless` | `APIFY_HEADLESS` | `True` | Whether to run browsers in headless mode. | +| `storage_dir` | `APIFY_LOCAL_STORAGE_DIR` | `'./storage'` | Directory holding local storages when running outside the platform. | +| `is_at_home` | `APIFY_IS_AT_HOME` | `False` | Set by the platform — `True` when the Actor runs on Apify. | + ## Reading the runtime environment The `Actor.get_env` method returns a dictionary with all `APIFY_*` environment variables parsed into their typed values. This is useful for inspecting the Actor's runtime context, such as the Actor ID, run ID, or default storage IDs. Variables that are not set or are invalid will have a value of `None`. diff --git a/docs/02_concepts/12_storage_clients.mdx b/docs/02_concepts/12_storage_clients.mdx new file mode 100644 index 00000000..81ede2db --- /dev/null +++ b/docs/02_concepts/12_storage_clients.mdx @@ -0,0 +1,64 @@ +--- +id: storage-clients +title: Storage clients +description: Choose and configure the backend the Actor uses for datasets, key-value stores, and request queues. +--- + +import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock'; +import ApiLink from '@theme/ApiLink'; + +import SharedRequestQueueExample from '!!raw-loader!roa-loader!./code/12_shared_request_queue.py'; +import CustomStorageClientExample from '!!raw-loader!roa-loader!./code/12_custom_storage_client.py'; + +Storage clients are the components that actually read and write your [storages](./storages) — datasets, key-value stores, and request queues. The Apify SDK selects an appropriate client automatically based on where the Actor runs, so for most Actors you never need to think about them. This page explains the available clients and how to customize them when you do. + +## How the Actor selects a storage client + +By default, the Actor uses a `SmartApifyStorageClient` — a hybrid client that delegates to one of two underlying clients depending on the environment: + +- When running **on the Apify platform** (detected automatically), or when you pass `force_cloud=True`, it uses the **cloud** client — `ApifyStorageClient`, which persists data through the Apify API. +- When running **locally**, it uses the **local** client — `FileSystemStorageClient`, which emulates platform storages on your filesystem under the `storage` folder. + +This is what lets the same Actor code run unchanged both locally and on the platform. + +## Available storage clients + +The `apify.storage_clients` module provides the following clients: + +- `SmartApifyStorageClient` — the default hybrid client described above. It wraps a `cloud_storage_client` and a `local_storage_client` and routes each call to the right one. +- `ApifyStorageClient` — talks to the Apify API. Used as the cloud client. +- `FileSystemStorageClient` — persists data to the local filesystem. Used as the default local client. +- `MemoryStorageClient` — keeps everything in memory only; nothing is persisted. Useful for tests and short-lived runs. + +## Single vs. shared request queue + +`ApifyStorageClient` supports two ways of accessing the Apify request queue, selected via its `request_queue_access` argument: + +- **`'single'`** (default) — optimized for a single consumer. It makes far fewer API calls, so it is cheaper and faster, but it does not support multiple clients consuming the same queue concurrently. This is the right choice for the majority of Actors. +- **`'shared'`** — supports multiple consumers working on the same queue at the same time, at the cost of more API calls. + +To opt into the shared client, set it as the cloud client of the `SmartApifyStorageClient` in the [service locator](https://crawlee.dev/python/docs/guides/service-locator) before entering the Actor context: + + + {SharedRequestQueueExample} + + +## Using cloud storage while running locally + +When developing locally, storages are read from and written to the local filesystem by default. To work with a storage on the Apify platform instead — for example, to read the output of a remote Actor run — pass `force_cloud=True` to `Actor.open_dataset`, `Actor.open_key_value_store`, or `Actor.open_request_queue`. This requires an Apify token, provided via the `APIFY_TOKEN` environment variable. + +## Customizing the storage client + +You can replace either of the underlying clients — for example, to keep all local data in memory instead of on disk. To do this, set a `SmartApifyStorageClient` with your chosen sub-clients in the service locator **before** entering the Actor context (or awaiting `Actor.init`): + + + {CustomStorageClientExample} + + +:::note + +The Actor's storage client must be a `SmartApifyStorageClient`. Setting a bare `ApifyStorageClient` or `MemoryStorageClient` directly in the service locator raises an error — wrap it in a `SmartApifyStorageClient` as shown above. + +::: + +For a deeper look at how storage clients work and how to write your own, see the [Crawlee storage clients guide](https://crawlee.dev/python/docs/guides/storage-clients). diff --git a/docs/02_concepts/code/04_actor_events.py b/docs/02_concepts/code/04_actor_events.py index 591fddcd..555c61b5 100644 --- a/docs/02_concepts/code/04_actor_events.py +++ b/docs/02_concepts/code/04_actor_events.py @@ -1,7 +1,6 @@ import asyncio -from typing import Any -from apify import Actor, Event +from apify import Actor, Event, EventPersistStateData async def main() -> None: @@ -15,9 +14,11 @@ async def main() -> None: processed_items = actor_state # Save the state when the `PERSIST_STATE` event happens - async def save_state(event_data: Any) -> None: + async def save_state(event_data: EventPersistStateData) -> None: nonlocal processed_items - Actor.log.info('Saving Actor state', extra=event_data) + Actor.log.info( + 'Persisting Actor state (migrating=%s)', event_data.is_migrating + ) await Actor.set_value('STATE', processed_items) Actor.on(Event.PERSIST_STATE, save_state) diff --git a/docs/02_concepts/code/12_custom_storage_client.py b/docs/02_concepts/code/12_custom_storage_client.py new file mode 100644 index 00000000..1d54d632 --- /dev/null +++ b/docs/02_concepts/code/12_custom_storage_client.py @@ -0,0 +1,22 @@ +import asyncio + +from crawlee import service_locator + +from apify import Actor +from apify.storage_clients import MemoryStorageClient, SmartApifyStorageClient + + +async def main() -> None: + # Keep all local data in memory instead of writing it to the filesystem + # when running outside the Apify platform. + service_locator.set_storage_client( + SmartApifyStorageClient(local_storage_client=MemoryStorageClient()), + ) + + async with Actor: + store = await Actor.open_key_value_store() + await store.set_value('example', {'hello': 'world'}) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/docs/02_concepts/code/12_shared_request_queue.py b/docs/02_concepts/code/12_shared_request_queue.py new file mode 100644 index 00000000..e2002ef9 --- /dev/null +++ b/docs/02_concepts/code/12_shared_request_queue.py @@ -0,0 +1,24 @@ +import asyncio + +from crawlee import service_locator + +from apify import Actor +from apify.storage_clients import ApifyStorageClient, SmartApifyStorageClient + + +async def main() -> None: + # Use the shared Apify request queue client, which supports multiple + # consumers working on the same queue at the cost of more API calls. + service_locator.set_storage_client( + SmartApifyStorageClient( + cloud_storage_client=ApifyStorageClient(request_queue_access='shared'), + ) + ) + + async with Actor: + request_queue = await Actor.open_request_queue() + await request_queue.add_request('https://crawlee.dev') + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/docs/03_guides/07_running_webserver.mdx b/docs/03_guides/07_running_webserver.mdx index c17c313b..9b63976a 100644 --- a/docs/03_guides/07_running_webserver.mdx +++ b/docs/03_guides/07_running_webserver.mdx @@ -18,9 +18,9 @@ The URL is available in the following places: - In Apify Console, on the Actor run details page as the **Container URL** field. - In the API as the `container_url` property of the [Run object](https://docs.apify.com/api/v2#/reference/actors/run-object/get-run). -- In the Actor as the `Actor.configuration.container_url` property. +- In the Actor as the `Actor.configuration.web_server_url` property. -The web server running inside the container must listen at the port defined by the `Actor.configuration.container_port` property. When running Actors locally, the port defaults to `4321`, so the web server will be accessible at `http://localhost:4321`. +The web server running inside the container must listen at the port defined by the `Actor.configuration.web_server_port` property. When running Actors locally, the port defaults to `4321`, so the web server will be accessible at `http://localhost:4321`. ## Example Actor @@ -30,6 +30,14 @@ The following example shows how to start a simple web server in your Actor, whic {WebserverExample} +## Actor Standby + +The example above runs a web server for the duration of a single Actor run. With [Actor Standby](https://docs.apify.com/platform/actors/development/programming-interface/standby), you can instead expose your Actor as an always-ready HTTP API: the platform keeps the Actor running in the background and routes incoming HTTP requests to the web server inside it, spinning up additional instances as the load grows. + +From the SDK's perspective, a Standby Actor is built the same way as the web server above — start an HTTP server listening on the port from `Actor.configuration.web_server_port`. The difference is operational: instead of doing its work once and exiting, a Standby Actor stays up and serves requests. This makes it a good fit for low-latency, on-demand use cases, such as serving scraped data or acting as a microservice. + +To get started quickly, use the [Standby Python template](https://apify.com/templates/python-standby). For details on enabling Standby, request routing, and readiness probes, see the [Actor Standby documentation](https://docs.apify.com/platform/actors/development/programming-interface/standby). + ## Conclusion In this guide, you learned how to run a web server inside your Apify Actor. By leveraging the container URL and port provided by the platform, you can expose HTTP endpoints for monitoring, reporting, or serving content during Actor execution. If you have questions or need assistance, feel free to reach out on our [GitHub](https://github.com/apify/apify-sdk-python) or join our [Discord community](https://discord.com/invite/jyEM2PRvMU). From 17e48aee1c930a01490cf5ff20f28870cb66cf42 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 4 Jun 2026 12:33:35 +0200 Subject: [PATCH 2/4] feat: re-export StorageClient from apify.storage_clients --- src/apify/storage_clients/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apify/storage_clients/__init__.py b/src/apify/storage_clients/__init__.py index 8a62e3dc..fef5dde8 100644 --- a/src/apify/storage_clients/__init__.py +++ b/src/apify/storage_clients/__init__.py @@ -1,4 +1,4 @@ -from crawlee.storage_clients import MemoryStorageClient +from crawlee.storage_clients import MemoryStorageClient, StorageClient from ._apify import ApifyStorageClient from ._file_system import ApifyFileSystemStorageClient as FileSystemStorageClient @@ -9,4 +9,5 @@ 'FileSystemStorageClient', 'MemoryStorageClient', 'SmartApifyStorageClient', + 'StorageClient', ] From 547af693a618e8446ac7f3c973bdc96501840f7d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 4 Jun 2026 12:33:36 +0200 Subject: [PATCH 3/4] docs: document Crawlee storage clients and rename storages page --- docs/01_introduction/quick-start.mdx | 2 +- docs/02_concepts/03_storages.mdx | 2 +- docs/02_concepts/12_storage_clients.mdx | 4 ++++ website/docusaurus.config.js | 4 ---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/01_introduction/quick-start.mdx b/docs/01_introduction/quick-start.mdx index f0f0887d..e77a7121 100644 --- a/docs/01_introduction/quick-start.mdx +++ b/docs/01_introduction/quick-start.mdx @@ -85,7 +85,7 @@ To learn more about the features of the Apify SDK and how to use them, check out - [Actor lifecycle](../concepts/actor-lifecycle) - [Actor input](../concepts/actor-input) -- [Working with storages](../concepts/storages) +- [Storages](../concepts/storages) - [Storage clients](../concepts/storage-clients) - [Actor events & state persistence](../concepts/actor-events) - [Proxy management](../concepts/proxy-management) diff --git a/docs/02_concepts/03_storages.mdx b/docs/02_concepts/03_storages.mdx index 7470fa95..0686a217 100644 --- a/docs/02_concepts/03_storages.mdx +++ b/docs/02_concepts/03_storages.mdx @@ -1,6 +1,6 @@ --- id: storages -title: Working with storages +title: Storages description: Use datasets, key-value stores, and request queues to persist Actor data. --- diff --git a/docs/02_concepts/12_storage_clients.mdx b/docs/02_concepts/12_storage_clients.mdx index 81ede2db..ee01a69e 100644 --- a/docs/02_concepts/12_storage_clients.mdx +++ b/docs/02_concepts/12_storage_clients.mdx @@ -30,6 +30,10 @@ The `apify.storage_clients` module provides the following clients: - `FileSystemStorageClient` — persists data to the local filesystem. Used as the default local client. - `MemoryStorageClient` — keeps everything in memory only; nothing is persisted. Useful for tests and short-lived runs. +All of these implement Crawlee's `StorageClient` interface, so any of them can be used as a sub-client of `SmartApifyStorageClient` — see [customizing the storage client](#customizing-the-storage-client) below. + +Crawlee additionally ships storage clients backed by a self-hosted database — a [`RedisStorageClient`](https://crawlee.dev/python/api/class/RedisStorageClient) and a [`SqlStorageClient`](https://crawlee.dev/python/api/class/SqlStorageClient). The Apify SDK does not re-export these, because they each require an extra dependency that the SDK does not install. To use one, install the matching Crawlee extra (for example `pip install 'crawlee[redis]'` or `crawlee[sql-postgres]` / `crawlee[sql-sqlite]`), import the client from `crawlee.storage_clients`, and pass it as a sub-client of `SmartApifyStorageClient`. See the [Crawlee storage clients guide](https://crawlee.dev/python/docs/guides/storage-clients) for details. + ## Single vs. shared request queue `ApifyStorageClient` supports two ways of accessing the Apify request queue, selected via its `request_queue_access` argument: diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 2583d851..d6ef5fd6 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -259,10 +259,6 @@ module.exports = { url: 'https://crawlee.dev/python/api/class/FileSystemStorageClient', group: 'Storage clients', }, - { - url: 'https://crawlee.dev/python/api/class/SqlStorageClient', - group: 'Storage clients', - }, // Request loaders { url: 'https://crawlee.dev/python/api/class/RequestLoader', From 1c257aedadb2ce28554c16f8827ae4380d1b01f1 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 4 Jun 2026 16:40:12 +0200 Subject: [PATCH 4/4] docs: fix inaccuracies, unify wording, and add concept conclusions --- docs/01_introduction/quick-start.mdx | 6 +++--- docs/02_concepts/01_actor_lifecycle.mdx | 2 +- docs/02_concepts/02_actor_input.mdx | 6 +++++- docs/02_concepts/03_storages.mdx | 12 ++++++++---- docs/02_concepts/04_actor_events.mdx | 4 ++++ docs/02_concepts/05_proxy_management.mdx | 10 +++++++--- .../02_concepts/06_interacting_with_other_actors.mdx | 4 ++++ docs/02_concepts/07_webhooks.mdx | 4 ++++ docs/02_concepts/08_access_apify_api.mdx | 4 ++++ docs/02_concepts/09_logging.mdx | 10 +++++++--- docs/02_concepts/10_configuration.mdx | 4 ++++ docs/02_concepts/11_pay_per_event.mdx | 12 ++++++++---- docs/02_concepts/12_storage_clients.mdx | 4 ++++ docs/03_guides/06_scrapy.mdx | 6 +++--- docs/04_upgrading/upgrading_to_v2.md | 6 +++--- docs/04_upgrading/upgrading_to_v3.md | 6 +++--- docs/04_upgrading/upgrading_to_v4.md | 2 +- 17 files changed, 73 insertions(+), 29 deletions(-) diff --git a/docs/01_introduction/quick-start.mdx b/docs/01_introduction/quick-start.mdx index e77a7121..2cea9b80 100644 --- a/docs/01_introduction/quick-start.mdx +++ b/docs/01_introduction/quick-start.mdx @@ -59,7 +59,7 @@ The Actor's runtime dependencies are specified in the `requirements.txt` file, w The Actor's source code is in the `src` folder. This folder contains two important files: - `main.py` - which contains the main function of the Actor -- `__main__.py` - which is the entrypoint of the Actor package setting up the Actor [logger](../concepts/logging) and executing the Actor's main function via [`asyncio.run()`](https://docs.python.org/3/library/asyncio-runner.html#asyncio.run). +- `__main__.py` - which is the entrypoint of the Actor package, executing the Actor's main function via [`asyncio.run()`](https://docs.python.org/3/library/asyncio-runner.html#asyncio.run). @@ -67,7 +67,7 @@ The Actor's source code is in the `src` folder. This folder contains two importa {MainExample}
- + {UnderscoreMainExample} @@ -86,7 +86,6 @@ To learn more about the features of the Apify SDK and how to use them, check out - [Actor lifecycle](../concepts/actor-lifecycle) - [Actor input](../concepts/actor-input) - [Storages](../concepts/storages) -- [Storage clients](../concepts/storage-clients) - [Actor events & state persistence](../concepts/actor-events) - [Proxy management](../concepts/proxy-management) - [Interacting with other Actors](../concepts/interacting-with-other-actors) @@ -95,6 +94,7 @@ To learn more about the features of the Apify SDK and how to use them, check out - [Logging](../concepts/logging) - [Actor configuration](../concepts/actor-configuration) - [Pay-per-event monetization](../concepts/pay-per-event) +- [Storage clients](../concepts/storage-clients) ### Guides diff --git a/docs/02_concepts/01_actor_lifecycle.mdx b/docs/02_concepts/01_actor_lifecycle.mdx index a89f8dd0..738bdc35 100644 --- a/docs/02_concepts/01_actor_lifecycle.mdx +++ b/docs/02_concepts/01_actor_lifecycle.mdx @@ -106,4 +106,4 @@ Update the status only when the user's understanding of progress changes - avoid ## Conclusion -This page has presented the full Actor lifecycle: initialization, execution, error handling, rebooting, shutdown and status messages. You've seen how the SDK supports both context-based and manual control patterns. For deeper dives, explore the `Actor` API reference, [guides](https://docs.apify.com/sdk/python/docs/guides/beautifulsoup-httpx), and [platform documentation](https://docs.apify.com/platform). +This page has presented the full Actor lifecycle: initialization, execution, error handling, rebooting, shutdown and status messages. You've seen how the SDK supports both context-based and manual control patterns. For deeper dives, explore the `Actor` API reference, [guides](../guides/beautifulsoup-httpx), and [platform documentation](https://docs.apify.com/platform). diff --git a/docs/02_concepts/02_actor_input.mdx b/docs/02_concepts/02_actor_input.mdx index 15807c05..a46210af 100644 --- a/docs/02_concepts/02_actor_input.mdx +++ b/docs/02_concepts/02_actor_input.mdx @@ -12,7 +12,7 @@ import ApiLink from '@theme/ApiLink'; The Actor gets its [input](https://docs.apify.com/platform/actors/running/input) from the input record in its default [key-value store](https://docs.apify.com/platform/storage/key-value-store). -To access it, instead of reading the record manually, you can use the `Actor.get_input` convenience method. It will get the input record key from the Actor configuration, read the record from the default key-value store,and decrypt any [secret input fields](https://docs.apify.com/platform/actors/development/secret-input). +To access it, instead of reading the record manually, you can use the `Actor.get_input` convenience method. It will get the input record key from the Actor configuration, read the record from the default key-value store, and decrypt any [secret input fields](https://docs.apify.com/platform/actors/development/secret-input). For example, if an Actor received a JSON input with two fields, `{ "firstNumber": 1, "secondNumber": 2 }`, this is how you might process it: @@ -34,4 +34,8 @@ The Apify platform supports [secret input fields](https://docs.apify.com/platfor No special handling is needed in your code — when you call `Actor.get_input`, encrypted fields are automatically decrypted using the Actor's private key, which is provided by the platform via environment variables. You receive the plaintext values directly. +## Conclusion + +This page has shown how to read Actor input with `Actor.get_input`, how to load URL sources with `ApifyRequestList`, and how secret input fields are decrypted automatically when you read them. + For more details on Actor input and how to define input schemas, see the [Actor input](https://docs.apify.com/platform/actors/running/input) and [input schema](https://docs.apify.com/platform/actors/development/input-schema) documentation on the Apify platform. diff --git a/docs/02_concepts/03_storages.mdx b/docs/02_concepts/03_storages.mdx index 0686a217..b46ff4bb 100644 --- a/docs/02_concepts/03_storages.mdx +++ b/docs/02_concepts/03_storages.mdx @@ -45,11 +45,11 @@ Each dataset item, key-value store record, or request in a request queue is then When developing locally, opening any storage will by default use local storage. To change this behavior and to use remote storage you have to use `force_cloud=True` argument in `Actor.open_dataset`, `Actor.open_request_queue` or `Actor.open_key_value_store`. Proper use of this argument allows you to work with both local and remote storages. -Calling another remote Actor and accessing its default storage is typical use-case for using `force-cloud=True` argument to open remote Actor's storages. +Calling another remote Actor and accessing its default storage is a typical use-case for using `force_cloud=True` argument to open remote Actor's storages. ### Local storage persistence -By default, the storage contents are persisted across multiple Actor runs. To clean up the Actor storages before the running the Actor, use the `--purge` flag of the [`apify run`](https://docs.apify.com/cli/docs/reference#apify-run) command of the Apify CLI. +By default, the storage contents are persisted across multiple Actor runs. To clean up the Actor storages before running the Actor, use the `--purge` flag of the [`apify run`](https://docs.apify.com/cli/docs/reference#apify-run) command of the Apify CLI. ```bash apify run --purge @@ -106,8 +106,8 @@ To get an iterator of the data, you can use the `Dataset.export_to_csv` -or `Dataset.export_to_json` method. +using the `Dataset.export_to` method with the +`content_type` argument set to `'csv'` or `'json'`. {DatasetExportsExample} @@ -185,4 +185,8 @@ To check if all the requests in the queue are handled, you can use the +## Conclusion + +This page has described the events emitted during a run — `SYSTEM_INFO`, `MIGRATING`, `ABORTING`, `PERSIST_STATE`, and `EXIT` — how to handle them with `Actor.on`, and how to persist state automatically with `Actor.use_state`. + For more details on platform events and state persistence, see the [system events](https://docs.apify.com/platform/actors/development/programming-interface/system-events) and [state persistence](https://docs.apify.com/platform/actors/development/state-persistence) documentation on the Apify platform. diff --git a/docs/02_concepts/05_proxy_management.mdx b/docs/02_concepts/05_proxy_management.mdx index d60763b5..08d99b7c 100644 --- a/docs/02_concepts/05_proxy_management.mdx +++ b/docs/02_concepts/05_proxy_management.mdx @@ -22,7 +22,7 @@ The Apify SDK provides built-in proxy management through the {ApifyProxyExample} @@ -38,7 +38,7 @@ If you want to use Apify Proxy locally, make sure that you run your Actors via t All your proxy needs are managed by the `ProxyConfiguration` class. You create an instance using the `Actor.create_proxy_configuration()` method. Then you generate proxy URLs using the `ProxyConfiguration.new_url()` method. -### Apify proxy vs. your own proxies +### Apify Proxy vs. your own proxies The `ProxyConfiguration` class covers both Apify Proxy and custom proxy URLs, so that you can easily switch between proxy providers. However, some features of the class are available only to Apify Proxy users, mainly because Apify Proxy is what one would call a super-proxy. It's not a single proxy server, but an API endpoint that allows connection through millions of different IP addresses. So the class essentially has two modes: Apify Proxy or Your proxy. @@ -54,7 +54,7 @@ When no `session_id` is provided, your custom proxy URLs are rotated round-robin {ProxyRotationExample} -### Apify proxy configuration +### Apify Proxy configuration With Apify Proxy, you can select specific proxy groups to use, or countries to connect from. For even finer control, you can also target a specific subdivision (e.g. a US state) using the `subdivision_code` parameter alongside `country_code`. This allows you to get better proxy performance after some initial research. @@ -120,4 +120,8 @@ Make sure you have the `httpx` library installed: pip install httpx ``` +## Conclusion + +This page has explained how to manage proxies with the `ProxyConfiguration` class — using Apify Proxy or your own servers, keeping sessions sticky across requests, configuring tiered proxy rotation, and feeding proxy settings from Actor input. + For full details on proxy configuration options, see the `ProxyConfiguration` API reference and the [Apify Proxy documentation](https://docs.apify.com/proxy). diff --git a/docs/02_concepts/06_interacting_with_other_actors.mdx b/docs/02_concepts/06_interacting_with_other_actors.mdx index cb28d7cf..f8600f62 100644 --- a/docs/02_concepts/06_interacting_with_other_actors.mdx +++ b/docs/02_concepts/06_interacting_with_other_actors.mdx @@ -63,4 +63,8 @@ When you set `gracefully=True`, the platform sends `ABORTING` and `PERSIST_STATE {InteractingAbortExample} +## Conclusion + +This page has shown how to interact with other Actors from your code — starting a run with `Actor.start`, waiting for it to finish with `Actor.call` or `Actor.call_task`, transforming a run with `Actor.metamorph`, and stopping one with `Actor.abort`. + For the full list of methods for interacting with other Actors, see the `Actor` API reference. For more details on running Actors and Actor tasks on the platform, see the [Actors](https://docs.apify.com/platform/actors) and [Actor tasks](https://docs.apify.com/platform/actors/tasks) documentation. diff --git a/docs/02_concepts/07_webhooks.mdx b/docs/02_concepts/07_webhooks.mdx index 5016d8a8..44e7dc1e 100644 --- a/docs/02_concepts/07_webhooks.mdx +++ b/docs/02_concepts/07_webhooks.mdx @@ -32,4 +32,8 @@ To ensure that duplicate ad-hoc webhooks won't get created in a case of Actor re {WebhookPreventingExample} +## Conclusion + +This page has shown how to create ad-hoc webhooks dynamically with `Actor.add_webhook` and how to avoid creating duplicates across Actor restarts using the `idempotency_key` parameter. + For more information about webhooks, including event types and payloads, see the [Apify webhooks documentation](https://docs.apify.com/platform/integrations/webhooks). diff --git a/docs/02_concepts/08_access_apify_api.mdx b/docs/02_concepts/08_access_apify_api.mdx index 5c4ba7ae..99d5545f 100644 --- a/docs/02_concepts/08_access_apify_api.mdx +++ b/docs/02_concepts/08_access_apify_api.mdx @@ -30,4 +30,8 @@ If you want to create a completely new instance of the client, for example, to g {ActorNewClientExample} +## Conclusion + +This page has shown how to reach the Apify API directly — through the built-in client exposed by `Actor.apify_client`, or a freshly configured one created with `Actor.new_client` — for platform features the SDK does not wrap directly. + For the full API client documentation, see the [Apify API Client for Python](https://docs.apify.com/api/client/python). diff --git a/docs/02_concepts/09_logging.mdx b/docs/02_concepts/09_logging.mdx index b2066053..bfea2cab 100644 --- a/docs/02_concepts/09_logging.mdx +++ b/docs/02_concepts/09_logging.mdx @@ -99,11 +99,11 @@ Typical use case for log redirection is to call another Actor using the -Each default redirect logger log entry will have a specific format. After the timestamp, it will contain cyan colored text that will contain the redirect information - the other actor's name and the run ID. The rest of the log message will be printed in the same manner as the parent Actor's logger is configured. +Each default redirect logger log entry will have a specific format. After the timestamp, it will contain cyan colored text that will contain the redirect information - the other Actor's name and the run ID. The rest of the log message will be printed in the same manner as the parent Actor's logger is configured. -The log redirection can be deep, meaning that if the other actor also starts another actor and is redirecting logs from it, then in the top-level Actor, you can see it as well. See the following example screenshot of the Apify log console when one actor recursively starts itself (there are 2 levels of recursion in the example). +The log redirection can be deep, meaning that if the other Actor also starts another Actor and is redirecting logs from it, then in the top-level Actor, you can see it as well. See the following example screenshot of the Apify log console when one Actor recursively starts itself (there are 2 levels of recursion in the example). -![Console with redirected logs](/img/guides/redirected_logs_example.webp 'Example of console with redirected logs from recursively started actor.') +![Console with redirected logs](/img/guides/redirected_logs_example.webp 'Example of console with redirected logs from recursively started Actor.') ### Redirecting logs from already running Actor run @@ -114,3 +114,7 @@ You can further decide whether you want to redirect just new logs of the ongoing {RedirectLogExistingRun} + +## Conclusion + +This page has covered how the SDK logs through the `apify` logger — setting the log level, formatting output with `ActorLogFormatter`, using the logger at each level, and redirecting logs from other Actor runs back into the parent run. For more on the underlying logging machinery, see Python's standard [`logging`](https://docs.python.org/3/library/logging.html) module documentation. diff --git a/docs/02_concepts/10_configuration.mdx b/docs/02_concepts/10_configuration.mdx index b064f10b..16c3321a 100644 --- a/docs/02_concepts/10_configuration.mdx +++ b/docs/02_concepts/10_configuration.mdx @@ -66,4 +66,8 @@ The `Actor.is_at_home` method ret {PlatformDetectionExample} +## Conclusion + +This page has shown how to configure an Actor through the `Configuration` class or environment variables, which options you are most likely to set yourself, and how to inspect the runtime environment with `Actor.get_env` and `Actor.is_at_home`. + For the full list of configuration options, see the `Configuration` API reference. For a complete list of environment variables available on the platform, see the [environment variables](https://docs.apify.com/platform/actors/development/programming-interface/environment-variables) documentation. diff --git a/docs/02_concepts/11_pay_per_event.mdx b/docs/02_concepts/11_pay_per_event.mdx index d8c04443..bf825ae4 100644 --- a/docs/02_concepts/11_pay_per_event.mdx +++ b/docs/02_concepts/11_pay_per_event.mdx @@ -13,7 +13,7 @@ import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock'; Apify provides several [pricing models](https://docs.apify.com/platform/actors/publishing/monetize) for monetizing your Actors. The most recent and most flexible one is [pay-per-event](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event), which lets you charge your users programmatically directly from your Actor. As the name suggests, you may charge the users each time a specific event occurs, for example a call to an external API or when you return a result. -To use the pay-per-event pricing model, you first need to [set it up](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event) for your Actor in the Apify console. After that, you're free to start charging for events. +To use the pay-per-event pricing model, you first need to [set it up](https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event) for your Actor in the Apify Console. After that, you're free to start charging for events. :::info How pay-per-event pricing works @@ -23,7 +23,7 @@ If you want more details about PPE pricing, please refer to our [PPE documentati ## Charging for events -After monetization is set in the Apify console, you can add `Actor.charge` calls to your code and start monetizing! +After monetization is set in the Apify Console, you can add `Actor.charge` calls to your code and start monetizing! {ActorChargeSource} @@ -31,7 +31,7 @@ After monetization is set in the Apify console, you can add `Actor.get_charging_manager()` to access the `ChargingManager`, which can provide more detailed information - for example how many events of each type can be charged before reaching the configured limit. +If you need finer control over charging, you can call `Actor.get_charging_manager()` to access the `ChargingManager`, which can provide more detailed information - for example how many events of each type can be charged before reaching the configured limit. ### Handling the charge limit @@ -59,7 +59,7 @@ For budget-aware crawling strategies, the `C ## Transitioning from a different pricing model -When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.get_pricing_info()` method which returns information about the current pricing model. +When you plan to start using the pay-per-event pricing model for an Actor that is already monetized with a different pricing model, your source code will need to support both pricing models during the transition period enforced by the Apify platform. Arguably the most frequent case is the transition from the pay-per-result model which utilizes the `ACTOR_MAX_PAID_DATASET_ITEMS` environment variable to prevent returning unpaid dataset items. The following is an example how to handle such scenarios. The key part is the `ChargingManager.get_pricing_info()` method which returns information about the current pricing model. {ConditionalActorChargeSource} @@ -77,4 +77,8 @@ If you also wish to see a log of all the events charged throughout the run, the Because pricing configuration is stored by the Apify platform, all events will have a default price of $1. +## Conclusion + +This page has covered the pay-per-event pricing model — charging for events with `Actor.charge`, respecting the charge limit so your Actor stops once the budget is exhausted, querying the `ChargingManager`, handling the transition from another pricing model, and testing charging locally. + For comprehensive details on pay-per-event pricing and Actor monetization, see the [pay-per-event](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event) and [monetization](https://docs.apify.com/platform/actors/publishing/monetize) documentation on the Apify platform. diff --git a/docs/02_concepts/12_storage_clients.mdx b/docs/02_concepts/12_storage_clients.mdx index ee01a69e..f4ea5a87 100644 --- a/docs/02_concepts/12_storage_clients.mdx +++ b/docs/02_concepts/12_storage_clients.mdx @@ -65,4 +65,8 @@ The Actor's storage client must be a `SmartApifyStorageClient`. Setting a bare ` ::: +## Conclusion + +This page has explained how the Actor selects a storage client, the clients available in the `apify.storage_clients` module, the difference between the single and shared request-queue clients, and how to customize the client through the service locator. + For a deeper look at how storage clients work and how to write your own, see the [Crawlee storage clients guide](https://crawlee.dev/python/docs/guides/storage-clients). diff --git a/docs/03_guides/06_scrapy.mdx b/docs/03_guides/06_scrapy.mdx index 12525609..81409ab2 100644 --- a/docs/03_guides/06_scrapy.mdx +++ b/docs/03_guides/06_scrapy.mdx @@ -23,9 +23,9 @@ In this guide, you'll learn how to use the [Scrapy](https://scrapy.org/) framewo ## Integrating Scrapy with the Apify platform -The Apify SDK provides an Apify-Scrapy integration. The main challenge of this is to combine two asynchronous frameworks that use different event loop implementations. Scrapy uses [Twisted](https://twisted.org/) for asynchronous execution, while the Apify SDK is based on [asyncio](https://docs.python.org/3/library/asyncio.html). The key thing is to install the Twisted's `asyncioreactor` to run Twisted's asyncio compatible event loop. The `apify.scrapy.run_scrapy_actor` function handles this reactor installation automatically. This allows both Twisted and asyncio to run on a single event loop, enabling a Scrapy spider to run as an Apify Actor with minimal modifications. +The Apify SDK provides an Apify-Scrapy integration. The main challenge of this is to combine two asynchronous frameworks that use different event loop implementations. Scrapy uses [Twisted](https://twisted.org/) for asynchronous execution, while the Apify SDK is based on [asyncio](https://docs.python.org/3/library/asyncio.html). The key thing is to install Twisted's `asyncioreactor` to run Twisted's asyncio compatible event loop. The `apify.scrapy.run_scrapy_actor` function handles this reactor installation automatically. This allows both Twisted and asyncio to run on a single event loop, enabling a Scrapy spider to run as an Apify Actor with minimal modifications. - + {UnderscoreMainExample} @@ -74,7 +74,7 @@ For further details, see the [Scrapy migration guide](https://docs.apify.com/cli The following example shows a Scrapy Actor that scrapes page titles and enqueues links found on each page. This example aligns with the structure provided in the Apify Actor templates. - + {UnderscoreMainExample} diff --git a/docs/04_upgrading/upgrading_to_v2.md b/docs/04_upgrading/upgrading_to_v2.md index 2269cda4..31318392 100644 --- a/docs/04_upgrading/upgrading_to_v2.md +++ b/docs/04_upgrading/upgrading_to_v2.md @@ -16,7 +16,7 @@ Support for Python 3.8 has been dropped. The Apify Python SDK v2.x now requires - There is a difference in the `RequestQueue.add_request` method: it accepts an `apify.Request` object instead of a free-form dictionary. - A quick way to migrate from dict-based arguments is to wrap it with a `Request.model_validate()` call. - The preferred way is using the `Request.from_url` helper which prefills the `unique_key` and `id` attributes, or instantiating it directly, e.g., `Request(url='https://example.tld', ...)`. - - For simple use cases, `add_request` also accepts plain strings that contain an URL, e.g. `queue.add_request('https://example.tld')`. + - For simple use cases, `add_request` also accepts plain strings that contain a URL, e.g. `queue.add_request('https://example.tld')`. - Removing the `StorageClientManager` class is a significant change. If you need to change the storage client, use `crawlee.service_container` instead. ## Configuration @@ -28,8 +28,8 @@ Attributes suffixed with `_millis` were renamed to remove said suffix and have t ## Actor - The `Actor.main` method has been removed as it brings no benefits compared to using `async with Actor`. -- The `Actor.add_webhook`, `Actor.start`, `Actor.call` and `Actor.start_task` methods now accept instances of the `apify.Webhook` model instead of an untyped `dict`. -- `Actor.start`, `Actor.call`, `Actor.start_task`, `Actor.set_status_message` and `Actor.abort` return instances of the `ActorRun` model instead of an untyped `dict`. +- The `Actor.add_webhook`, `Actor.start`, `Actor.call` and `Actor.call_task` methods now accept instances of the `apify.Webhook` model instead of an untyped `dict`. +- `Actor.start`, `Actor.call`, `Actor.call_task`, `Actor.set_status_message` and `Actor.abort` return instances of the `ActorRun` model instead of an untyped `dict`. - Upon entering the context manager (`async with Actor`), the `Actor` puts the default logging configuration in place. This can be disabled using the `configure_logging` parameter. - The `config` parameter of `Actor` has been renamed to `configuration`. - Event handlers registered via `Actor.on` will now receive Pydantic objects instead of untyped dicts. For example, where you would do `event['isMigrating']`, you should now use `event.is_migrating` diff --git a/docs/04_upgrading/upgrading_to_v3.md b/docs/04_upgrading/upgrading_to_v3.md index 729ad68b..c48e608a 100644 --- a/docs/04_upgrading/upgrading_to_v3.md +++ b/docs/04_upgrading/upgrading_to_v3.md @@ -110,9 +110,9 @@ async def main(): ) ``` -### Changes in storage clients +## Changes in storage clients -## Explicit control over storage clients used in Actor +### Explicit control over storage clients used in Actor - It is now possible to have full control over which storage clients are used by the `Actor`. To make development of Actors convenient, the `Actor` has two storage clients. One that is used when running on Apify platform or when opening storages with `force_cloud=True` and the other client that is used when running outside the Apify platform. The `Actor` has reasonable defaults and for the majority of use-cases there is no need to change it. However, if you need to use a different storage client, you can set it up before entering `Actor` context through `service_locator`. **Now (v3.0):** @@ -135,7 +135,7 @@ async def main(): ``` -## The default use of optimized ApifyRequestQueueClient +### The default use of optimized ApifyRequestQueueClient - The default client for working with Apify platform based `RequestQueue` is now optimized and simplified client which does significantly lower amount of API calls, but does not support multiple consumers working on the same queue. It is cheaper and faster and is suitable for the majority of the use cases. - The full client is still available, but it has to be explicitly requested via `request_queue_access="shared"` argument when using the `ApifyStorageClient`. diff --git a/docs/04_upgrading/upgrading_to_v4.md b/docs/04_upgrading/upgrading_to_v4.md index d7606598..8f04f4c7 100644 --- a/docs/04_upgrading/upgrading_to_v4.md +++ b/docs/04_upgrading/upgrading_to_v4.md @@ -6,7 +6,7 @@ description: Breaking changes and migration guide from Apify SDK v3.x to v4.0. This guide lists the breaking changes between Apify Python SDK v3.x and v4.0. -## Python 3.11+ required +## Python version support Support for Python 3.10 has been dropped. The Apify Python SDK v4.x now requires Python 3.11 or later — make sure your environment is on a compatible version before upgrading.