Skip to content
Account & billing

API keys & permissions

Create many API keys, limit them with scopes, resources, IPs, and spending caps, and revoke them.

Your account can have many API keys. Give each app, server, CI pipeline, or AI agent its own key, limit what it can do with scopes, and revoke it on its own without touching the others. Manage keys in the dashboard under Settings → API keys, or over the API with the key management endpoints.

Every key is sent the same way, in the Authorization header:

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/users/me

The API does not read keys from a ?token= query parameter. The only places that take a token in the URL are the CI build webhook, which uses its own per-Actor secret, and the hosted MCP server at /mcp, which passes the key through to the API.

What a key has

FieldDescription
NameRequired, up to 100 characters. Shown in the key list and in notifications.
DescriptionOptional, up to 500 characters. Note where the key is used.
CreatedWhen the key was created.
Last usedThe time and IP address of the key's most recent request.
ExpiresNever, or a date. After it, requests with the key get 401 token-expired.
AccessFull access or a list of scopes, plus optional resource restrictions and limits.
Statusactive, expired, or revoked.

The secret is shown once, when you create the key. Scrape.ly stores only a hash of it, so it can't be shown again. Afterwards the dashboard and the API show a masked prefix such as scrapely_sk_…ab12. Copy the key into your secret store right away; if you lose it, regenerate it.

  • Revoke stops a key immediately. A revoked key stays in the list, marked as revoked, and its requests get 401 token-not-valid.
  • Regenerate creates a new key with the same name, description, expiry, and permissions, and revokes the old one at the same time. Nothing is rotated silently: you get the new secret in the response and have to deploy it. A revoked key can't be regenerated; create a new one instead.

The Default key

Every account has a Default key: the account's original API key, in the format scrapely_ followed by 48 hex characters. It is listed first, with the ID default.

  • It has full access and never expires. The dashboard uses it, and it keeps working exactly as before.
  • Only the Default key can create, edit, regenerate, and revoke API keys. In practice that means the dashboard.
  • It can't be edited or revoked. You can rotate it in the dashboard, or with POST /v2/api-keys/reset (see Users API). Rotating it doesn't affect your additional keys.

Additional keys look like scrapely_sk_ + 16 hex characters + _ + 48 hex characters, for example scrapely_sk_3f9a0c1d2e4b5a67_….

Full access or scoped

When you create a key, choose one of two access modes:

ModeWhat the key can do
Full accessEverything the Default key can do, except managing API keys.
ScopedOnly what its scopes cover. Everything else is refused with 403 insufficient-permissions.

Scoped keys are deny-by-default: an endpoint that no scope covers is refused, even when it only reads data.

The dashboard has two presets to start from:

  • Read only: every :read scope.
  • Read & write: every read and write scope.
  • Automations: automations:write, runs:read, datasets:read, and tasks:read (with actors:run), the run permission Actor's own permission level, and no Actor limit. Enough for a service or agent that creates and runs automations.

You can then add or remove single scopes.

Scopes

A write scope includes its read scope. actors:manage includes actors:build, and actors:run and actors:build include actors:read.

ScopeAllows
actors:readRead Actors, versions, builds, source files, and OpenAPI documents.
actors:runStart runs of Actors and tasks, including synchronous runs (run-sync, run-sync-get-dataset-items) and Standby URLs.
actors:buildStart and abort builds.
actors:manageCreate, update, and delete Actors and versions, environment variables, deployment keys, and the CI webhook. Sensitive: a key with this scope can change the code your other keys run.
runs:readList and get runs, logs, run input, and a run's default dataset, key-value store, and request queue through /v2/actor-runs/{runId}/dataset, /key-value-store, and /request-queue. Also the last-run endpoints (…/runs/last).
runs:writeAbort, reboot, update, and delete runs, and write to a run's default storages. Resurrect and metamorph also need actors:run and the run permission actor-default; with an Actor restriction, the metamorph target must be one of the listed Actors.
tasks:readList and get tasks and their input. Tasks are the dashboard's saved inputs (the key form calls this area Saved inputs (tasks)), so this also covers GET /v2/actors/{actorId}/saved-inputs.
tasks:writeCreate, update, and delete tasks (saved inputs).
datasets:readList datasets, get them, and read their items.
datasets:writeCreate, update, and delete datasets, and push items.
keyValueStores:readList key-value stores, get them, list keys, and read records.
keyValueStores:writeCreate, update, and delete key-value stores and records.
requestQueues:readList request queues, get them, and read requests and the queue head.
requestQueues:writeCreate, update, and delete request queues, add and update requests, and lock them.
schedules:readList and get schedules and their logs.
schedules:writeCreate, update, and delete schedules. Also needs actors:run and the run permission actor-default, because scheduled runs start later with your account's own permissions, not the key's. Refused, except for deletes, on keys with a spending cap.
automations:readList and get automations, their run history, and single runs.
automations:writeCreate, update, delete, duplicate, and run automations, AI step included. Also needs actors:run and the run permission actor-default, and is refused for keys limited to specific Actors, because automation runs start with your account's own permissions, not the key's. Refused, except for deletes, on keys with a spending cap: those runs and the AI step, which is billed to your account like the AI chat, would run later without the key's caps.
webhooks:readList and get webhooks, webhook dispatches, and the integrations list.
webhooks:writeCreate, update, delete, and test webhooks. Also needs actors:run, runs:read, and the run permission actor-default, because a webhook's integration token can start and read runs. Refused for keys with an Actor or storage restriction or a spending cap.
billing:readUsage, limits, plan, and stats: GET /v2/users/me/usage/*, /v2/users/me/limits, /v2/users/me/plan, and /v2/users/me/stats.

Endpoints outside the scopes

Who can call itEndpoints
Any valid keyGET /v2/users/me (your identity; this is what scrapely login checks) and public Store and catalog reads.
Full-access keys and the Default keyProfile, notification settings, dashboard notifications, messages, monetization and payouts, earnings, approving full-permission Actors, bookmarks, writing issues and reviews, and connecting Slack or GitHub.
Default key only/v2/api-keys*, GET /v2/scrapely, the AI chat, and the dashboard live feed.
Run tokens onlyThe LLM gateway and the solver. No API key works there.

Example requests by scope

The examples use $SCRAPELY_TOKEN for the key. Replace the IDs with your own.

actors:read: get an Actor.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/actors/{actorId}

actors:run: start a run. The body is the Actor input.

.bash
curl -X POST https://api.scrape.ly/v2/actors/{actorId}/runs \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

actors:build: build version 0.1.

.bash
curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  "https://api.scrape.ly/v2/actors/{actorId}/builds?version=0.1&tag=latest"

actors:manage: update an Actor.

.bash
curl -X PUT https://api.scrape.ly/v2/actors/{actorId} \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "My scraper"}'

runs:read: read a run's log, and the items of its default dataset.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/actor-runs/{runId}/log

curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/actor-runs/{runId}/dataset/items

runs:write: abort a run.

.bash
curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/actor-runs/{runId}/abort

tasks:read: list your tasks.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/actor-tasks

tasks:write: create a task.

.bash
curl -X POST https://api.scrape.ly/v2/actor-tasks \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"actId": "{actorId}", "name": "daily-shop", "input": {"url": "https://example.com"}}'

datasets:read: read dataset items.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/datasets/{datasetId}/items

datasets:write: push items.

.bash
curl -X POST https://api.scrape.ly/v2/datasets/{datasetId}/items \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"title": "First item"}]'

keyValueStores:read: read a record.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/key-value-stores/{storeId}/records/OUTPUT

keyValueStores:write: write a record.

.bash
curl -X PUT https://api.scrape.ly/v2/key-value-stores/{storeId}/records/CONFIG \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"maxPages": 50}'

requestQueues:read: read the queue head.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/request-queues/{queueId}/head

requestQueues:write: add a request.

.bash
curl -X POST https://api.scrape.ly/v2/request-queues/{queueId}/requests \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/page-2", "uniqueKey": "https://example.com/page-2"}'

schedules:read: list schedules.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/schedules

schedules:write (with actors:run and run permission actor-default): create a schedule.

.bash
curl -X POST https://api.scrape.ly/v2/schedules \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "daily-scrape",
    "cronExpression": "0 6 * * *",
    "actions": [{ "type": "RUN_ACTOR_TASK", "actorTaskId": "{taskId}" }]
  }'

automations:read: list automations.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/automations

automations:write (with actors:run and run permission actor-default): run an automation now.

.bash
curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/automations/{automationId}/runs

webhooks:read: list webhooks.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/webhooks

webhooks:write (with actors:run and runs:read): create a webhook.

.bash
curl -X POST https://api.scrape.ly/v2/webhooks \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "eventTypes": ["ACTOR.RUN.SUCCEEDED"],
    "requestUrl": "https://example.com/hooks/scrapely",
    "condition": { "actorId": "{actorId}" }
  }'

billing:read: this month's usage.

.bash
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  https://api.scrape.ly/v2/users/me/usage/monthly

A refused request

A key with only datasets:read that tries to push items:

curl -X POST https://api.scrape.ly/v2/datasets/{datasetId}/items \
  -H "Authorization: Bearer $SCRAPELY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"title": "First item"}]'

The status is 403. Nothing is written.

Resource restrictions

A scoped key can also be limited to specific resources.

Specific Actors

With a list of Actors, the key can only read, run, and build those Actors, and see their runs, builds, logs, and tasks.

  • Account-wide lists are refused, for example GET /v2/actors and GET /v2/actor-runs. Use the per-Actor endpoints instead, such as GET /v2/actors/{actorId}/runs.
  • The key can't create Actors, schedules, or webhooks.

You can add your own Actors and public Store Actors, by ID or by username/actor-name.

Specific storages

With a list of storage IDs (datasets, key-value stores, and request queues), the key can only use those storages.

  • Listing storages and creating new ones are refused.
  • A run's default storages are reachable through /v2/actor-runs/{runId}/dataset, /v2/actor-runs/{runId}/key-value-store, and /v2/actor-runs/{runId}/request-queue when they are on the list, or when the key is also limited to specific Actors and may read that run.

Run permission

This setting decides how much access a run gets when a scoped key starts it.

ValueWhat the run gets
limited (default, recommended)The run is forced to LIMITED_PERMISSIONS. Its run token can only use the run's own default storages, storages passed in its input, storages it creates, and the child runs it starts. The key can't approve full-permission Actors.
actor-defaultThe run gets the permission level the Actor declares, which may be FULL_PERMISSIONS. Use it for a service that should only start one trusted Actor, ideally together with an Actor restriction.

With limited, Standby URLs are refused (a Standby run uses the Actor's own permissions, as on Apify), and so are resurrect and metamorph.

Attaching ad-hoc webhooks to a run start (the webhooks body field or query parameter) needs the same scopes as creating a webhook: webhooks:write and runs:read. Keys limited to specific Actors can't attach them.

Full-access keys behave like the Default key: runs get the Actor's declared permission level. See Security for what each level allows.

Optional limits

None of these are set by default.

LimitEffect
IP allowlistIP addresses or CIDR ranges, IPv4 or IPv6, for example 203.0.113.7 or 2001:db8::/32. Requests from any other address get 403 api-key-ip-not-allowed.
Max charge per runThe default and the ceiling of maxTotalChargeUsd for every run the key starts. A run asking for more is capped.
Max charge per dayThe total the key's runs may charge per UTC day. Each new run is capped to what's left of the day; a run that is still going holds its whole cap until it finishes and is settled, so parallel starts share one budget. Once it's used up, run starts return 402 api-key-daily-cap-reached until the next UTC day.
Rate limit per minuteRequests per minute for this key. Over it, the API returns 429 rate-limit-exceeded with Retry-After. Your account rate limits still apply.

Errors

StatusTypeMeaning
401token-not-validThe key is unknown or revoked.
401token-expiredThe key's expiry date has passed.
403insufficient-permissionsThe key's scopes or resource restrictions don't cover this request.
403api-key-ip-not-allowedThe request came from an address outside the key's IP allowlist.
402api-key-daily-cap-reachedThe key's daily charge limit is used up, so the run can't start.
429rate-limit-exceededThe key's or the account's rate limit was hit. Wait for Retry-After seconds.

Manage keys over the API

These endpoints accept only the Default key. Any other key, a run token, or an OAuth token gets 403 insufficient-permissions. They are limited to 10 requests per minute per IP address.

GET/v2/api-keys
POST/v2/api-keys
GET/v2/api-keys/scopes
GET/v2/api-keys/{keyId}
PUT/v2/api-keys/{keyId}
PATCH/v2/api-keys/{keyId}
DELETE/v2/api-keys/{keyId}
POST/v2/api-keys/{keyId}/regenerate
EndpointDescription
GET /v2/api-keysLists every key, the Default key first (ID default), with masked prefixes. Revoked keys are included.
POST /v2/api-keysCreates a key. Returns 201; the response includes key once.
GET /v2/api-keys/scopesThe scope catalog: scopes, the read-only preset (readOnly), what each write scope implies, and the requirements of automations:write, schedules:write, and webhooks:write (alsoRequires, runPermission, allActors). The dashboard uses it to warn about a key that would be refused.
GET /v2/api-keys/{keyId}One key.
PUT / PATCH /v2/api-keys/{keyId}Changes a key's name, description, expiry, or permissions. The secret stays the same. The Default key can't be edited.
DELETE /v2/api-keys/{keyId}Revokes a key immediately. Returns 204. The Default key can't be revoked.
POST /v2/api-keys/{keyId}/regenerateCreates a new key with the same settings and revokes the old one. Returns 201; the response includes key once and replaces with the old key's ID.

Request body fields for create and update:

FieldTypeDescription
namestringRequired on create. Up to 100 characters.
descriptionstring or nullUp to 500 characters.
expiresAtstring or nullISO 8601 date and time, or null for no expiry.
fullAccessbooleantrue for full access. Scopes and restrictions are then ignored.
scopesstring[]Scopes for a scoped key. Implied scopes are added for you.
actorIdsstring[]Limit the key to these Actors (IDs or username/actor-name).
storageIdsstring[]Limit the key to these dataset, key-value store, and request queue IDs.
ipAllowliststring[]IP addresses or CIDR ranges.
runPermissionstringlimited (default) or actor-default.
maxChargePerRunUsdnumber or nullDefault and ceiling of maxTotalChargeUsd for runs the key starts.
maxChargePerDayUsdnumber or nullTotal charges of the key's runs per UTC day.
rateLimitPerMinnumber or nullRequests per minute for this key.

Create a key for a CI pipeline that deploys one Actor:

curl -X POST https://api.scrape.ly/v2/api-keys \
  -H "Authorization: Bearer $SCRAPELY_DEFAULT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub Actions deploy",
    "description": "Builds janedoe/my-actor on push",
    "expiresAt": "2027-06-30T00:00:00.000Z",
    "fullAccess": false,
    "scopes": ["actors:build", "actors:read"],
    "actorIds": ["janedoe/my-actor"]
  }'

Revoke it:

.bash
curl -X DELETE -H "Authorization: Bearer $SCRAPELY_DEFAULT_KEY" \
  https://api.scrape.ly/v2/api-keys/3f9a0c1d2e4b5a67

CLI, SDKs, and MCP

CLI. scrapely login works with any key: the Default key, a full-access key, or a scoped key. It only checks the key with GET /v2/users/me. Each command then succeeds or fails according to the key's scopes. See CLI authentication.

.bash
scrapely login --token "$SCRAPELY_TOKEN"

API clients and SDKs. The JavaScript and Python clients send the key as a Bearer token, so a scoped key works there the same way.

MCP. The hosted MCP server acts with the key you give it. A scoped key limits what an AI agent can do: a read-only key can't start paid runs.

Use a separate, scoped key for everything that isn't you in the dashboard:

UseScopes
CI that deploys an Actor (scrapely push)actors:build + actors:read, restricted to that Actor
A backend that triggers runs and reads resultsactors:run + runs:read, restricted to the Actors it uses
A dashboard or BI tool that reads resultsdatasets:read (or the Read only preset), restricted to the storages it needs
An MCP client or AI agentRead only, or actors:run + runs:read with a daily charge limit
A service or agent that sets up automationsThe Automations preset, without spending caps (a capped key can't create, change, or run automations). Cap each step with Maximum cost per run instead
A third-party integrationOnly the scopes it asks for, with an expiry date

Give each key a name you'll recognize, set an expiry where you can, and revoke keys you no longer use. Use Last used to find keys that have gone quiet.

What's next