Skip to content
API

Request queues

Add, lock, and update crawl requests so several workers can share one queue.

Request queues hold a dynamic list of URLs to crawl, with locking so that several workers do not fetch the same request. All endpoints are prefixed with https://api.scrape.ly/v2. Authenticate with Authorization: Bearer $SCRAPELY_TOKEN.

Refer to a queue by its UUID or, on every route except updating and deleting the queue itself, by named ID: username~queue-name, where username is your own public username.

Unnamed queues expire after your account's unnamed-storage retention period: 2 days (48 hours) on the free plan, and 31, 60 or 90 days on the Starter, Scale or Business plan. On the free plan, the default storages of your latest 10 runs are kept until those runs are deleted. Named queues do not expire. Creating a named queue that you already own returns the existing queue.

A run's default queue is also reachable under the run: /v2/actor-runs/{runId}/request-queue and its sub-paths (for example /v2/actor-runs/{runId}/request-queue/head) are rewritten to /v2/request-queues/{defaultRequestQueueId}/.... You must own the run.

Endpoints

MethodPathDescription
GET/v2/request-queuesList queues you own
POST/v2/request-queuesCreate a queue
GET/v2/request-queues/{queueId}Get queue metadata
PUT/v2/request-queues/{queueId}Rename a queue
DELETE/v2/request-queues/{queueId}Delete a queue and its requests
GET/v2/request-queues/{queueId}/requestsList requests
POST/v2/request-queues/{queueId}/requestsAdd a single request
POST/v2/request-queues/{queueId}/requests/batchAdd up to 25 requests
DELETE/v2/request-queues/{queueId}/requests/batchDelete up to 25 requests
GET/v2/request-queues/{queueId}/requests/{requestId}Get a request
PUT/v2/request-queues/{queueId}/requests/{requestId}Update a request
DELETE/v2/request-queues/{queueId}/requests/{requestId}Delete a request
GET/v2/request-queues/{queueId}/headPeek at pending requests
POST/v2/request-queues/{queueId}/head/lockFetch and lock pending requests
PUT/v2/request-queues/{queueId}/requests/{requestId}/lockProlong a lock
DELETE/v2/request-queues/{queueId}/requests/{requestId}/lockRelease a lock
POST/v2/request-queues/{queueId}/requests/unlockRelease all locks held by a clientKey

Errors and limits

Errors use the standard envelope { "error": { "type": "...", "message": "..." } }.

Statuserror.typeWhen
400invalid-requestMalformed queue ID, or filter combined with exclusiveStartId.
400invalid-request-idA request ID in the path or body that isn't 1 to 128 letters, digits, -, _, or ~, or a body id on PUT that differs from the path. See Request IDs.
400invalid-inputMissing uniqueKey or url, a batch over 25 items, a missing lockSecs, or an invalid or missing clientKey.
402insufficient-balanceCreating a queue or adding requests with no available credit.
403forbiddenThe queue belongs to another account (some routes return 404 instead), or the lock belongs to another client.
403insufficient-permissionsA limited-permission Actor run token tried to use a queue outside its scope.
404record-not-foundThe queue or request does not exist.
409name-takenRenaming to a name you already use for another queue.
409invalid-stateProlonging a lock on a request that is handled, not locked, or whose lock expired.
429rate-limit-exceededStorage rate limit.

Request queue routes share the storage rate limit with datasets and key-value stores: by default 5,000 requests per minute for your account, plus a per-IP ceiling of 5,000 requests per minute. See Datasets API.

Request IDs

A request ID is 1 to 128 characters of letters, digits, -, _, and ~. Dots, slashes, %, and other characters are refused, so an ID is always a single URL path segment. IDs the API generates from uniqueKey are 15 letters and digits, the same IDs Crawlee and the SDKs compute.

  • Routes with a {requestId} in the path return 400 invalid-request-id for any other ID, before looking anything up.
  • Add request with an id outside these rules returns 400 invalid-request-id. Without an id (or with "" or null), the ID is generated.
  • In the batch routes, an item with an invalid id is returned in unprocessedRequests with an error object; the rest of the batch is processed.

Client keys and lock ownership

clientKey (1 to 32 characters) identifies a worker. When a list or head call comes from a different clientKey than earlier calls, or from none, the queue's hadMultipleClients becomes true.

A lock belongs to the clientKey that took it and, inside an Actor run, to that run as well. Prolonging or releasing a lock needs the same clientKey from the same run; a lock taken inside a run cannot be released with your personal token from outside it.

Get list of request queues

GET/v2/request-queues

Lists queues you own. Unnamed queues are omitted unless you pass unnamed=1.

Query paramTypeDescription
unnamedboolean1 or true includes unnamed queues.
namestringCase-insensitive substring match on the queue name.
sortBystringcreatedAt (default), modifiedAt, or name.
descboolean1 or true reverses the sort order.
offsetnumberNumber of queues to skip (default 0).
limitnumberPage size (default 1000, max 1000).
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  "https://api.scrape.ly/v2/request-queues?unnamed=1&limit=20"

size is a human-readable string. For the size in bytes, use stats.storageBytes from Get request queue.

GET/v2/request-queues?limit=5&unnamed=1

Create request queue

POST/v2/request-queues
Body / query fieldTypeDescription
namestringOptional queue name. If you already own a queue with this name, that queue is returned with 200.
.bash
curl -X POST https://api.scrape.ly/v2/request-queues \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "crawl-queue"}'

A new queue returns 201 with a Location header. The body matches Get request queue.

Get request queue

GET/v2/request-queues/{queueId}
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e
GET/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e

Update request queue

PUT/v2/request-queues/{queueId}

Renames the queue. {queueId} must be a UUID.

Body fieldTypeDescription
namestringNew queue name. Returns 409 name-taken if you already have another queue with this name.
.bash
curl -X PUT https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "renamed-queue"}'

Delete request queue

DELETE/v2/request-queues/{queueId}

Deletes the queue and all its requests. {queueId} must be a UUID. Returns 204 No Content.

.bash
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e

List requests

GET/v2/request-queues/{queueId}/requests

Lists requests in the queue, ordered by request ID (not by the order they were added). Filter with filter; there is no status parameter. Paginate with cursor.

Query paramTypeDescription
limitnumberPage size (default 100, max 10000).
cursorstringOpaque cursor from nextCursor on the previous page.
exclusiveStartIdstringDeprecated: start after this request ID. Cannot be combined with filter.
clientKeystringWorker identity (1 to 32 characters).
filterstring or arraylocked or pending. Pass both as filter=locked&filter=pending or as a JSON array, filter=["locked","pending"].
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests?filter=pending&limit=50&clientKey=worker-1"

nextCursor is present only when there are more requests. The response also echoes cursor and exclusiveStartId when you sent them.

GET/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests?filter=pending&limit=20

Add request

POST/v2/request-queues/{queueId}/requests

Adds a single request. uniqueKey and url are required. The request ID is your id if you send one, otherwise it is derived from uniqueKey, so adding the same uniqueKey twice is deduplicated: the second call returns 200 with wasAlreadyPresent: true and leaves the stored request unchanged.

Query paramTypeDescription
forefrontboolean1 or true puts the request at the front of the pending queue. All forefront requests share the front position and are ordered among themselves by request ID.
Body fieldTypeDescription
uniqueKeystringDeduplication key (required).
urlstringRequest URL (required).
methodstringHTTP method (default GET).
idstringOptional explicit request ID. See Request IDs.
payloadstringOptional request body.
headersobjectOptional headers.
userDataobjectOptional custom data.
noRetrybooleanDisable retries.

Any other fields you send are stored with the request and returned by Get request.

.bash
curl -X POST https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"uniqueKey": "https://example.com", "url": "https://example.com", "method": "GET"}'

A new request returns 201:

.json
{
  "data": {
    "requestId": "Xb3kQ9pLm2RtV7w",
    "wasAlreadyPresent": false,
    "wasAlreadyHandled": false
  }
}

Add requests (batch)

POST/v2/request-queues/{queueId}/requests/batch

The body is a JSON array of up to 25 request objects, not a { "requests": [...] } wrapper. Each item follows Add request. forefront is a query parameter.

Query paramTypeDescription
forefrontboolean1 or true puts new requests at the front of the pending queue.
.bash
curl -X POST "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/batch" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    { "uniqueKey": "https://example.com", "url": "https://example.com" },
    { "uniqueKey": "https://example.org", "url": "https://example.org" }
  ]'

Returns 201. Items that could not be stored are listed in unprocessedRequests with their uniqueKey, url, and method:

.json
{
  "data": {
    "processedRequests": [
      { "requestId": "Xb3kQ9pLm2RtV7w", "uniqueKey": "https://example.com", "wasAlreadyPresent": false, "wasAlreadyHandled": false },
      { "requestId": "Hn4sW8cZq1YuE6d", "uniqueKey": "https://example.org", "wasAlreadyPresent": true, "wasAlreadyHandled": true }
    ],
    "unprocessedRequests": []
  }
}

Delete requests (batch)

DELETE/v2/request-queues/{queueId}/requests/batch

The body is a JSON array of up to 25 items, each with an id or a uniqueKey. Returns 200. Items with neither are returned in unprocessedRequests. Requests that do not exist are skipped and appear in neither list.

curl -X DELETE https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/batch \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{ "uniqueKey": "https://example.com" }, { "id": "Hn4sW8cZq1YuE6d" }]'

Get request

GET/v2/request-queues/{queueId}/requests/{requestId}

Returns the stored request object, including every field you sent when adding or updating it.

curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w
GET/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w

Update request

PUT/v2/request-queues/{queueId}/requests/{requestId}

Merges the body into the stored request. The handled state follows handledAt in the body:

  • A handledAt timestamp marks the request handled and removes it from the pending and locked sets.
  • A body without handledAt (or with handledAt: null) marks it unhandled and puts it back in the pending queue. Always send handledAt when you update a handled request, or it is crawled again.

If the request does not exist, it is added and the response is 200 with wasAlreadyPresent: false. wasAlreadyHandled reports the state before this update.

Query paramTypeDescription
forefrontbooleanWhen the request goes back to pending, put it at the front.
curl -X PUT https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"handledAt": "2026-08-14T15:05:00.000Z"}'

Delete request

DELETE/v2/request-queues/{queueId}/requests/{requestId}

Returns 204 No Content, including when the request does not exist.

.bash
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w

Get head

GET/v2/request-queues/{queueId}/head

Returns pending requests from the front of the queue without locking them. The first limit pending requests are read and those with an active lock are dropped, so the response can hold fewer than limit items even when more unlocked requests exist.

Query paramTypeDescription
limitnumberMaximum requests to return (default 100, max 1000).
clientKeystringWorker identity (1 to 32 characters).
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/head?limit=10&clientKey=worker-1"
GET/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/head?limit=10

Get head and lock

POST/v2/request-queues/{queueId}/head/lock

Fetches pending requests and locks them so other workers cannot take them. Lock options are query parameters, not a JSON body.

Query paramTypeDescription
lockSecsnumberLock duration in seconds (required).
limitnumberMaximum requests to lock (default 25, max 25).
clientKeystringLock owner (1 to 32 characters). A random key is generated if omitted, but then you cannot prolong or release the locks, so always send one.
curl -X POST \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/head/lock?lockSecs=60&limit=10&clientKey=worker-1" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"

queueHasLockedRequests is true when any client, including this call, holds an active lock.

Prolong request lock

PUT/v2/request-queues/{queueId}/requests/{requestId}/lock

Extends a lock you hold. Both query parameters are required.

Query paramTypeDescription
lockSecsnumberNew lock duration in seconds, counted from now (required).
clientKeystringThe clientKey that took the lock (required).

Returns 404 if the request does not exist, 409 invalid-state if it is handled, not locked, or the lock has expired, and 403 forbidden if another client holds the lock.

curl -X PUT \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w/lock?lockSecs=60&clientKey=worker-1" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"

Delete request lock

DELETE/v2/request-queues/{queueId}/requests/{requestId}/lock

Releases a lock you hold, so other workers can take the request again. Returns 204 No Content, including when the lock was already released. Returns 403 forbidden if another client holds the lock.

Query paramTypeDescription
clientKeystringThe clientKey that took the lock (required).
forefrontboolean1 or true puts the request at the front of the pending queue.
.bash
curl -X DELETE \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7w/lock?clientKey=worker-1" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"

Unlock requests

POST/v2/request-queues/{queueId}/requests/unlock

Releases every lock held by a clientKey, for example when a worker shuts down.

Query paramTypeDescription
clientKeystringLock owner to match (required, 1 to 32 characters).
forefrontbooleanPut the unlocked requests at the front of the pending queue.
runIdstringRelease locks taken inside this run. Only honoured for an Actor run token with access to that run; otherwise the run of the calling token is used.
curl -X POST \
  "https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/unlock?clientKey=worker-1" \
  -H "Authorization: Bearer $SCRAPELY_TOKEN"