Apify
Fronts actor and task execution for Apify on https://api.apify.com. Start a run, wait for it or poll it, and read the dataset it produced. Whatever follows /apify/ is relayed to Apify, provided it names one of the admitted actions:
Admission is default-deny against a fixed allowlist: only the actions listed below get through, and every other path draws a 404. Mounted methods: GET, POST.
Supported scope
Give {actorId} / {actorTaskId} in tilde form as one path segment (e.g. apify~web-scraper); the slash-separated username/name form is not accepted.
Add ?waitForFinish=60 to the create and a run that finishes within 60 seconds comes back finished, in the same response, so most short jobs take one call to start and one to read the dataset. A longer run comes back still running: poll GET /apify/v2/actor-runs/{runId}?waitForFinish=60 until its status is terminal. Page the dataset with limit and offset, and trim each item with fields.
The synchronous run-sync and run-sync-get-dataset-items actions, for both actors and saved tasks, were retired on 2026-09-25 and now answer 404. Their responses carried no cost, so they could only be billed a flat rate that fell short of what most runs cost. Use POST .../runs?waitForFinish=60 and read the run’s dataset instead.
Credential handling
Apify will take its token through two channels — a token query parameter or an Authorization: Bearer header — and the gateway closes off both:
Authorizationis overwritten with Apify’s secret token (Apify reads the very header that carries your gateway token). The gateway consumes your token itself; Apify never receives it.- A
tokensupplied by the client in the query is discarded (any encoding), so nothing can displace the injected token.
Ownership
A run you start asynchronously — and the default dataset it writes to — is registered to your account before the create response reaches you. That registration is what makes the run id and dataset id usable: polling, aborting, and reading dataset items all check it first.
An id belonging to another account answers exactly what an id that never existed answers: a 404, with no upstream call made on its behalf. There’s no separate “forbidden” status, so nothing about anyone else’s runs is disclosed — including whether they exist.
A run of your own that Apify has since expired also ends in a 404, but by a different route: the read is authorized, so it reaches Apify, and what comes back is Apify’s own 404. Handle it the same way — the run is gone — without expecting it to look identical to the ownership refusal above.
The account-wide GET /v2/actor-runs and GET /v2/datasets listings carry no id to check, so they aren’t mounted. Enumerate from your own side: keep the id and defaultDatasetId each create hands back.
If a create succeeds upstream but the gateway can’t record the run as yours, it answers 500 and discards the run rather than returning an id nothing tracks — nothing is charged for it. Retry the request.
Billing
Every run reports its own cost, and it’s billed from that report:
- Starting a run carries no flat charge of its own, and polling and reading the dataset cost nothing.
- Once a run reaches a terminal state —
SUCCEEDED,FAILED,TIMED-OUT, orABORTED— it is billed once, priced from its ownusageTotalUsdas Apify reports it. That figure already sums the run’s compute, proxy traffic, storage operations, dataset writes and the actor’s own per-result charges. - The figure is read about 15 seconds after the run finishes. Apify’s first read of a finished run can still carry preliminary costs, so the gateway reads the run again once they’ve settled. You don’t have to poll for this to happen.
- The charge is taken once per run, no matter how many times you poll it, and an abort followed by a poll still charges once.
- A terminal run that genuinely cost nothing is charged nothing.
- The charge lands on the account that created the run — never on whoever happens to poll it.
usageTotalUsd is a running total while a run is live, which is why nothing is billed until the run is finished: charging mid-run would bill for compute the run hasn’t spent yet.
Examples
Run and wait
Run a saved task
Poll, read, abort
A create answers with the run object. Keep both ids from it:
data.id is what you poll and abort; data.defaultDatasetId is where the run’s items land. Polling returns the same shape with an updated status, finishedAt, and usageTotalUsd.
Responses arrive as Apify’s JSON, untouched. Field-level request and response detail lives in Apify’s API reference; the gateway modifies neither.

