API
How to call Starkscan REST—base URL, auth, lists, errors. Paths live in the API reference.
API
The explorer, SDK, and CLI share one REST contract. For every path and field, open the API reference or starkscan-openapi.yaml. This page is how to use the API: URL shape, keys, paging, mistakes we see in the wild.
Start with API discovery when a client should configure itself from the current deployment instead of copying a route list from documentation.
Base URL
Hosted external lane:
- Production base =
https://api.starkscan.co - Optional override:
STARKSCAN_BASE_URL=https://<custom-host> - Resources =
/v1/...→ example:https://api.starkscan.co/v1/SN_MAIN/status - Do not use the same-origin
/v1/...explorer lane for external integrations; it is reserved for app traffic on Starkscan-hosted pages.
Explorer
Same objects as JSON: Dashboard · Transactions · Contracts · Watchlist
Related docs
- Get an API key — create or rotate the key used by RPC, REST, SDK, CLI, and MCP
- API discovery — capabilities, OpenRPC
rpc.discover, schema validation, and the RFC 9727 catalog - Starkscan RPC — copy the mainnet node URL or use header-auth JSON-RPC
- Concepts — base URL, auth, tiers, cursors
- Reference — generated catalogs and download artifacts
- Launch matrix — RPC, REST, SDK, CLI, and MCP readiness
- Classify addresses in bulk — ordered summaries and address intelligence for wallet/paymaster backends
- Class directory and coverage — origin catalog, observed instance lower bounds, and classification coverage
- Advanced utilities — batch previews and summaries
- Route certification — public states, evidence dimensions, and nightly proof
- Migration skills — source-backed
SKILL.mdartifacts for Voyager and accounting workflows - Self-serve account routes — session-authenticated key lifecycle and usage
- Monitor 10 wallets — one starter, three surfaces
- Agent HTTP quickstart — bounded setup for coding agents that call Starkscan directly
- Privacy Pool data API — public activity, note evidence, prepared metrics, and finalized public-flow boundaries
- Privacy Pool metrics and note evidence — copyable status, analytics, bucket, series, event, commitment, and nullifier integrations
- Event decoding — raw payloads, decoding statuses, provenance, and coverage claims
- Prepared staking API — finalized staking summaries, validators, delegators, activity, and typed coverage
OpenAPI tags (map only)
The sidebar under API reference is the full list. This table orients you:
| Tag | Covers |
|---|---|
| Account | Session-authenticated self-serve API-key lifecycle and usage |
| Addresses | Activity, txs, holdings, aggregate views |
| Blocks | Block metadata and lists |
| Contracts | Class, verification, reads |
| Reference | Generated OpenAPI artifacts and reference surfaces |
| Search | Explorer search |
| Status | Chain status |
| Tokens | Metadata, supply, balances, transfers |
| Transactions | Detail, previews, related reads |
| Utilities | Helpers; key tier may vary (Advanced utilities) |
Protocol-domain surfaces are part of the public contract only when Starkscan documents them explicitly.
Tiers
| Tier | Meaning |
|---|---|
| Official public API | Default contract in /docs and Scalar |
| Advanced utilities | Supported, often needs a broader key |
| Partner | Partner-only indexed analytics such as token-holder census |
Start on the official tier. Add utilities only for batch jobs that truly need them.
Certification states
The OpenAPI reference includes x-starkscan-certification on each operation.
| State | Use it how |
|---|---|
certified | Safe default for production client workflows. Current launch set: status, block read, timestamp-to-block, tx read, token total supply, and token balance-of. |
beta | Usable with named clients and explicit limits. Treat indexed lists, holder census, and protocol routes as beta until their reconciliation gates are complete. |
experimental | Partner preview only. Do not depend on schema stability. |
unsupported | Not for client use. |
Correctness is the hard launch gate. For token reads, use block_tag=<block_number> or block_tag=<block_hash> when you need reproducible exactness; latest and pending are live moving state. If a route is slow but certified, use backoff, caching, or a lower quota. If a route is fast but not yet reconciled, keep it out of unattended production paths.
Self-serve account routes
/v1/me/* is the personal-workspace control plane behind the signed-in /api-key experience.
- Auth with a Better Auth session, not
X-Starkscan-Api-Key. GET/HEAD/OPTIONSmay use the hosted browser session cookie or a bearer session token.POST/DELETEmust useAuthorization: Bearer <session_token>.GET /v1/me/api-keyslists metadata only.POST /v1/me/api-keysissues or rotates the default live read + batch + write key. Write execution remains separately gated; see Self-serve account routes.DELETE /v1/me/api-keys/{public_id}revokes one key.GET /v1/me/usagereturns recent request activity, failures, and per-key aggregates.
See Self-serve account routes for the exact auth shape and examples.
First calls
If you do not have a key yet, create one from the hosted API keys page. One server-side integration may use its scoped key across RPC, REST, the TypeScript SDK, the CLI, and MCP; browser-direct integrations need a dedicated client key and must never share it with a server-side service. Do not use the same-origin explorer /v1/* lane for external integrations. If your software asks for a Starknet node URL, start with Starkscan RPC.
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/status"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/token/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/total-supply?block_tag=latest"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/token/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/balance-of/0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8?block_tag=latest"For deterministic checks, resolve a block first and pass it as block_tag:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/token/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/balance-of/0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8?block_tag=10000000"Block reads
The safe block reads in the public spec come with concrete examples and defaults:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/blocks?limit=25"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/block/8717378?tx_limit=50"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/block/8717378/txs?limit=25"Wallet loop
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8/activity?limit=50"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8/transactions?limit=50"curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8/assets/discovery?scope=discovered_plus_registry&limit=25"curl -X POST \
-H "Content-Type: application/json" \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-d '{"ownerAddress":"0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8","mode":"require_complete","scope":"discovered_plus_registry","limit":25,"blockPreference":"latest_accepted_l2"}' \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/query/wallet-state"Filter transfers to several wallets—repeat address=:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/token/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/transfers?address=0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8&address=0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a&limit=100"One POST for many summaries (utility key tier):
curl -X POST \
-H "Content-Type: application/json" \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-d '{"addresses":["0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a","0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8"]}' \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/summaries"Readable attribution for one address:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a/attribution"Batch deployment, attribution, and inbound-funds flags:
curl -X POST \
-H "Content-Type: application/json" \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-d '{"addresses":["0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a","0x259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8"]}' \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/address/intelligence"Full scripted loop: Monitor 10 wallets.
Agent pitfalls
POST …/tx/previews→ body{"hashes":[...]}POST …/address/summaries→ body{"addresses":[...]}POST …/address/intelligence→ body{"addresses":[...]}GET …/address/{address}/attribution→ readable alias and indexed deployment/token metadata for one addressGET …/contract/.../read→ Starknet selector, not a Cairo nameGET …/verification404 → no record yet, not a missing route- 403 on a documented utility → key scope, not a typo in
X-Starkscan-Api-Key
Token reads
Read token-summary freshness before the counters
GET /v1/{chain}/token/{token} separates metadata from transfer-aggregate freshness:
summaryCacheStatus: currentorcomputedmeans the aggregate fields are available under the declared summary version.summaryCacheStatus: stalemeans cached counts can lag the independently indexed transfer feed. Do not interpret an oldlatestTransferBlockor a cached zero count as current chain truth.summaryCacheStatus: metadata_onlymeans transfer aggregates are not known yet:transferCount,distinctFromCount, anddistinctToCountarenull.latestTransferBlockcan still be populated from the independent transfer index.
Never coerce a null aggregate to zero. When freshness matters, compare the status and latestTransferBlock with the newest row from the documented transfer feed.
balance-of— you know the contract address.assets/discovery— you need candidate token contracts for the wallet. It returns evidence and coverage, not balances.query/wallet-state— you need a bounded wallet snapshot whose balances all refer to one immutable block hash.- Big screens: use discovery plus wallet state instead of unbounded client-side
balance-offan-out. walletSafe=trueis scoped to the selected candidates and declared discovery coverage; it is not global discovery proof.GET /v1/{chain}/token/{token}/holdersis a partner-tier complete, pageable census of one immutable indexed ERC-20 holder generation. It is intentionally outside the default read-key lane.
Partner holder example:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/token/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/holders?limit=50"Keep following nextCursor until it is null. Every page in one walk must
repeat the same chainId, tokenAddress, holderCount,
holderBalanceTotalRaw, and snapshot identity: generationId, asOfBlock,
asOfBlockHash, and rowDigest. expiresAt is the walk-specific continuation
deadline. Immutable generations expose generationId, asOfBlockHash, and
rowDigest together. A complete terminal walk has unique addresses and
contiguous ranks 1..holderCount.
New transfers can activate a newer generation without changing retained pages
in the current walk. Use the cursor before expiresAt; the default retention is
six hours, but the returned timestamp is authoritative. An invalid, cross-token,
revoked, or expired cursor returns HTTP 400 with
code="invalid_request"; discard the partial walk and restart without a
cursor. The API never silently moves a cursor to the latest generation.
If a generation or count cache is warming, the route instead returns retryable
HTTP 503 with Retry-After; retry the same request without discarding a valid
cursor.
nextCursor is page coverage. completeness.truncated stays false for normal
immutable pagination, while completeness.exact describes certification of the
full generation. Require certification.status="certified" and
completeness.exact=true for an exact population claim. Uncertified or stale
rows can still be useful and fully pageable. A redacted response under
revoked, unavailable, or audit_failed certification is not proof of zero
holders.
holders/analytics is computed from the complete generation, not the current
page. Combine it with a holder walk only when the durable snapshot identity fields
match. validatedAgainst="starknet_rpc_balanceOf" means bounded holder
samples were checked at the exact block hash. Starknet RPC does not enumerate
all holders and is not the population source.
Cross-layer messages, events, and token facts
Beyond the core reads, these public (read-tier) routes are easy to miss — full request/response schemas are in the API reference:
Cross-layer messages (L1 ↔ L2)
GET /v1/{chain}/messages— paginated canonical cross-layer messagesGET /v1/{chain}/contract/{address}/messages— canonical messages for one contractGET /v1/{chain}/contract/{address}/bridge-signals— L2 bridge-signal activity for one contract
Indexed events
GET /v1/{chain}/events— paginated, API-decoded events with optional address, positionaltopic0..topic15, and block filtersGET /v1/{chain}/contract/{address}/events— contract-scoped event search with the same positionaltopic0..topic15contract
Copy-paste global event search:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/events?selector=0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9&address=0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d&from_block=10630000&to_block=10630325&limit=100"Copy-paste contract-scoped event search:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/contract/0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d/events?selector=0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9&topic1=0x5983efa05a23ecc4eb29d8717f86b34412964ea152d6a499ea447fcf5f5ee39&topic2=0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8&from_block=10630000&to_block=10630325&limit=100"Event filters are exact-position matches on both routes. topic0..topic15 map to key positions zero through fifteen; repeated values at one position are OR, populated positions are AND, and omitted positions are wildcards. selector aliases topic0. The contract route also keeps key/keys as sequential singleton compatibility inputs; the global route rejects those aliases because they do not identify a position. Each position is capped at 128 distinct felts, each request at 256 total, pages at 100. Any topic1..topic15 filter requires topic0 plus explicit numeric from_block and to_block; an address alone is not a topic0 anchor. Ordinary keys may span at most 10,000 blocks. Wallet workspaces may request larger ranges only for ready selector-position coverage listed by /v1/meta/capabilities; unsupported selectors return 422 and unavailable declared coverage returns 503. Topic0-only searches may omit numeric bounds and page retained history.
Voyager /events migration
Voyager-style indexers usually call GET /events?contract={market}&p={page}&ps=100 and stop at lastPage. Starkscan uses the indexed contract-events route instead:
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/contract/$MARKET_ADDRESS/events?from_block=$FROM_BLOCK&to_block=$TO_BLOCK&limit=100"Keep calling the same route until nextCursor is null. Treat nextCursor as opaque and URL-encode it before putting it in the query string:
cursor="$(printf '%s' "$NEXT_CURSOR" | jq -sRr @uri)"
curl \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/contract/$MARKET_ADDRESS/events?from_block=$FROM_BLOCK&to_block=$TO_BLOCK&limit=100&cursor=$cursor"Response shape is { items, nextCursor, eventDecodingDegraded }, not { items, lastPage }.
Field mapping for Voyager adapters:
| Voyager field | Starkscan field |
|---|---|
items | items |
lastPage | stop when nextCursor is null |
keys | keys (the complete canonical key array; topic0..topic3 are compatibility aliases) |
name | eventName when attributed, otherwise null |
selector | topic0 |
transactionHash | txHash |
transactionNumber | txIndex |
number | logIndex |
timestamp | Unix seconds from timestampIso |
data | data |
Event decoding contract
Contract, global, block-detail, and transaction-detail event views use one server-certified contract. data[] is authoritative and keys[] is the canonical indexed key array; legacy rows without payload.keys reconstruct only topic0..topic3, so their keys[] may be incomplete. decodedFields is emitted only when an exact materialized ABI schema or a reviewed standard selector-and-arity schema consumes the full payload; its source/reason fields carry provenance.
decoded: typeddecodedFieldsare certified for the payload.name_only: the event name is attributed, but typed fields are not certified.unknown: no attribution is available at the event's execution class.
eventDecodingDegraded: true means only that the optional attribution lookup failed for this response, not that an individual event is unknown. Starkscan does not call RPC, Voyager, class-ABI, or trace sources at request time to fill event fields. The web UI consumes the API result directly.
Fully decoded response:
{"items":[{"blockNumber":10630025,"timestampIso":"2026-07-15T12:00:00Z","txHash":"0xabc","txIndex":4,"logIndex":1,"address":"0xcontract","keys":["0xtransfer","0xfrom","0xto"],"topic0":"0xtransfer","topic1":"0xfrom","topic2":"0xto","topic3":null,"data":["0x1","0x0"],"decodingStatus":"decoded","eventName":"Transfer","eventNameSource":"class_abi","decodedFields":[{"label":"amount","type":"core::integer::u256","source":"data","kind":"u256","status":"decoded","rawValues":["0x1","0x0"],"originIndexes":[0,1],"displayValue":"1","addressValue":null,"numericValue":"1","textValue":null,"boolValue":null}],"decodedFieldsSource":"class_abi"}],"nextCursor":null,"eventDecodingDegraded":false}Name-only response:
{"items":[{"blockNumber":10630024,"timestampIso":"2026-07-15T11:59:00Z","txHash":"0xdef","txIndex":3,"logIndex":0,"address":"0xcontract","keys":["0xapproval","0xowner"],"topic0":"0xapproval","topic1":"0xowner","topic2":null,"topic3":null,"data":["0x10","0x0"],"decodingStatus":"name_only","eventName":"Approval","eventNameSource":"selector_unique","decodedFieldsUnavailableReason":"selector_only_attribution"}],"nextCursor":null,"eventDecodingDegraded":false}Unknown response:
{"items":[{"blockNumber":10630023,"timestampIso":"2026-07-15T11:58:00Z","txHash":"0x123","txIndex":2,"logIndex":7,"address":"0xcontract","keys":["0xunmatched","0x7"],"topic0":"0xunmatched","topic1":"0x7","topic2":null,"topic3":null,"data":["0xbeef"],"decodingStatus":"unknown"}],"nextCursor":null,"eventDecodingDegraded":false}Token facts
GET /v1/{chain}/token/{token}/controls— indexed token control facts
GET /v1/{chain}/token/{token}/markets/pools (DEX pool facts) and GET /v1/{chain}/token/{token}/holders/analytics (holder-concentration analytics) also exist but require a partner-tier key — read-tier keys receive 403.
Privacy Pool public data
The beta external surface includes status, analytics, metric-buckets,
metrics/series, events, commitments, and nullifiers. See
Privacy Pool metrics and note evidence for
copyable requests, response gates, cursor handling, and intentional exclusions.
GET /v1/{chain}/privacy-pool/tvl is the current finalized snapshot.
GET /v1/{chain}/privacy-pool/tvl/series returns prepared hourly points, not
a certified continuous history. Both are beta and can be called directly over
REST or through the current Starkscan SDK.
Server-to-server clients normally omit Origin. Browser clients may call external api.starkscan.co/v1 or app-host /api/v1 REST routes from arbitrary origins by sending the API key in X-Starkscan-Api-Key or X-Api-Key; responses use wildcard, non-cookie CORS (Access-Control-Allow-Origin: * without Access-Control-Allow-Credentials). Both preflights and actual requests carrying bearer, cookie, internal, or legacy credentials are excluded from that wildcard lane and remain subject to Starkscan's strict origin allowlist. The public edge also strips caller-supplied internal keys as credential sanitization; stripping does not move those requests into the wildcard lane. MCP, OAuth, WSS, and self-serve session requests remain restricted to Starkscan-configured origins. Never embed a shared production key in a public frontend bundle; use a client-specific key with bounded scope and rate limits.
curl --fail-with-body --silent --show-error \
-D /tmp/starkscan-tvl.headers \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/privacy-pool/tvl" \
-o /tmp/starkscan-tvl.jsonKeep the returned weak ETag and revalidate it. If-None-Match takes precedence over If-Modified-Since; a matching request returns 304 with no body:
etag="$(awk 'BEGIN{IGNORECASE=1} /^etag:/ {sub(/^[^:]+:[[:space:]]*/, ""); sub(/\r$/, ""); print; exit}' /tmp/starkscan-tvl.headers)"
curl --fail-with-body --silent --show-error \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
-H "If-None-Match: $etag" \
"${STARKSCAN_BASE_URL:-https://api.starkscan.co}/v1/SN_MAIN/privacy-pool/tvl" \
-o /dev/null -w '%{http_code}\n'Raw decimal-string token amounts are authoritative.
coverage.latestEventCursor is the exact block:tx:log public-flow watermark
for current-snapshot same-block reconciliation. coverage.finalizedOnly=true means
starkscan_indexed_finalized_tier, not L1 settlement; call the snapshot
L1-accepted only when coverage.asOfL1Accepted=true. The L1 fields are null
when indexed acceptance evidence is unavailable. The request path never calls
RPC, another explorer, or a price provider. Schema-v1 valuation fields are
legacy compatibility fields, not a pricing contract for new integrations.
Hourly pages require inclusive, UTC-hour-aligned from and to RFC3339
timestamps plus granularity=hour. They return at most 24 oldest-first points;
pass nextCursor unchanged until it is null. An empty items[] page or
sparse UTC-hour sequence is unavailable history, not a zero-value point or
complete-range assertion; fail closed. The current page shape has no
page-level coverage or freshness watermark, so do not synthesize missing hours
from RPC, another explorer, or request-time pricing. Hourly assets include
nonzero public-flow amounts and amount-incomplete degraded entries; known zero
amounts are omitted until reactivated.
Keyed api.starkscan.co responses remain private. The trusted same-origin /v1 lane may use public caching but is reserved for Starkscan app traffic. ETag is authoritative. On the current snapshot, Last-Modified is the current process cache-generation time, so a cold process may conservatively return 200; on an hourly page, it is the newest materialization timestamp in that page.
Every request
- For data-plane routes under
/v1/{chain}/*, sendX-Starkscan-Api-Key; browser-direct REST clients may useX-Api-Key. - The explorer's same-origin
/v1lane is trusted and distinct from the external API-host/v1and app-host/api/v1lanes. - Never paste full API keys into chats, tickets, screenshots, or PR comments. Redact them like
mzk_REDACTED_KEY. - For workspace-control routes under
/v1/me/*, use Better Auth sessions for your personal Starkscan workspace: cookie auth for safe reads, bearer token required forPOST/DELETE. - Log
X-Request-Idwhen you open a support thread. - Expect client/server failures as JSON with
code,message,docSlug, andrequestId. TheX-Request-Idheader is canonical. - Log
X-Starkscan-Route-Classwhen present so agents can back off by class. - Treat
503as retryable when it indicates temporary unavailability, and honorRetry-Afterwhen present. - Sanity-check a host with
npx -y @starkscan/cli doctor(base URL, auth, reachability, hosted MCP), plus astatuscall and a deliberately bad key to confirm401.
503 example
HTTP/1.1 503 Service Unavailable
Retry-After: 5
X-Request-Id: mzk-...
Content-Type: application/json; charset=utf-8
{"code":"service_unavailable","message":"Temporarily unavailable","docSlug":"api/retry","requestId":"mzk-..."}Honor Retry-After. Do not spin in a tight loop.
401 / 403 examples
HTTP/1.1 401 Unauthorized
X-Request-Id: mzk-...
WWW-Authenticate: Bearer realm="starkscan", error="invalid_token"
Content-Type: application/json; charset=utf-8
{"code":"unauthorized","message":"Unauthorized","docSlug":"api/auth","requestId":"mzk-..."}HTTP/1.1 403 Forbidden
X-Request-Id: mzk-...
WWW-Authenticate: Bearer realm="starkscan", error="insufficient_scope", scope="batch"
Content-Type: application/json; charset=utf-8
{"code":"forbidden","message":"Forbidden","docSlug":"api/auth","requestId":"mzk-..."}Not REST?
| Need | Doc |
|---|---|
| Typed TS | SDK |
| Shell | CLI |
| MCP | MCP quickstart |
| Starknet JSON-RPC provider | Starkscan RPC |
| Click-only | Explorer |
Documented elsewhere on purpose
- MCP transport → MCP quickstart
- Batch address classification → Classify addresses in bulk
- Batch helpers → Advanced utilities
- Protocol domains → hidden until explicitly exposed
- Private ops → not part of your contract