Users
Current user, profile, limits, plan and balance, usage, settings, and API key.
Account endpoints: the current user, profile, limits, plan and credit balance, usage, notification preferences, account settings, and the API key. All paths are relative to https://api.scrape.ly.
Every /v2/users/me/* route, GET /v2/users/{userId}, and the API key routes require your user API key. Run tokens, CI build tokens, and x402 payments are rejected with 403 insufficient-permissions. The API key routes (/v2/api-keys, /v2/scrapely) also reject OAuth access tokens (for example from the hosted MCP server) with 403 insufficient-permissions. Additional API keys can call these routes only as their scopes allow: billing:read covers usage, limits, plan, and stats; GET /v2/users/me works with any key; the API key routes accept only the Default key.
Money fields ending in Usd are US dollars. Fields ending in Nd are nanodollars ($1 = 1_000_000_000). The same holds for run objects: usageUsd and usageTotalUsd are dollars, and usageTotalNd is nanodollars.
Get current user
/v2/users/meReturns the authenticated user: profile, plan, and account fields. The plan object keeps the shape existing API clients expect.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/users/me| Field | Notes |
|---|---|
isPaying | true while your paid plan's renewal date is in the future. |
storeDiscount | Percent discount on Store Actor events that allow discounts. |
plan.planPricing.chargeableServiceUnitPricesUsd | Your per-unit platform prices (compute units, proxy, data transfer, storage operations). |
plan.planPricing.llmGatewayMarkup | Your plan's LLM gateway multiplier: each request costs OpenRouter's price times this. |
plan.enabledPlatformFeatures | SCHEDULER, WEBHOOKS, and ACTORS_PUBLIC_ALL appear only on paid plans. |
The example is trimmed. The full plan object also includes description, usageDiscountPercent, and add-on price fields kept for client compatibility.
/v2/users/meGet a user by ID
/v2/users/{userId}Pass your own ID or me to get the same object as GET /v2/users/me. For any other user ID, you get public fields only:
{
"data": {
"id": "aNoThErUsErId456",
"username": "acme",
"name": "Acme Data",
"imageUrl": "https://...",
"createdAt": "2026-02-01T09:00:00.000Z"
}
}Unknown IDs return 404 user-not-found.
Profile
/v2/users/me/profile/v2/users/me/profileGet or update your profile. PUT is partial: only fields you send change. Send null to clear a text field.
| Body field | Type | Notes |
|---|---|---|
scrapelyUsername | string | Public username, max 20 characters. Used in Actor paths (username/actor-name). See Usernames. |
bio | string | Max 200 characters. |
readme | string | Max 2000 characters. |
websiteUrl / linkedinUrl | string | Must start with http:// or https://. Max 500 characters. |
githubUsername / twitterUsername / discordUserId | string | Optional social handles. Max 100 characters. |
isPublic | boolean | Public profiles can be fetched without auth. Cannot be set to false while you have published Actors. |
showEmail | boolean | Whether the public profile includes your email. |
curl -X PUT https://api.scrape.ly/v2/users/me/profile \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "bio": "Builds scrapers", "isPublic": true }'These are the only fields PUT accepts. Any other field, such as email, firstName, or lastName, returns 400 invalid-input with a message that names the field and lists the editable ones. To change your picture, use Profile picture.
GET returns the profile fields plus imageUrl, createdAt, Actor stats (totalUsers, monthlyUsers, publicActors, totalRuns, successRate, avgRating, totalReviews), and up to 100 of your public Actors. PUT returns the updated profile fields.
| Error | When |
|---|---|
400 invalid-input | A field that isn't editable, a value of the wrong type, a username with characters that aren't allowed, an empty username, or a username starting with x402_. |
409 | A text field too long, invalid URL, username already taken, or unpublishing a profile that has published Actors. |
400 profanity-detected | scrapelyUsername, bio, or readme contains blocked words. |
Usernames
Your public username is the owner part of your Actor paths (username/actor-name and username~actor-name):
- It can have up to 20 ASCII letters, digits,
.,_, and-, must start with a letter or digit, and can't contain... Names starting withx402_are reserved. - Usernames aren't case-sensitive. They are stored in lowercase, and a case variant of a taken name is taken too.
- You can change your username, but not remove it.
- Changing it moves all your Actors to the new name. Paths with your old username keep working and still lead to your Actors.
- A username stays with the account that first used it. Once you change it, nobody else can take the old one, and neither can anyone take the username of a deleted account. You can switch back to a username you used before.
Profile picture
/v2/users/me/profile/picture/v2/users/me/profile/pictureUpload a picture as JSON or remove the current one. imageData is a base64 data URL. It is resized to 512×512 and stored as WebP.
curl -X PUT https://api.scrape.ly/v2/users/me/profile/picture \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "imageData": "data:image/png;base64,..." }'PUT returns { "data": { "imageUrl": "data:image/webp;base64,..." } }. DELETE returns { "data": { "success": true, "message": "Profile picture removed" } }.
Errors: 400 missing-image when imageData is absent, 400 image-too-large when the image is over about 1 MB.
Public profile
/v2/users/{username}/public-profileReturns a user's public profile: name, avatar, bio, links, stats (monthlyUsers across their public Actors, publicActors, successRate, avgRating, totalReviews), and public Actors, each with stats.totalUsers30Days. successRate is the average of the public Actors' success rates, each over its current build or its last 30 days, leaving out Actors without enough runs in either; null when none has enough. No run or total-user counts are public. No authentication required. Private profiles return 404 user-not-found unless you are the owner. email is included only when the user enabled showEmail.
curl https://api.scrape.ly/v2/users/janedoe/public-profile/v2/users/janedoe/public-profileStats
/v2/users/me/statsDashboard statistics for the last 30 days, with percentage deltas versus the previous 30 days. Cached for about 45 seconds.
{
"data": {
"period": "last 30 days",
"totalRuns": { "value": 42, "delta": 10, "period": "last 30 days" },
"successRate": { "value": 95.2, "delta": 1.4, "failedRuns": 2 },
"resultsScraped": { "value": 12000, "delta": 8 },
"computeUsed": { "value": 3.14, "delta": -2 }
}
}/v2/users/me/statsLimits
/v2/users/me/limitsYour plan limits, with current usage where it applies.
{
"data": {
"dataRetentionDays": 31,
"userId": "uSeRiDeXaMpLe123",
"limits": {
"maxConcurrency": { "used": 1, "limit": 8 },
"maxConcurrentBuilds": { "used": 0, "limit": 10 },
"maxTotalMemoryGb": { "used": 2.0, "limit": 32 },
"maxActorCount": { "used": 3, "limit": 20 },
"maxActorTaskCount": { "used": 4, "limit": 50 },
"maxScheduleCount": { "used": 1, "limit": 10 },
"maxWebhookCount": { "used": 2, "limit": 10 },
"minRunMemoryMbytes": 128,
"maxRunMemoryMbytes": 32768,
"maxBuildMemoryMbytes": 8192,
"maxBuildTimeoutSecs": 1800,
"maxApiRateLimit": 1000,
"maxStorageRateLimit": 5000,
"maxLlmRateLimit": 600,
"maxLlmConcurrency": 20,
"llmFreeDailyCapUsd": { "used": 0.12, "limit": 2.5 }
},
"freeTier": {
"active": true,
"maxConcurrency": 2,
"maxRunMemoryMbytes": 4096,
"maxScheduleCount": 5,
"chatMessagesPerDay": { "used": 3, "limit": 30 },
"monthlyFreeCreditUsd": 5
}
}
}| Field | Meaning |
|---|---|
maxConcurrency | Runs in progress at the same time. |
maxConcurrentBuilds | Builds in progress at the same time. |
maxTotalMemoryGb | Memory used by all running runs and builds combined. |
maxApiRateLimit | API requests per minute for your account. |
maxStorageRateLimit | Storage API requests per minute for your account. |
maxLlmRateLimit | LLM gateway requests per minute for your account: your own limit, or the platform default. |
maxLlmConcurrency | LLM gateway requests your account can have running at the same time: your own limit, or the platform default. |
freeTier | The free plan caps. While active is true, they are already applied to the limits above. chatMessagesPerDay is today's (UTC) AI chat usage, or null when there is no daily cap. monthlyFreeCreditUsd is the free credit added each month, or null on a paid plan. |
llmFreeDailyCapUsd | Free-plan daily LLM limit in USD: used is today's (UTC) settled LLM charges, limit is the daily cap. Requests still in flight aren't in used yet. null on a paid plan, which has no daily limit. |
/v2/users/me/limitsPlan
/v2/users/me/planYour current plan, this month's usage, and your credit balance.
{
"data": {
"plan": {
"id": "starter",
"name": "Starter",
"tier": "starter",
"renewalDate": "2026-09-01T00:00:00.000Z"
},
"limits": {
"maxConcurrency": 8,
"maxConcurrentBuilds": 10,
"maxTotalMemoryGb": 32
},
"usage": {
"currentMonth": "2026-08",
"usage": { "computeUnits": 41.2, "datasetWrites": 12000 },
"usageUsd": {
"computeUnits": 12.36,
"datasetReads": 0.01,
"datasetWrites": 0.06,
"kvsReads": 0,
"kvsWrites": 0,
"rqReads": 0,
"rqWrites": 0,
"externalTransfer": 0,
"internalTransfer": 0,
"actorFee": 0,
"browser": 0,
"proxyResidential": 0,
"proxyDatacenter": 0,
"llm": 0.05
},
"usageTotalNd": 12430000000,
"usageTotalUsd": 12.43
},
"billing": {
"balance": 36.57,
"usageTotalUsd": 12.43,
"breakdown": {
"subscription": 0,
"subscription_paid": 36.57,
"payg": 0,
"dev_earnings": 0
},
"totalPaygPurchased": 0,
"packageCapacity": 49,
"packageConsumed": 12.43,
"packageRemaining": 36.57
}
}
}| Field | Meaning |
|---|---|
plan.renewalDate | When the paid plan renews or expires. null on the free plan. |
usage.usageUsd.browser / proxyResidential / proxyDatacenter / llm | This month's browser, residential proxy, datacenter proxy, and LLM gateway charges. All are included in usageTotalNd and both usageTotalUsd fields. |
billing.balance | Spendable credits in USD: subscription + subscription_paid + payg + promo. Expired credit is not included. |
billing.breakdown.promo | Free credit: the free plan's monthly $5 and any promotional credit. Spent before any other credit. |
billing.promo | Free credit details: remaining, granted and consumed (USD) of the unexpired free credit, and expiresAt, when the current free credit resets. |
billing.hasPaidPlan | false while you're on the free plan and its limits apply. |
freeTier.active | Same as !billing.hasPaidPlan. |
billing.breakdown.subscription | Older free-plan credits. |
billing.breakdown.subscription_paid | Credits included with your paid plan. |
billing.breakdown.payg | Pay-as-you-go credits you bought. |
billing.breakdown.dev_earnings | Earnings from your paid Actors. Not spent on runs. |
billing.packageCapacity / packageConsumed / packageRemaining | Totals across your active credit packages. |
billing.credit | The Credit left summary of the billing page, in USD: periodStart, periodEnd, totalUsd, usedUsd, usedThisPeriodUsd, usedBeforePeriodUsd, paidFromEarlierCreditUsd, reservedUsd (held for running work), availableUsd, balanceUsd, and sources. |
billing.credit.sources | One entry per kind of unexpired credit you have: plan (plan credit), additional (additional credit), free (free monthly credit), or prepaid (prepaid credit), each with amountUsd, remainingUsd, and expiresAt (null for prepaid credit, which doesn't expire). |
/v2/users/me/planUsage
/v2/users/me/usage/daily/v2/users/me/usage/monthly/v2/users/me/usage/actors/v2/users/me/usage/pay-per-event| Endpoint | Query | Description |
|---|---|---|
/usage/daily | date=YYYY-MM-DD | Usage quantities for one day. Defaults to today. |
/usage/monthly | date=YYYY-MM | Service usage with prices, pay-per-event items, and a daily breakdown. Defaults to the current month. |
/usage/actors | date=YYYY-MM, limit, offset | Per-Actor platform and developer charges for a month. |
/usage/pay-per-event | date=YYYY-MM, limit (1-100, default 10), offset, search, actorId, sort (amount, quantity, name) | Paginated pay-per-event charges for a month. |
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
"https://api.scrape.ly/v2/users/me/usage/monthly?date=2026-08"/usage/monthly returns:
usageCyclewithstartAtandendAt.monthlyServiceUsage: one entry per service, each withquantity,baseAmountUsd,baseUnitPriceUsd,amountAfterVolumeDiscountUsd, andpriceTiers. Services includeACTOR_COMPUTE_UNITS,BROWSER_CREDITS,PROXY_RESIDENTIAL_TRANSFER_GBYTES,PROXY_DATACENTER_TRANSFER_GBYTES,LLM_TOKENS(LLM gateway input plus output tokens; prices differ by model, sobaseUnitPriceUsdis the average price per token across the models used),ACTOR_FEE, dataset, key-value store, and request queue reads/writes,DATA_TRANSFER_EXTERNAL_GBYTES,DATA_TRANSFER_INTERNAL_GBYTES, and timed-storage GB-hours.BROWSER_SECONDSandBROWSER_PROXY_TRANSFER_GBYTESare informational only.payPerEventItemsfor pay-per-event Actor charges.dailyServiceUsages:{ date, serviceUsage, totalUsageCreditsUsd }per day.totalUsageCreditsUsdBeforeVolumeDiscount,totalUsageCreditsUsdAfterVolumeDiscount, andusageTotalUsd.
/usage/daily returns { "data": { "date": "2026-08-14", "usage": { ... } } }, with counts such as ACTOR_COMPUTE_UNITS, DATASET_WRITES, and DATA_TRANSFER_EXTERNAL_GB.
/usage/actors returns { "data": { "month", "total", "count", "limit", "offset", "actors": [...] } }.
/usage/pay-per-event returns { "data": { "month", "total", "count", "limit", "offset", "totalUsd", "items": [...], "actors": [...] } }. Each item has the same shape as an entry in payPerEventItems. total counts the items that match the filters, and totalUsd is the whole month's pay-per-event total. actors lists { actorId, actorDisplayPath } for every Actor with charges that month, whatever the filters.
/v2/users/me/usage/monthly/v2/users/me/usage/dailyNotification settings
/v2/users/me/notification-settings/v2/users/me/notification-settingsGet or update notification preferences. The response is a flat object, with no data wrapper and no nested email object. PUT is partial and returns the full settings object.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/users/me/notification-settingsThe example shows the defaults for a new account.
curl -X PUT https://api.scrape.ly/v2/users/me/notification-settings \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "lowBalanceThresholdNd": 10000000000, "lowBalanceEmail": true }'Boolean preference fields: lowBalanceEmail, lowBalanceWeb, lowBalanceWebhook, storageExpirationEmail, storageExpirationWeb, scheduleFailureEmail, scheduleFailureWeb, issueCommentConsole, issueCommentEmail, messageConsole, messageEmail, actorAutoTestEmail, actorAutoTestWeb, actorMaintenanceEmail, actorMaintenanceWeb, actorPricingEmail, actorPricingWeb, planExpirationEmail, planExpirationWeb, creditExpirationEmail, creditExpirationWeb, apiKeyCreatedEmail, apiKeyCreatedWeb.
Delivery fields: lowBalanceWebhookUrl, lowBalanceThresholdNd, consoleNotificationsEnabled, emailNotificationsEnabled, notificationEmail.
notificationEmail is where emails go: a custom address you verified, otherwise your account email (notificationEmailSource is custom or account). PUT accepts it only as null or your account email (both switch back to the account email), or the current address. A new address returns 409 email_verification_required and has to be verified with a code first; see Notification email.
Errors use a plain string body, for example { "error": "Invalid low balance webhook URL format" }:
| Status | When |
|---|---|
400 | lowBalanceWebhookUrl is not a string or null, is not a valid URL, or points to a private or local address (Webhook URL not allowed: ...). |
500 | Settings could not be read or saved. |
See Notifications for which settings take effect and how delivery works.
/v2/users/me/notification-settingsPermission approval skip
/v2/users/me/permission-approval-skip/v2/users/me/permission-approval-skipWhen enabled, Actors that request full permissions run without a per-Actor approval. In the dashboard this is Settings → Preferences → Skip permission approvals.
PUT body: { "skip": true }. A non-boolean skip returns 400 invalid-input. Both methods return { "data": { "skipActorPermissionApprovals": true } }.
curl -X PUT https://api.scrape.ly/v2/users/me/permission-approval-skip \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "skip": true }'/v2/users/me/permission-approval-skipGitHub imports
/v2/users/me/transform-github-actors/v2/users/me/transform-github-actorsWhen you import an Actor from GitHub or a ZIP, Scrape.ly can adapt existing Actor source (SDK imports, base images) so the project runs on Scrape.ly. This setting turns that adaptation on or off. In the dashboard this is Settings → Preferences → Transform actors.
PUT body: { "transformGithubActors": true }. A non-boolean value returns 400 invalid-input. Both methods return { "data": { "transformGithubActors": true } }.
curl -X PUT https://api.scrape.ly/v2/users/me/transform-github-actors \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "transformGithubActors": true }'/v2/users/me/transform-github-actorsAPI keys
Each account has a Default key, with full access to the account. It looks like scrapely_ followed by 48 hex characters. You can also create additional keys with scopes, expiry, resource restrictions, IP allowlists, and spending caps. Creating, editing, regenerating, and revoking them is covered in API keys & permissions.
/v2/api-keys/v2/api-keys/resetGET lists every key with a masked prefix, never the full key. The Default key comes first, with the ID default. The full response shape is in API keys & permissions.
POST /v2/api-keys/reset rotates the Default key only. Your additional keys keep working.
POST /v2/api-keys/reset replaces the key immediately:
- The previous key stops working.
- All OAuth access tokens issued for your account (for example to MCP clients) are revoked.
- An "API key created" notification is sent (see Notifications).
The response is the only time the new key is returned through this endpoint:
{
"data": {
"apiKey": "scrapely_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}curl -X POST -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/api-keys/resetThese endpoints are also limited to 10 requests per minute per IP address.
/v2/api-keysCLI helper
/v2/scrapelyReturns your user ID and full API key. Used by the CLI and the dashboard. There is no data wrapper.
{
"userId": "uSeRiDeXaMpLe123",
"apiKey": "scrapely_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}Returns 404 with { "error": "Scrapely user could not be found..." } if the account has no key.
curl -H "Authorization: Bearer $SCRAPELY_TOKEN" \
https://api.scrape.ly/v2/scrapelyPlan tiers
/v2/plan-tiersLists active plan tiers with their limits. Public, no authentication required. Responses are cacheable for 5 minutes.
curl https://api.scrape.ly/v2/plan-tiersThe values above are examples. Read the live tiers from this endpoint.
/v2/plan-tiers