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
| Method | Path | Description |
|---|---|---|
GET | /v2/request-queues | List queues you own |
POST | /v2/request-queues | Create 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}/requests | List requests |
POST | /v2/request-queues/{queueId}/requests | Add a single request |
POST | /v2/request-queues/{queueId}/requests/batch | Add up to 25 requests |
DELETE | /v2/request-queues/{queueId}/requests/batch | Delete 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}/head | Peek at pending requests |
POST | /v2/request-queues/{queueId}/head/lock | Fetch and lock pending requests |
PUT | /v2/request-queues/{queueId}/requests/{requestId}/lock | Prolong a lock |
DELETE | /v2/request-queues/{queueId}/requests/{requestId}/lock | Release a lock |
POST | /v2/request-queues/{queueId}/requests/unlock | Release all locks held by a clientKey |
Errors and limits
Errors use the standard envelope { "error": { "type": "...", "message": "..." } }.
| Status | error.type | When |
|---|---|---|
400 | invalid-request | Malformed queue ID, or filter combined with exclusiveStartId. |
400 | invalid-request-id | A 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. |
400 | invalid-input | Missing uniqueKey or url, a batch over 25 items, a missing lockSecs, or an invalid or missing clientKey. |
402 | insufficient-balance | Creating a queue or adding requests with no available credit. |
403 | forbidden | The queue belongs to another account (some routes return 404 instead), or the lock belongs to another client. |
403 | insufficient-permissions | A limited-permission Actor run token tried to use a queue outside its scope. |
404 | record-not-found | The queue or request does not exist. |
409 | name-taken | Renaming to a name you already use for another queue. |
409 | invalid-state | Prolonging a lock on a request that is handled, not locked, or whose lock expired. |
429 | rate-limit-exceeded | Storage 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 return400 invalid-request-idfor any other ID, before looking anything up. - Add request with an
idoutside these rules returns400 invalid-request-id. Without anid(or with""ornull), the ID is generated. - In the batch routes, an item with an invalid
idis returned inunprocessedRequestswith anerrorobject; 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
/v2/request-queuesLists queues you own. Unnamed queues are omitted unless you pass unnamed=1.
| Query param | Type | Description |
|---|---|---|
unnamed | boolean | 1 or true includes unnamed queues. |
name | string | Case-insensitive substring match on the queue name. |
sortBy | string | createdAt (default), modifiedAt, or name. |
desc | boolean | 1 or true reverses the sort order. |
offset | number | Number of queues to skip (default 0). |
limit | number | Page 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.
/v2/request-queues?limit=5&unnamed=1Create request queue
/v2/request-queues| Body / query field | Type | Description |
|---|---|---|
name | string | Optional queue name. If you already own a queue with this name, that queue is returned with 200. |
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
/v2/request-queues/{queueId}curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5eUpdate request queue
/v2/request-queues/{queueId}Renames the queue. {queueId} must be a UUID.
| Body field | Type | Description |
|---|---|---|
name | string | New queue name. Returns 409 name-taken if you already have another queue with this name. |
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
/v2/request-queues/{queueId}Deletes the queue and all its requests. {queueId} must be a UUID. Returns 204 No Content.
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5eList requests
/v2/request-queues/{queueId}/requestsLists 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 param | Type | Description |
|---|---|---|
limit | number | Page size (default 100, max 10000). |
cursor | string | Opaque cursor from nextCursor on the previous page. |
exclusiveStartId | string | Deprecated: start after this request ID. Cannot be combined with filter. |
clientKey | string | Worker identity (1 to 32 characters). |
filter | string or array | locked 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.
/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests?filter=pending&limit=20Add request
/v2/request-queues/{queueId}/requestsAdds 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 param | Type | Description |
|---|---|---|
forefront | boolean | 1 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 field | Type | Description |
|---|---|---|
uniqueKey | string | Deduplication key (required). |
url | string | Request URL (required). |
method | string | HTTP method (default GET). |
id | string | Optional explicit request ID. See Request IDs. |
payload | string | Optional request body. |
headers | object | Optional headers. |
userData | object | Optional custom data. |
noRetry | boolean | Disable retries. |
Any other fields you send are stored with the request and returned by Get request.
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:
{
"data": {
"requestId": "Xb3kQ9pLm2RtV7w",
"wasAlreadyPresent": false,
"wasAlreadyHandled": false
}
}Add requests (batch)
/v2/request-queues/{queueId}/requests/batchThe 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 param | Type | Description |
|---|---|---|
forefront | boolean | 1 or true puts new requests at the front of the pending queue. |
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:
{
"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)
/v2/request-queues/{queueId}/requests/batchThe 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
/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/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7wUpdate request
/v2/request-queues/{queueId}/requests/{requestId}Merges the body into the stored request. The handled state follows handledAt in the body:
- A
handledAttimestamp marks the request handled and removes it from the pending and locked sets. - A body without
handledAt(or withhandledAt: null) marks it unhandled and puts it back in the pending queue. Always sendhandledAtwhen 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 param | Type | Description |
|---|---|---|
forefront | boolean | When 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
/v2/request-queues/{queueId}/requests/{requestId}Returns 204 No Content, including when the request does not exist.
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/requests/Xb3kQ9pLm2RtV7wGet head
/v2/request-queues/{queueId}/headReturns 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 param | Type | Description |
|---|---|---|
limit | number | Maximum requests to return (default 100, max 1000). |
clientKey | string | Worker 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"/v2/request-queues/7a9e2c4b-1d3f-4b6a-8c5e-0f1a2b3c4d5e/head?limit=10Get head and lock
/v2/request-queues/{queueId}/head/lockFetches pending requests and locks them so other workers cannot take them. Lock options are query parameters, not a JSON body.
| Query param | Type | Description |
|---|---|---|
lockSecs | number | Lock duration in seconds (required). |
limit | number | Maximum requests to lock (default 25, max 25). |
clientKey | string | Lock 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
/v2/request-queues/{queueId}/requests/{requestId}/lockExtends a lock you hold. Both query parameters are required.
| Query param | Type | Description |
|---|---|---|
lockSecs | number | New lock duration in seconds, counted from now (required). |
clientKey | string | The 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
/v2/request-queues/{queueId}/requests/{requestId}/lockReleases 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 param | Type | Description |
|---|---|---|
clientKey | string | The clientKey that took the lock (required). |
forefront | boolean | 1 or true puts the request at the front of the pending queue. |
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
/v2/request-queues/{queueId}/requests/unlockReleases every lock held by a clientKey, for example when a worker shuts down.
| Query param | Type | Description |
|---|---|---|
clientKey | string | Lock owner to match (required, 1 to 32 characters). |
forefront | boolean | Put the unlocked requests at the front of the pending queue. |
runId | string | Release 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"