Actor runs
List, inspect, abort, metamorph, resurrect, charge, and read logs and storages of runs.
Inspect, update, abort, metamorph, reboot, resurrect, charge, and read logs for Actor runs. All paths are relative to https://api.scrape.ly.
To start a run, use POST /v2/actors/{actorId}/runs or run a task. The JSON body is the input object at the top level.
Statuses: READY, RUNNING, SUCCEEDED, FAILED, ABORTING, ABORTED, TIMING-OUT, TIMED-OUT. Terminal statuses are SUCCEEDED, FAILED, ABORTED, and TIMED-OUT.
Run statuses use a hyphen: TIMED-OUT. Webhook event names use an underscore: ACTOR.RUN.TIMED_OUT.
List runs
/v2/actor-runsLists your runs across all Actors. Accepts a user API key, an OAuth token, or a full-permission scoped token. Limited scoped tokens get 403 insufficient-permissions.
| Query param | Description |
|---|---|
status | One value or a comma-separated list, for example RUNNING,SUCCEEDED. Use TIMED-OUT. |
startedAfter / startedBefore | ISO 8601 timestamps. |
origin | How the run was started: API, CLI, WEB, DEVELOPMENT, or SCHEDULER. |
search | Matches the run ID or the Actor name. |
sortBy | startedAt (default), finishedAt, status, duration, or usage. |
limit / offset / desc | Pagination. Default and maximum limit is 1000. desc defaults to false. |
To list the runs of one Actor, use GET /v2/actors/{actorId}/runs. It takes the same status, startedAfter, startedBefore, sortBy, and pagination parameters. For a task, use GET /v2/actor-tasks/{taskId}/runs.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
"https://api.scrape.ly/v2/actor-runs?status=RUNNING,SUCCEEDED&limit=5&desc=1"/v2/actor-runs?limit=5&desc=1Get run
/v2/actor-runs/{runId}You can only read your own runs. A run's scoped token can read its own run and the child runs it started. If the Actor defines an output schema, the response also includes a computed output map of URLs. On someone else's Actor, templates that reference build fields (such as {{run.buildId}}) resolve to empty strings.
Build info is owner only. On runs of an Actor you don't own, buildId, buildNumber, and options.build are left out, here and everywhere else runs are returned (run lists, task lastRun, webhooks, and the dashboard's live updates).
Pass waitForFinish (seconds, up to 60) to wait for the run to reach a terminal status. The response comes as soon as the run finishes, or after 45 seconds with the run in its current state (see Waiting for runs and builds). To wait longer, repeat the request until status is terminal. Without waitForFinish, the endpoint returns immediately.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/v2/actor-runs/{runId}usageUsd breaks the run's cost down by service and usageTotalUsd is the total, both in US dollars. usageTotalNd is the same total in nanodollars ($1 = 1_000_000_000), an exact integer for accounting. Run lists and search return usageTotalUsd and usageTotalNd too.
Read tabular results from defaultDatasetId (dataset items). Named output such as OUTPUT is a record in defaultKeyValueStoreId. You can also reach both through the run storage shortcuts. There is no /output run endpoint.
Get run input
/v2/actor-runs/{runId}/inputReturns the INPUT record from the run's default key-value store as raw JSON, not wrapped in data. If the run has no input, the body is null.
Secret input fields come back encrypted (ENCRYPTED_VALUE:...), including for your API key. Only the run's own token gets them decrypted, so Actor.getInput() inside the run sees plain values.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/input/v2/actor-runs/{runId}/inputRun storages
/v2/actor-runs/{runId}/dataset/v2/actor-runs/{runId}/dataset/items/v2/actor-runs/{runId}/key-value-store/v2/actor-runs/{runId}/key-value-store/keys/v2/actor-runs/{runId}/key-value-store/records/{recordKey}/v2/actor-runs/{runId}/request-queue/v2/actor-runs/{runId}/request-queue/head/v2/actor-runs/{runId}/request-queue/requestsAny path under /v2/actor-runs/{runId}/dataset, /key-value-store, or /request-queue is forwarded to the run's default storage. It behaves exactly like the matching dataset, key-value store, or request queue endpoint, with every method and parameter, so you don't need to look up the storage ID first.
Only the run owner, or the run's own scoped token, can use these paths. An unknown run returns 404 run-not-found. A run without that default storage returns 404 record-not-found.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
"https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/dataset/items?format=csv&limit=100"/v2/actor-runs/{runId}/dataset/items?limit=10/v2/actor-runs/{runId}/key-value-store/records/OUTPUTUpdate run
/v2/actor-runs/{runId}| Body field | Type | Description |
|---|---|---|
statusMessage | string | Message shown on the run. |
isStatusMessageTerminal | boolean | Whether the status message is final. |
generalAccess | string | FOLLOW_USER_SETTING, ANYONE_WITH_ID_CAN_READ, or RESTRICTED. |
id | string | If present, must match {runId}. |
An id that doesn't match, or an unknown generalAccess value, returns 400 invalid-request. Returns 200 with the updated run under data.
curl -X PUT https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"statusMessage": "Crawling page 12/40"}'Delete run
/v2/actor-runs/{runId}Deletes a run that is READY or has finished. A run that is still RUNNING, ABORTING, or TIMING-OUT returns 400 invalid-state, so abort it first. Returns 204 No Content.
Deleting a run removes the run record. Its default storages are not deleted right away; they expire with your storage retention period.
curl -X DELETE https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt \
-H "Authorization: Bearer $SCRAPELY_TOKEN"Abort run
/v2/actor-runs/{runId}/abortStops a run. The run moves to ABORTING and then ABORTED. Pass gracefully=1 (or true) to give the Actor time to shut down cleanly instead of stopping the container at once. A READY run that hasn't started yet goes straight to ABORTED. Aborting a run that already finished, or is already ABORTING, changes nothing and returns the run as it is.
Returns 200 OK with the run under data:
{
"data": {
"id": "a7XyZ8kLmN2pQ4rSt",
"status": "ABORTING"
}
}curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
"https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/abort?gracefully=1"Metamorph run
/v2/actor-runs/{runId}/metamorphTurns a RUNNING run into a run of a different Actor, keeping the same run ID and storages. A run can metamorph at most 10 times.
| Parameter | Where | Description |
|---|---|---|
targetActorId | body or query | Required. Target Actor ID or username~name. actorId is accepted as an alias. |
input | body | New input for the target Actor (not runInput). |
build | body or query | Target build tag or number. Only for a target Actor you own; on someone else's Actor it's ignored and the User Build is used. |
curl -X POST https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/metamorph \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"targetActorId": "otheruser~other-actor",
"input": { "url": "https://example.com" },
"build": "latest"
}'The previous INPUT is stored as INPUT-METAMORPH-N in the default key-value store.
Returns 200 OK with the run under data, including a metamorphs array. Each item has createdAt, actorId, buildId (only for Actors you own), and inputKey. If the target Actor has no matching build, the run's buildId and buildNumber are cleared after the switch. The new container gets the target build's ACTOR_BUILD_ID, ACTOR_BUILD_NUMBER, and ACTOR_BUILD_TAGS, not the original Actor's. The container switches to the target Actor after the response, so actId in this response still shows the original Actor. Read the run again to see the new actId.
{
"data": {
"id": "a7XyZ8kLmN2pQ4rSt",
"actId": "eLN5rTB8sYTBWdfzx",
"status": "RUNNING",
"metamorphs": [
{
"createdAt": "2026-08-14T15:00:00.000Z",
"actorId": "targetActorId123",
"buildId": "bUiLdIdExAmPlE123",
"inputKey": "INPUT-METAMORPH-1"
}
]
}
}| Status | Type | Meaning |
|---|---|---|
| 400 | invalid-state | The run is not RUNNING. |
| 400 | limit-exceeded | The run already metamorphed 10 times. |
| 403 | insufficient-permissions | A limited-permission run tried to metamorph into a full-permission Actor. |
| 403 | full-permission-actor-not-approved | The target Actor needs full permissions and you haven't approved it. See Permission approvals. |
| 404 | actor-not-found | The target Actor does not exist. |
The wrong-state status code differs by endpoint: metamorph and resurrect return 400, while charge returns 405.
Reboot run
/v2/actor-runs/{runId}/rebootRestarts the run's container and keeps its storages. Allowed while the run is READY or RUNNING; any other status returns 400 invalid-run-status. Returns 202:
{ "data": { "accepted": true, "runId": "a7XyZ8kLmN2pQ4rSt" } }curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/rebootResurrect run
/v2/actor-runs/{runId}/resurrectRestarts a finished run in place, with the same run ID and storages. The run must be SUCCEEDED, FAILED, ABORTED, or TIMED-OUT. Returns 200 with the run under data and a Location header. The run goes back to RUNNING, and an ACTOR.RUN.RESURRECTED webhook event fires.
Optional query parameters (defaults come from the original run): build, timeout, memory, maxItems, maxTotalChargeUsd, restartOnError. build is owner only; on someone else's Actor the run keeps its original build, or runs the User Build if that build was deleted.
| Status | Type | Meaning |
|---|---|---|
| 400 | invalid-state | The run has not finished. |
| 400 | invalid-memory / limit-exceeded | The memory is invalid or would exceed your total memory limit. |
| 402 | payment-required | Not enough credit to start the run. |
| 429 | concurrency-limit-exceeded / run-capacity-check-busy | You are at your concurrent run limit, or another run request is being evaluated. |
curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
"https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/resurrect?memory=4096"Charge events
/v2/actor-runs/{runId}/chargeCharges pay-per-event events against a RUNNING run you own. The SDK charging helpers and scrapely actor charge call this endpoint.
| Body field | Type | Description |
|---|---|---|
eventName | string | Required. An event name from the Actor's pricing. 1–128 bytes, starts with a letter or digit, then letters, digits, ., _, :, or -. |
count | integer | Number of events, from 1 to 1000000. Default 1. |
The dataset-item event (scrapely-default-dataset-item) is charged automatically when items are pushed and cannot be charged through this endpoint.
Send an Idempotency-Key header (up to 128 characters of letters, digits, ., _, :, or -) whenever the charge is in a code path that can retry. Without it, a retry after a network error charges twice. A replay with the same key and payload returns the original result with duplicate: true. Keys are remembered for at least 35 days after the run ends, and a replay works even after the run has finished. If the charge was voided, the replay returns acceptedCount: 0.
Response (201, not wrapped in data). operationId is your Idempotency-Key, or a generated ID if you didn't send one:
{
"operationId": "charge-1",
"eventName": "result-item",
"acceptedCount": 10,
"requestedCount": 10,
"duplicate": false
}If the charge would go over the run's maxTotalChargeUsd, only part of it may be accepted: compare acceptedCount with requestedCount.
| Status | Type | Meaning |
|---|---|---|
| 201 | — | Charge recorded, or replayed for a repeated Idempotency-Key. |
| 400 | invalid-input | Bad eventName, count, or Idempotency-Key. error.code says which: event-name-invalid, event-name-too-long, event-name-server-derived, count-invalid, count-too-large, idempotency-key-invalid, or idempotency-key-too-long. |
| 400 | unknown-event | The Actor's pricing has no event with that name. |
| 402 | charge-limit-exceeded | The run already reached maxTotalChargeUsd. Next to error, the body includes acceptedCount: 0 and requestedCount. |
| 403 | forbidden | Not your run. |
| 404 | run-not-found | The run does not exist. |
| 405 | invalid-state | The run is not RUNNING (and the request is not a replay). |
| 409 | conflict | The Idempotency-Key was already used with a different event or count. |
| 409 | ledger-sealed | The run has finished and its charges were finalized. |
| 503 | ledger-unavailable | Charging is temporarily unavailable. Retry with the same Idempotency-Key. |
curl -X POST https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/charge \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: charge-1" \
-d '{"eventName": "result-item", "count": 10}'Void a charge
/v2/actor-runs/{runId}/charge/voidReverses a charge whose items were never stored, so a result is only billed if it was saved. The SDKs call this when a push fails after its charge was accepted. No body. The Idempotency-Key header is required and is the key of the charge to void.
Only the run's own token can void its charges; a user API token gets 403 forbidden. Voiding works until the run is settled.
Response (200, not wrapped in data). status is voided, or already-voided for a repeated void. voidedCount is the number of events reversed:
{ "status": "voided", "voidedCount": 10 }| Status | Type | Meaning |
|---|---|---|
| 200 | — | Charge voided, or already voided. |
| 400 | invalid-input | The Idempotency-Key is missing, invalid, or too long. error.code says which: idempotency-key-required, idempotency-key-invalid, or idempotency-key-too-long. |
| 403 | forbidden | Not your run, or not the run's own token. |
| 404 | run-not-found | The run does not exist. |
| 404 | charge-not-found | No charge with this Idempotency-Key was applied to the run. |
| 409 | ledger-sealed | The run has been settled; its charges can no longer be voided. |
| 503 | ledger-unavailable | The void could not be confirmed. Retry with the same Idempotency-Key. |
curl -X POST https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/charge/void \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Idempotency-Key: charge-1"Get run log
/v2/actor-runs/{runId}/logReturns the log as plain text (Content-Type: text/plain), not a JSON envelope. There is no /log/stream endpoint.
On runs of an Actor you don't own, Scrape.ly's own log lines name no build: image names (which carry the build number) are replaced with [actor image]. The same applies to older runs whose log is read as the log record of the run's key-value store.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/actor-runs/a7XyZ8kLmN2pQ4rSt/log/v2/actor-runs/{runId}/logFor the last run of an Actor or task, see GET /v2/actors/{actorId}/runs/last/log and GET /v2/actor-tasks/{taskId}/runs/last/log.
Get log by ID
/v2/logs/{logId}Returns the log of a run or a build by its ID, as plain text. The ID is checked as a run first, then as a build. Owner only: someone else's run returns 403 forbidden, and an unknown build or one you can't read returns 404 record-not-found.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/logs/a7XyZ8kLmN2pQ4rSt