Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d912c73
feat: add `standby_request` for calling Standby Actors
vdusek Oct 19, 2026
7b85f25
chore: keep error class names intact when syncing async docstrings
vdusek Oct 19, 2026
f7f6d0a
docs: document param conversion and ValueError in `standby_request`
vdusek Oct 19, 2026
181ac08
test: cover more `standby_request` body and header cases
vdusek Oct 19, 2026
3f3d397
docs: shorten the Standby Actors concept page
vdusek Oct 19, 2026
8fc1b16
docs: link the platform docs for Standby authentication
vdusek Oct 19, 2026
4b61d87
feat: add per-request `compression` argument to HTTP clients
vdusek Oct 20, 2026
daabf01
feat: add opt-in body compression to `standby_request`
vdusek Oct 20, 2026
b3995a6
docs: document Standby authentication and body compression
vdusek Oct 20, 2026
2136cca
Merge remote-tracking branch 'origin/master' into feat/standby-request
vdusek Oct 23, 2026
5f04b11
refactor: rename `standby_request` to `server_request`
vdusek Oct 23, 2026
0a42f08
Merge remote-tracking branch 'origin/master' into feat/standby-request
vdusek Oct 26, 2026
4b33343
docs: say Server Actor request bodies are uncompressed by default
vdusek Oct 26, 2026
e22661c
refactor: narrow server_request method and params types
vdusek Oct 26, 2026
5fe9c3c
feat: add get_server_url to Actor clients
vdusek Oct 26, 2026
6f049b8
Merge remote-tracking branch 'origin/master' into feat/standby-request
vdusek Oct 26, 2026
f986ba2
feat: add get_server_openapi_spec to Actor clients
vdusek Oct 26, 2026
4369f0c
Merge remote-tracking branch 'origin/master' into feat/standby-request
vdusek Oct 26, 2026
9514c19
docs: document get_server_url and get_server_openapi_spec
vdusek Oct 26, 2026
1524fba
docs: note that a replaced HttpClient.call must accept compression
vdusek Oct 26, 2026
4eaa7f0
style: keep the Content-Encoding comment next to its check
vdusek Oct 26, 2026
0addab8
docs: clarify the build-number lookup comment in get_server_openapi_spec
vdusek Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions docs/02_concepts/14_server_actors.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
id: server-actors
title: Server Actors
description: Send HTTP requests to Server Actors, and get their URL and OpenAPI specification.
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import ApiLink from '@theme/ApiLink';

import ServerAsyncExample from '!!raw-loader!./code/14_server_async.py';
import ServerSyncExample from '!!raw-loader!./code/14_server_sync.py';
import ServerOtherClientAsyncExample from '!!raw-loader!./code/14_server_other_client_async.py';
import ServerOtherClientSyncExample from '!!raw-loader!./code/14_server_other_client_sync.py';

A [Server Actor](https://docs.apify.com/platform/actors/running/standby) runs a web server. You call it over HTTP, and the platform keeps a run ready to answer, so each call skips the run startup. To send a request to such an Actor, use <ApiLink to="class/ActorClient#server_request">`ActorClient.server_request`</ApiLink>. It takes the path on the Actor's server, and optionally the HTTP method, query parameters, headers, and a body:

<Tabs>
<TabItem value="AsyncExample" label="Async client" default>
<CodeBlock className="language-python">
{ServerAsyncExample}
</CodeBlock>
</TabItem>
<TabItem value="SyncExample" label="Sync client">
<CodeBlock className="language-python">
{ServerSyncExample}
</CodeBlock>
</TabItem>
</Tabs>

The method returns the server's response. To find out which endpoints an Actor serves, see its page in [Apify Store](https://apify.com/store) or its README.

## How the request is sent

Note that:

- The client takes the server's URL from the Actor's `standby_url`. It fetches the Actor from the API on the first call and reuses the URL for later calls on the same `ActorClient`. If the Actor isn't a Server Actor, the method raises an `ApifyClientError`.
- The path is always resolved against that URL, so a path can't send the request to another host.
- By default, the request body is sent uncompressed, since the Actor's server may not decode a compressed body. If the server decodes it, pass `compression`, for example `compression='gzip'`. The client then compresses large bodies the same way as for API calls.

## Calling the server with another HTTP client

For an Actor with many endpoints, you may prefer a client generated from the server's OpenAPI specification, or an HTTP client you already use. Two methods give such a client what it needs:

- <ApiLink to="class/ActorClient#get_server_url">`ActorClient.get_server_url`</ApiLink> returns the server's URL. It fetches and reuses the URL the same way as `server_request`.
- <ApiLink to="class/ActorClient#get_server_openapi_spec">`ActorClient.get_server_openapi_spec`</ApiLink> returns the server's OpenAPI specification as a `dict`. It's the [web server schema](https://docs.apify.com/platform/actors/development/actor-definition/web-server-schema) from the Actor's definition, taken from the build the Actor runs in Server mode. That's the build set in the Actor's Standby settings, or `latest` if none is set. If the Actor doesn't define a web server schema, the method returns `None`.

A request from another client needs your Apify token in the `Authorization` header, and a timeout long enough for a server run to start. For details, see [Authentication](#authentication) and [Timeouts, retries, and errors](#timeouts-retries-and-errors). The following example uses [HTTPX2](https://github.com/pydantic/httpx2), which you can install with the `httpx2` extra:

```sh
pip install "apify-client[httpx2]"
```

<Tabs>
<TabItem value="AsyncExample" label="Async client" default>
<CodeBlock className="language-python">
{ServerOtherClientAsyncExample}
</CodeBlock>
</TabItem>
<TabItem value="SyncExample" label="Sync client">
<CodeBlock className="language-python">
{ServerOtherClientSyncExample}
</CodeBlock>
</TabItem>
</Tabs>

## Authentication

The request carries the client's API token in the `Authorization` header. The platform removes the token before it forwards the request, so the token doesn't reach the Actor's code.

If the Actor reads the `Authorization` header for its own authentication, pass that header in `headers`, and send your Apify token in the `X-Apify-Authorization` header instead. A header you pass in `headers` replaces the client's `Authorization` header for that request.

For details, see [How do I authenticate my requests](https://docs.apify.com/platform/actors/running/standby#how-do-i-authenticate-my-requests) in the platform documentation.

## Timeouts, retries, and errors

When no server run is ready, the first request waits for the platform to start one, which can take a few minutes. The method uses the `long` timeout tier by default for that reason. You can pass a different `timeout`, the same way as for API calls. For details, see [Timeouts](./11_timeouts.mdx).

An error status from the Actor's server raises an `ApifyApiError`, like an error from the API. Server errors and rate-limit responses are retried with the client's retry settings, so a request that the server failed to answer may reach it more than once. If your endpoint isn't safe to call twice, lower `max_retries` on the client. For details, see [Retries](./05_retries.mdx) and [Error handling](./04_error_handling.mdx).
20 changes: 20 additions & 0 deletions docs/02_concepts/code/14_server_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from apify_client import ApifyClientAsync

TOKEN = 'MY-APIFY-TOKEN'


async def main() -> None:
apify_client = ApifyClientAsync(TOKEN)
actor_client = apify_client.actor('username/actor-name')

# Send a GET request to the /search endpoint of the Actor's server.
response = await actor_client.server_request('/search', params={'q': 'apify'})
print(response.json())

# Send a POST request with a JSON body.
response = await actor_client.server_request(
'/items',
method='POST',
json={'url': 'https://apify.com'},
)
print(response.status_code)
25 changes: 25 additions & 0 deletions docs/02_concepts/code/14_server_other_client_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import httpx2

from apify_client import ApifyClientAsync

TOKEN = 'MY-APIFY-TOKEN'


async def main() -> None:
apify_client = ApifyClientAsync(TOKEN)
actor_client = apify_client.actor('username/actor-name')

# Get the server's URL and the OpenAPI specification of its endpoints.
server_url = await actor_client.get_server_url()
spec = await actor_client.get_server_openapi_spec()
if spec is not None:
print(list(spec['paths']))

# The first request to an idle Actor waits for a server run to start.
async with httpx2.AsyncClient(
base_url=server_url,
headers={'Authorization': f'Bearer {TOKEN}'},
timeout=360,
) as http_client:
response = await http_client.get('/search', params={'q': 'apify'})
print(response.json())
25 changes: 25 additions & 0 deletions docs/02_concepts/code/14_server_other_client_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import httpx2

from apify_client import ApifyClient

TOKEN = 'MY-APIFY-TOKEN'


def main() -> None:
apify_client = ApifyClient(TOKEN)
actor_client = apify_client.actor('username/actor-name')

# Get the server's URL and the OpenAPI specification of its endpoints.
server_url = actor_client.get_server_url()
spec = actor_client.get_server_openapi_spec()
if spec is not None:
print(list(spec['paths']))

# The first request to an idle Actor waits for a server run to start.
with httpx2.Client(
base_url=server_url,
headers={'Authorization': f'Bearer {TOKEN}'},
timeout=360,
) as http_client:
response = http_client.get('/search', params={'q': 'apify'})
print(response.json())
20 changes: 20 additions & 0 deletions docs/02_concepts/code/14_server_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from apify_client import ApifyClient

TOKEN = 'MY-APIFY-TOKEN'


def main() -> None:
apify_client = ApifyClient(TOKEN)
actor_client = apify_client.actor('username/actor-name')

# Send a GET request to the /search endpoint of the Actor's server.
response = actor_client.server_request('/search', params={'q': 'apify'})
print(response.json())

# Send a POST request with a JSON body.
response = actor_client.server_request(
'/items',
method='POST',
json={'url': 'https://apify.com'},
)
print(response.status_code)
2 changes: 1 addition & 1 deletion scripts/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"""Path to the source code of the apify_client package."""

_SUBSTITUTIONS = [
(re.compile(r'Client'), 'ClientAsync'),
(re.compile(r'Client(?!Error)'), 'ClientAsync'),
(re.compile(r'\bsynchronously\b'), 'asynchronously'),
(re.compile(r'\bSynchronously\b'), 'Asynchronously'),
(re.compile(r'\bsynchronous\b'), 'asynchronous'),
Expand Down
Loading
Loading