Rate limits
Understand Starkscan route-class budgets, headers, and the correct retry behavior for external API keys.
Rate limits
Starkscan rate-limits external API keys by route class.
Treat rate limits as part of the HTTP contract, not as a hidden operational detail.
Route classes
Current hosted external API traffic exposes three response route classes.
light and heavy are the ordinary REST budget buckets. batch identifies
JSON-RPC and Query Plane routes, which own their method or cost-unit budget in
addition to that response class. Clients must back off on the exact class and
headers returned by the response rather than assume one shared numeric limit.
| Class | Typical use | Example routes |
|---|---|---|
light | cheap host/status, simple lookup reads, transfer-row traversal, and balance-of for hosted keys | GET /v1/{chain}/status, GET /v1/{chain}/block/{block_ref}, GET /v1/{chain}/tx/{tx_hash}, GET /v1/{chain}/address/{address}/transfers, GET /v1/{chain}/token/{token}/balance-of/{address} |
heavy | indexed lists, wallet/profile reads, token total-supply, traces, search, contract reads, and batch helpers | GET /v1/{chain}/token/{token}/total-supply, GET /v1/{chain}/address/{address}/transactions, GET /v1/{chain}/tx/{tx_hash}/trace, GET /v1/{chain}/search, GET /v1/{chain}/contract/{address}/read, POST /v1/{chain}/tx/previews, POST /v1/{chain}/address/summaries, POST /v1/{chain}/address/intelligence |
batch | JSON-RPC and Partner Query Plane requests; use the additional RPC-method or Query Plane cost headers | POST /api/v1/{chain}/rpc with X-Starkscan-Api-Key, POST /v1/{chain}/query/wallet-paymaster-view, POST /v1/{chain}/query/defi-lending-market-view, POST /v1/{chain}/query/support-proof-bundle |
The exact numeric budget is deployment-controlled. Read the headers instead of hard-coding assumptions into clients: the response's X-Starkscan-Route-Class is authoritative for REST.
Access tier and rate budget are separate. Address batch helpers require a key
with utility/batch access, preserve request order, and then debit the hosted
heavy rate bucket for the returned headers (x-ratelimit-policy: heavy;w=60).
Named wallet, paymaster, and migration partner policies can receive higher
per-minute heavy limits than ordinary expanded keys; clients should still use
the response headers as the source of truth.
For batch address classification specifically:
POST /v1/{chain}/address/summariesandPOST /v1/{chain}/address/intelligencerequire utility/batch access.- Keep batches at or below 128 addresses.
- Treat
403as access-tier evidence, not a malformed route. - Treat
429as rate pressure and honorRetry-After. - Treat
503as bounded-query timeout backpressure and honorRetry-Afterwhen present. Current address batch timeout responses useRetry-After: 2.
For authenticated JSON-RPC beta traffic, Starkscan can emit finer rpc_* method
classes such as rpc_read_light, rpc_read_state, rpc_read_history,
rpc_simulation, and rpc_write in X-Starkscan-Rpc-Class
(x-starkscan-rpc-class). RPC requests keep X-Starkscan-Route-Class as
batch, so back off by the RPC class header when present and by the route class
header for normal REST traffic.
JSON-RPC batching is a request-shape limit, not a quota discount. An empty
array fails before dispatch with empty_batch; arrays of 1 through 50 accepted
items execute their normal per-method class and consume that many child units.
Arrays of 51 or more fail before child quota or upstream dispatch with
batch_too_large (maxItems=50), including 51, 100, and 200-item requests.
The aggregate serialized request/response cap is 16 MiB. Accepted RPC responses
keep X-Starkscan-Route-Class: batch; X-Starkscan-Rpc-Class describes an
accepted child method when present.
The STRK20 prover relay is the one surface that is
not rate-limited per minute. It emits X-Starkscan-Rpc-Class: rpc_prove and is
budgeted by concurrency and per-UTC-day volume instead, because a single proof
can occupy the prover for minutes and a per-minute bucket cannot describe that.
Back off on prover_daily_budget_exhausted, prover_key_concurrency,
prover_queue_full, and prover_unavailable using Retry-After; a daily-budget
Retry-After points at the next UTC midnight.
Partner Query Plane bundles also keep X-Starkscan-Route-Class: batch, but
they debit a separate query-class cost budget. Use
X-Starkscan-Query-Class (wallet_paymaster, defi_lending, or
support_proof),
X-Starkscan-Cost-Units, and the x-ratelimit-* headers for Query Plane
backoff. Do not use X-Starkscan-Rpc-Class for Query Plane accounting. On a
typed Query Plane error, X-Starkscan-Query-Error-Class identifies whether the
failure is invalid_request, unsupported_section, too_large_request,
stale_source, permission_denied, source_unavailable, or rate_limited.
For wallet/app Voyager migrations, treat the replacement set as:
| Migration route | Expected hosted key class |
|---|---|
GET /v1/{chain}/token/{token}/balance-of/{address} | light |
GET /v1/{chain}/tx/{tx_hash} | light |
GET /v1/{chain}/address/{address}/transactions | heavy |
GET /v1/{chain}/address/{address}/transfers | light |
Headers to read
Starkscan-issued API keys emit these budget headers on budgeted responses:
x-ratelimit-limitx-ratelimit-remainingx-ratelimit-policyX-Starkscan-Route-ClassX-Starkscan-Rpc-Classon authenticated JSON-RPC beta responsesX-Starkscan-Query-Class,X-Starkscan-Cost-Units, andX-Starkscan-Query-Error-Classon partner Query Plane responses
On 429 Too Many Requests, Starkscan also emits:
Retry-After
Example response headers:
x-ratelimit-limit: 30
x-ratelimit-remaining: 0
x-ratelimit-policy: heavy;w=60
X-Starkscan-Route-Class: heavy
retry-after: 12Retry rules
- On
429 Too Many Requests, stop and honorRetry-After. - Do not retry immediately in a tight loop.
- Keep concurrency bounded even when
x-ratelimit-remainingstill looks healthy. - Prefer the smallest route set that answers the task.
- Do not let a
heavy429 stop unrelatedlightprobes; keep backoff state per route class. - For address batch helpers, stay at or below 128 addresses per request, assume the request debits the
heavyREST budget, and reduce batch size if you receive503withRetry-After.
Example 429
curl -i \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/$STARKSCAN_CHAIN/contract/<address>/read?selector=<selector>"Representative response:
HTTP/1.1 429 Too Many Requests
retry-after: 12
x-ratelimit-limit: 30
x-ratelimit-remaining: 0
x-ratelimit-policy: heavy;w=60
X-Starkscan-Route-Class: heavy
content-type: application/json; charset=utf-8
{"code":"rate_limited","message":"Rate limit exceeded; retry shortly","docSlug":"api/rate-limits","requestId":"mzk-..."}Operational note
Current public-read rate limiting is process-local fixed-window state:
- counters reset on process restart
- each replica enforces its own independent window budget
- boundary-adjacent bursts can briefly approach roughly double the minute budget
Do not build client correctness on an assumption that every host behaves like one perfectly global distributed limiter.
Best practices for agents
- Use Agent HTTP quickstart as the bounded starter contract.
- Back off by route class. A
heavy429 should not force an agent to stop cheaplightstatus checks. - Log
X-Request-Idon every failure. - Include rate-limit headers in issue reports when present.
- Prefer cursor-based incremental reads over repeated full rescans.