Skip to main content
Version: Next

Timeouts

The client gives every API request a timeout from one of three tiers, each with a default duration suited to the kind of request it covers. Methods that poll for a job to finish run without one:

TierDefaultPurpose
short5 secondsMetadata reads and writes (get(), update(), delete())
medium30 secondsListing, batch and trigger operations (list(), start(), batchAddRequests())
long360 secondsDownloads, uploads and streaming (listItems(), setRecord(), log().get())
noTimeoutnonePolling that waits for a job to finish (call(), waitForFinish())

Every client method is assigned the tier that matches the expected duration of its request. The reference of each method names its tier. You don't need to change the tiers unless you work with unusually large payloads or a slow network.

The client never aborts a request that runs without a timeout, so a connection that stalls is not retried either. ActorClient.call() and RunClient.waitForFinish() poll until the job ends; an explicit timeoutSecs bounds the requests they send, and has to leave room for the minute the API may hold each poll.

Methods such as ActorClient.start() and RunClient.get() take a waitForFinish parameter, which asks the API to hold the response until the job finishes, for up to a minute. Such a request gets the requested wait on top of its tier, so the client doesn't abort it while the API is still holding it.

Configuring default timeouts

Set the duration of each tier on the ApifyClient constructor. The timeoutMaxSecs option caps the timeout of any single request attempt. It bounds the growth of the timeout across retries, and it caps tier and per-call timeouts alike, so raise it whenever you need a request timeout longer than the default 360 seconds. A tier configured above the cap is capped too, and the client logs a warning to make the cut-off visible.

import { ApifyClient } from 'apify-client';

// Configure the default timeout tiers globally.
const client = new ApifyClient({
token: 'MY-APIFY-TOKEN',
timeoutShortSecs: 10,
timeoutMediumSecs: 60,
timeoutLongSecs: 600,
timeoutMaxSecs: 600,
});

const datasetClient = client.dataset('dataset-id');

// Override the timeout for a single call with a number of seconds.
const { items } = await datasetClient.listItems({ timeoutSecs: 120 });

// Or use a tier name to select a predefined timeout.
const dataset = await datasetClient.get({ timeoutSecs: 'long' });

A single request queue client can be held to a shorter budget with client.requestQueue(id, { timeoutSecs }), which caps the default tier of every request that client sends. A per-call timeoutSecs is not capped by it.

Per-call overrides

Every method that sends a request accepts a timeoutSecs option, which replaces the tier of the method for that call. Pass a number of seconds for an exact duration, a tier name to switch tiers, or 'noTimeout' to let the request run for as long as it takes. For the type, see TimeoutOptions.

const datasetClient = client.dataset('my-dataset-id');

// An exact timeout for this call.
const { items } = await datasetClient.listItems({ timeoutSecs: 120 });

// Another tier.
const dataset = await datasetClient.get({ timeoutSecs: 'long' });

// No timeout at all.
await datasetClient.pushItems(items, { timeoutSecs: 'noTimeout' });

A number above timeoutMaxSecs is capped at it, and the client logs a warning. To let such a call use its full timeout, raise timeoutMaxSecs in the client constructor.

Methods that start an Actor run keep the API's run timeout apart from the request timeout. The runTimeoutSecs option of start(), call() and resurrect() bounds how long the run may execute on the platform, while timeoutSecs bounds the request that starts it.

Aborting a call

Every method that accepts timeoutSecs also accepts a signal option that takes an AbortSignal. Once the signal aborts, the client ends the request in flight and skips any remaining retries. It sends no further poll, and the method rejects with the signal's reason. Use it to stop a call from a shutdown handler, or to give a polling method such as call() an overall deadline:

const controller = new AbortController();
process.once('SIGTERM', () => controller.abort());

// Stops waiting for the run once the process receives SIGTERM. The run itself keeps going on the platform.
const run = await client.actor('my-actor').call(input, { signal: controller.signal });

// Gives up on the whole wait after 10 minutes, however many polls it takes.
const finishedRun = await client.run('my-run-id').waitForFinish({ signal: AbortSignal.timeout(600_000) });

Aborting the signal only stops the current API call. To stop an Actor run on the platform, call RunClient.abort().

Interaction with retries

Timeouts work together with retries. A request that times out counts as a failed attempt and is retried, up to maxRetries times. The timeout applies to each attempt on its own, and doubles with every retry up to timeoutMaxSecs, so a request that timed out at 5 seconds gets 10 seconds on the second attempt and 20 on the third.