Skip to content

feat: add Server Actor support (formerly Actor Standby) - #1153

Merged
vdusek merged 57 commits into
masterfrom
feat/standby-request
Nov 4, 2026
Merged

vdusek merged 57 commits into
masterfrom
feat/standby-request

Conversation

@vdusek

@vdusek vdusek commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Adds ActorClient.server() and ActorClientAsync.server(), which return an ActorServerClient / ActorServerClientAsync for Server Actors (formerly Actor Standby):

actor_server_client = client.actor('username/actor-name').server()
response = actor_server_client.request('/search', params={'q': 'apify'})
url = actor_server_client.get_url()
spec = actor_server_client.get_openapi_spec()
  • request() sends a request to the Actor's server. The host always comes from the Actor's standbyUrl. The token goes in the X-Apify-Authorization header, which the platform strips, so headers the caller passes, Authorization included, reach the Actor as they are. It doesn't follow redirects and returns a redirect response as it is, so the token can't reach a host the Actor's code picked.
  • request() returns the server's response whatever its status. Only 429 and 503 are retried, since other statuses may come from the Actor's code. Network errors and timeouts aren't retried, since the Actor may have already handled the request. It raises only when it gets no response: the Actor doesn't exist or isn't a Server Actor, or the request fails or times out.
  • request() sends bodies uncompressed unless compression is set, returns the body unread with stream=True, and defaults to the long timeout to cover a cold start.
  • get_url() returns the server URL. It's fetched once per ActorClient, by get_url() or get_openapi_spec().
  • get_openapi_spec() returns the web server schema from the build that serves the server (actorStandby.build, or latest), or None if there's none. The schema field comes from fix(openapi): add missing fields to the Actor definition schema apify-docs#3115.
  • get_url() and get_openapi_spec() take a timeout.

ActorClient.standby() is a deprecated alias of server() and will be removed in v4.

Other changes:

  • HttpClient.call and HttpClientAsync.call take new arguments: compression, authorization_header, raise_for_status, retry_status_codes, retry_transport_errors, and follow_redirects. A custom HTTP client that overrides call must accept and honor them, and one that implements send_request must honor follow_redirects.
  • Request params are appended with & to a URL that already has a query.
  • The docstring sync script no longer turns ApifyClientError into a nonexistent ApifyClientAsyncError.
  • A Server Actors concepts page, unit tests, and an integration test against a temporary echo Server Actor.

Closes: #973

✍️ Drafted by Claude Code

@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Oct 19, 2026
@vdusek vdusek self-assigned this Oct 19, 2026
@codecov

codecov Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.39583% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.72%. Comparing base (f6be852) to head (ddd6a06).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
src/apify_client/_resource_clients/actor_server.py 97.43% 3 Missing ⚠️
src/apify_client/types.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1153      +/-   ##
==========================================
+ Coverage   95.67%   95.72%   +0.05%     
==========================================
  Files          61       62       +1     
  Lines        6473     6651     +178     
==========================================
+ Hits         6193     6367     +174     
- Misses        280      284       +4     
Flag Coverage Δ
integration 91.08% <81.25%> (-0.52%) ⬇️
unit 90.66% <96.35%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

vdusek added a commit to apify/apify-docs that referenced this pull request Oct 20, 2026
While working on better support for calling Standby Actors from the API
client (apify/apify-client-python#1153), I couldn't find an explanation
of how the authentication headers are handled (Maybe I just missed it?).
I have two questions:

1. Does the Apify token reach the Actor's code? (It shouldn't.).
2. If an Actor needs its own authentication header, what's the best
practice for sending it? Is it `X-Apify-Authorization`?

I drafted a change; see the diff and suggest better/correct/more precise
explanation. Thank you.

(tagging @mvolfik, as I know a lot of the work there was done by you)
@vdusek
vdusek requested review from Pijukatel and removed request for Pijukatel October 20, 2026 06:58
@vdusek vdusek changed the title feat: add standby_request for calling Standby Actors feat: add standby_request for calling Standby Actors, with per-request compression in HTTP clients Oct 20, 2026
@vdusek vdusek changed the title feat: add standby_request for calling Standby Actors, with per-request compression in HTTP clients feat: add server_request for calling Server Actors, with per-request compression in HTTP clients Oct 23, 2026
@vdusek vdusek changed the title feat: add server_request for calling Server Actors, with per-request compression in HTTP clients feat: add server_request for calling Server Actors endpoints Oct 26, 2026
@vdusek vdusek changed the title feat: add server_request for calling Server Actors endpoints feat: add server_request for calling Server Actor endpoints Oct 26, 2026
@vdusek
vdusek requested review from Pijukatel and janbuchar October 26, 2026 07:40
@vdusek
vdusek marked this pull request as ready for review October 26, 2026 07:40
@vdusek
vdusek requested a review from szaganek as a code owner October 26, 2026 07:40
@apify-service-account apify-service-account added the tested Temporary label used only programatically for some analytics. label Oct 26, 2026

@janbuchar janbuchar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, even though apify/apify-client-js#1179 (comment) applies here as well

@vdusek
vdusek merged commit 63bd448 into master Nov 4, 2026
37 checks passed
@vdusek
vdusek deleted the feat/standby-request branch November 4, 2026 07:40
vdusek added a commit to apify/apify-client-js that referenced this pull request Nov 4, 2026
Adds `ActorClient.server()`, which returns an `ActorServerClient` for
Server Actors (formerly Actor Standby):

```ts
const actorServerClient = client.actor('username/actor-name').server();
const response = await actorServerClient.request('/search', { params: { q: 'apify' } });
const url = await actorServerClient.getUrl();
const spec = await actorServerClient.getOpenApiSpec();
```

- `request()` sends a request to the Actor's server. The host always
comes from the Actor's `standbyUrl`. The token goes in the
`X-Apify-Authorization` header, which the platform strips, so headers
the caller passes, `Authorization` included, reach the Actor as they
are. It doesn't follow redirects and returns a redirect response as it
is, so the token can't reach a host the Actor's code picked.
- `request()` returns the server's response whatever its status. Only
429 and 503 are retried, since other statuses may come from the Actor's
code. Network errors and timeouts aren't retried, since the Actor may
have already handled the request. It throws only when it gets no
response: the Actor doesn't exist or isn't a Server Actor, or the
request fails or times out.
- `request()` sends bodies uncompressed unless `compression` is set,
returns the body parsed, as a buffer, or as a stream depending on
`responseType`, and defaults to the `long` timeout to cover a cold
start.
- `getUrl()` returns the server URL. It's fetched once per
`ActorClient`, by `getUrl()` or `getOpenApiSpec()`.
- `getOpenApiSpec()` returns the web server schema from the build that
serves the server (`actorStandby.build`, or `latest`), or `undefined` if
there's none.

`ActorClient.standby()` is a deprecated alias of `server()` and will be
removed in v4.

Other changes:

- `ApifyRequestConfig` takes new per-request options: `compression`,
`authorizationHeader`, `throwOnErrorStatus`, `retryStatusCodes`,
`retryTransportErrors`, and `followRedirects`. A custom HTTP client that
overrides `call()` should honor them, and one that overrides
`sendRequest()` must honor the new `HttpRequest.followRedirects` field,
or it would follow a redirect with the token.
- A Server Actors concepts page, unit tests, and an integration test
against a temporary echo Server Actor.

Port of apify/apify-client-python#1153.

Closes: #1159

*✍️ Drafted by Claude Code*
vdusek added a commit that referenced this pull request Nov 5, 2026
…1202)

Renames the `actor_standby_*` arguments of `ActorClient.update`,
`ActorCollectionClient.create`, `TaskClient.update`, and
`TaskCollectionClient.create` (sync and async) to the Server Actor
naming used by `ActorClient.server()`:

| Deprecated | New |
| --- | --- |
| `actor_standby_is_enabled` | `server_is_enabled` |
| `actor_standby_desired_requests_per_actor_run` |
`server_desired_requests_per_actor_run` |
| `actor_standby_max_requests_per_actor_run` |
`server_max_requests_per_actor_run` |
| `actor_standby_idle_timeout` | `server_idle_timeout` |
| `actor_standby_build` | `server_build` |
| `actor_standby_memory_mbytes` | `server_memory_mbytes` |
| `actor_standby_disable_standby_fields_override` |
`server_disable_fields_override` |
| `actor_standby_should_pass_actor_input` |
`server_should_pass_actor_input` |

The task methods take the five arguments they had before. The old names
stay as deprecated aliases until v4, the same way as `build_tag` in
`validate_input()`. Each one set emits a `DeprecationWarning` at the
caller, and passing an alias together with its new name raises a
`ValueError`. All eight methods share one helper, `build_actor_standby`,
which maps the aliases and builds the `actorStandby` field.

`actor_standby_tenancy` from #1169 isn't on master yet, so it isn't
covered here. #1169 can add it as `server_tenancy` directly, without an
alias. It should ship in the same release as #1153.

Closes: #1195

*✍️ Drafted by Claude Code*
janbuchar pushed a commit to apify/apify-client-js that referenced this pull request Nov 10, 2026
…1179)

Adds `ActorClient.server()`, which returns an `ActorServerClient` for
Server Actors (formerly Actor Standby):

```ts
const actorServerClient = client.actor('username/actor-name').server();
const response = await actorServerClient.request('/search', { params: { q: 'apify' } });
const url = await actorServerClient.getUrl();
const spec = await actorServerClient.getOpenApiSpec();
```

- `request()` sends a request to the Actor's server. The host always
comes from the Actor's `standbyUrl`. The token goes in the
`X-Apify-Authorization` header, which the platform strips, so headers
the caller passes, `Authorization` included, reach the Actor as they
are. It doesn't follow redirects and returns a redirect response as it
is, so the token can't reach a host the Actor's code picked.
- `request()` returns the server's response whatever its status. Only
429 and 503 are retried, since other statuses may come from the Actor's
code. Network errors and timeouts aren't retried, since the Actor may
have already handled the request. It throws only when it gets no
response: the Actor doesn't exist or isn't a Server Actor, or the
request fails or times out.
- `request()` sends bodies uncompressed unless `compression` is set,
returns the body parsed, as a buffer, or as a stream depending on
`responseType`, and defaults to the `long` timeout to cover a cold
start.
- `getUrl()` returns the server URL. It's fetched once per
`ActorClient`, by `getUrl()` or `getOpenApiSpec()`.
- `getOpenApiSpec()` returns the web server schema from the build that
serves the server (`actorStandby.build`, or `latest`), or `undefined` if
there's none.

`ActorClient.standby()` is a deprecated alias of `server()` and will be
removed in v4.

Other changes:

- `ApifyRequestConfig` takes new per-request options: `compression`,
`authorizationHeader`, `throwOnErrorStatus`, `retryStatusCodes`,
`retryTransportErrors`, and `followRedirects`. A custom HTTP client that
overrides `call()` should honor them, and one that overrides
`sendRequest()` must honor the new `HttpRequest.followRedirects` field,
or it would follow a redirect with the token.
- A Server Actors concepts page, unit tests, and an integration test
against a temporary echo Server Actor.

Port of apify/apify-client-python#1153.

Closes: #1159

*✍️ Drafted by Claude Code*

2.x backport: Built on the 2.x axios `HttpClient`, which gains opt-in `throwOnErrorStatus`, `retryStatusCodes`, `retryTransportErrors` and `doNotCompress` request options. There is no `compression` option, since it depends on the pluggable compressors from #1065. The request body is always sent uncompressed. The `AbortSignal` support and the timeout tiers are not ported, and errors are plain `Error`s.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for calling Standby Actors

4 participants