Skip to main content

Apify API: Get a Token and Run an Actor in 5 Minutes

Quick answer: Your APIFY_TOKEN lives under Console → Integrations. Copy it, export it as an environment variable, and send it as Authorization: Bearer <token> on every call to https://api.apify.com/v2. From there you can start an Actor run, poll it, and read the dataset.

That's the whole loop. Most responses wrap payloads in a data object; errors return error.type and error.message. Field-by-field detail lives in the Apify API v2 documentation. This guide covers the parts a reference alone doesn't make obvious: which endpoint to pick for a 10-second job versus a 10-minute one, the timeout each one enforces, and where the output actually ends up.

ConcernDetail
Token locationConsole → Integrations
Base URLhttps://api.apify.com/v2
Auth (recommended)Authorization: Bearer <APIFY_TOKEN>
Actor ID formsInternal ID, or username~actor-name (tilde, not slash, in raw REST paths)
Sync waitPOST .../runs?waitForFinish=60: max 60 seconds. For up to 300s, use POST /v2/acts/{id}/run-sync or /run-sync-get-dataset-items

Replace placeholders (YOUR_TOKEN, compass~crawler-google-places, RUN_ID, DATASET_ID) with your own values. This guide uses Compass's Google Maps Scraper (compass/crawler-google-places) as the running example, the same Actor you can run no-code from Scrape Google Maps with Apify if you'd rather skip the API entirely.

Every number on this page (timeouts, rate limits, webhook event names) was re-checked against live Apify docs on 2026-09-10. Nothing had changed since the last review, but you're reading today's numbers, not an archived copy.

Common endpoints (cheat sheet)​

MethodEndpointPurpose
POST/acts/{actorId}/runsStart an Actor run; body { "input": { ... } }
GET/actor-runs/{runId}Poll status, defaultDatasetId, status
GET/datasets/{datasetId}/itemsPage through result rows (limit, offset, format)
GET/PUT/POST/key-value-stores/...Read/write run metadata, INPUT, OUTPUT
POST/webhooksRegister HTTP callbacks for run events
GET/users/meSanity-check token / account

Authentication (API token)​

Create a token under Apify Console → Settings → API & Integrations. That tab is also where you revoke one, and where you review which third-party apps are connected to your account. Treat revocation as the recovery path: a leaked token cannot be rotated in place, so generate the replacement, move your deployments over, then revoke the old one. Two-factor authentication and active session management sit separately under Settings → Login & Privacy (help.apify.com — account settings, checked 2026-09-10).

Export the token in your shell rather than committing it:

export APIFY_TOKEN='YOUR_TOKEN'

curl (verify the token)

curl -sS "https://api.apify.com/v2/users/me" \
-H "Authorization: Bearer ${APIFY_TOKEN}"

Python

import os, requests

token = os.environ["APIFY_TOKEN"]
r = requests.get(
"https://api.apify.com/v2/users/me",
headers={"Authorization": f"Bearer {token}"},
timeout=60,
)
r.raise_for_status()
print(r.json()["data"])

JavaScript (fetch)

const token = process.env.APIFY_TOKEN;
const res = await fetch('https://api.apify.com/v2/users/me', {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(await res.text());
const { data } = await res.json();
console.log(data);

Run an Actor synchronously (wait for finish)​

waitForFinish on POST /v2/acts/{id}/runs caps at 60 seconds. For longer sync calls (up to 300s), use POST /v2/acts/{id}/run-sync (returns the run object) or POST /v2/acts/{id}/run-sync-get-dataset-items (returns dataset items directly). Anything longer: start async and poll, or use webhooks.

curl

ACTOR_ID='compass~crawler-google-places'

curl -sS -X POST "https://api.apify.com/v2/acts/${ACTOR_ID}/runs?waitForFinish=60" \
-H "Authorization: Bearer ${APIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"input": {
"searchStringsArray": ["plumbers in New York"],
"maxCrawledPlacesPerSearch": 10
}
}'

Python

import os, requests

token = os.environ["APIFY_TOKEN"]
actor_id = "compass~crawler-google-places"
url = f"https://api.apify.com/v2/acts/{actor_id}/runs?waitForFinish=60"
payload = {
"input": {
"searchStringsArray": ["plumbers in New York"],
"maxCrawledPlacesPerSearch": 10,
}
}
r = requests.post(
url,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json=payload,
timeout=400,
)
r.raise_for_status()
run = r.json()["data"]
print(run["id"], run["status"], run["defaultDatasetId"])

JavaScript (apify-client, recommended)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('compass/crawler-google-places').call({
searchStringsArray: ['plumbers in New York'],
maxCrawledPlacesPerSearch: 10,
});

console.log(run.id, run.status, run.defaultDatasetId);

Install: npm install apify-client. The client maps compass/crawler-google-places to the API’s username~actor form.

Run an Actor asynchronously (fire-and-forget)​

Omit waitForFinish (or set 0). You receive a runId immediately.

curl

ACTOR_ID='compass~crawler-google-places'

curl -sS -X POST "https://api.apify.com/v2/acts/${ACTOR_ID}/runs" \
-H "Authorization: Bearer ${APIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"input":{"searchStringsArray":["plumbers in Austin"],"maxCrawledPlacesPerSearch":5}}'

Python

import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("compass/crawler-google-places").start(
run_input={
"searchStringsArray": ["plumbers in Austin"],
"maxCrawledPlacesPerSearch": 5,
}
)
print("started", run["id"])

pip install apify-client

JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const { id } = await client.actor('compass/crawler-google-places').start({
searchStringsArray: ['plumbers in Austin'],
maxCrawledPlacesPerSearch: 5,
});
console.log('started', id);

Poll run status​

Poll GET /v2/actor-runs/{runId} until status is terminal (SUCCEEDED, FAILED, ABORTED, TIMED-OUT).

curl

RUN_ID='YOUR_RUN_ID'

curl -sS "https://api.apify.com/v2/actor-runs/${RUN_ID}" \
-H "Authorization: Bearer ${APIFY_TOKEN}"

Python

import os, time, requests

token = os.environ["APIFY_TOKEN"]
run_id = "YOUR_RUN_ID"
headers = {"Authorization": f"Bearer {token}"}

while True:
r = requests.get(f"https://api.apify.com/v2/actor-runs/{run_id}", headers=headers, timeout=60)
r.raise_for_status()
run = r.json()["data"]
print(run["status"])
if run["status"] in ("SUCCEEDED", "FAILED", "ABORTED", "TIMED-OUT"):
break
time.sleep(5)

JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.run('YOUR_RUN_ID').waitForFinish();
console.log(run.status, run.defaultDatasetId);

Read dataset output​

Use defaultDatasetId from the run. Paginate with limit and offset.

curl

DATASET_ID='YOUR_DATASET_ID'

curl -sS "https://api.apify.com/v2/datasets/${DATASET_ID}/items?format=json&clean=true&limit=1000&offset=0" \
-H "Authorization: Bearer ${APIFY_TOKEN}"

Python

import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
page = client.dataset("YOUR_DATASET_ID").list_items(limit=1000, clean=True)
items = page.items
print(len(items))

JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const result = await client.dataset('YOUR_DATASET_ID').listItems({ limit: 1000 });
console.log(result.items.length);

Webhooks (run finished → your HTTPS endpoint)​

Register a webhook with POST /v2/webhooks. You must own or control requestUrl (HTTPS). The condition object scopes events; actorId is often the Actor’s internal ID from the Actor’s API tab in Console (you can also manage webhooks in the UI).

curl

curl -sS -X POST "https://api.apify.com/v2/webhooks" \
-H "Authorization: Bearer ${APIFY_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"requestUrl": "https://your.domain/apify-webhook",
"eventTypes": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED", "ACTOR.RUN.TIMED_OUT"],
"condition": { "actorId": "YOUR_ACTOR_INTERNAL_ID" },
"description": "notify backend when compass maps run ends"
}'

Python

import os, requests

token = os.environ["APIFY_TOKEN"]
body = {
"requestUrl": "https://your.domain/apify-webhook",
"eventTypes": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED", "ACTOR.RUN.TIMED_OUT"],
"condition": {"actorId": "YOUR_ACTOR_INTERNAL_ID"},
"description": "pipeline notifications",
}
r = requests.post(
"https://api.apify.com/v2/webhooks",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json=body,
timeout=60,
)
r.raise_for_status()
print(r.json()["data"])

JavaScript

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const wh = await client.webhooks().create({
requestUrl: 'https://your.domain/apify-webhook',
eventTypes: ['ACTOR.RUN.SUCCEEDED', 'ACTOR.RUN.FAILED', 'ACTOR.RUN.TIMED_OUT'],
condition: { actorId: 'YOUR_ACTOR_INTERNAL_ID' },
description: 'pipeline notifications',
});
console.log(wh.id);

Payload shape and template interpolation are documented under Webhooks in API v2.

Rate limits​

ScopeLimit
Global250,000 requests / minute per user (or per IP if unauthenticated)
Default per resource60 requests / second (single Actor, run, dataset, store, etc.)
Key-value store records200 rps
Dataset items / request-queue operations400 rps

On 429, sleep with jitter and retry (exponential backoff). The official JavaScript and Python clients implement this for you.

Next steps​

Frequently Asked Questions

Use version 2 at https://api.apify.com/v2. All examples in this guide target that base path.

Under Console → Integrations, in your Apify account settings. Copy it and send it as Authorization: Bearer <token> on each request. Avoid ?token= in URLs for production, because it leaks via logs and referrers.

Up to 60 seconds on POST /v2/acts/{id}/runs. For up to 300 seconds, use POST /v2/acts/{id}/run-sync or /run-sync-get-dataset-items. For anything longer, start asynchronously and poll GET /v2/actor-runs/{runId}, or attach a webhook.

Usually in the run’s default dataset (defaultDatasetId). Some Actors also write INPUT/OUTPUT or files to the default key-value store, so check the run object and the Actor’s README.

POST /v2/actor-tasks/{taskId}/runs with the same patterns (optional waitForFinish, same dataset flow). Task IDs use username~taskName naming similar to Actors.

Globally ~250k requests/minute per account. Per resource, most endpoints default to 60 rps; dataset/queue endpoints allow up to 400 rps; key-value record CRUD allows 200 rps. Official clients retry 429/5xx with exponential backoff.

Yes. Use the HTTP module with Bearer auth, or native Apify modules. For runs longer than Make’s module timeout, start async and continue on webhook or polling. See the Make.com integration guide on this site.

Common mistakes and fixes

401 token-not-provided or 403 on run.

Send `Authorization: Bearer <token>` on every call. Use a token from [Console → Integrations](https://console.apify.com/account/integrations?fpr=use-apify). Named Actor IDs (`username~actor`) require auth.

Synchronous run returns 408 or times out.

`waitForFinish` waits up to 60s on `POST /v2/acts/{id}/runs`. For longer runs, omit it and poll `GET /v2/actor-runs/{runId}`, attach a webhook, or use the `run-sync` / `run-sync-get-dataset-items` endpoints (max 300s).

429 rate-limit-exceeded.

Backoff exponentially. Prefer official clients (`apify-client`), which retry 429/5xx. Spread polling across time; avoid tight loops per resource.

Run succeeds but dataset looks empty.

Confirm `defaultDatasetId` from the run object. Some Actors write to key-value stores, so use `defaultKeyValueStoreId` and record keys from logs.