Starkscan

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

OpenAPI tags (map only)

The sidebar under API reference is the full list. This table orients you:

TagCovers
AccountSession-authenticated self-serve API-key lifecycle and usage
AddressesActivity, txs, holdings, aggregate views
BlocksBlock metadata and lists
ContractsClass, verification, reads
ReferenceGenerated OpenAPI artifacts and reference surfaces
SearchExplorer search
StatusChain status
TokensMetadata, supply, balances, transfers
TransactionsDetail, previews, related reads
UtilitiesHelpers; key tier may vary (Advanced utilities)

Protocol-domain surfaces are part of the public contract only when Starkscan documents them explicitly.

Tiers

TierMeaning
Official public APIDefault contract in /docs and Scalar
Advanced utilitiesSupported, often needs a broader key
PartnerPartner-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.

StateUse it how
certifiedSafe default for production client workflows. Current launch set: status, block read, timestamp-to-block, tx read, token total supply, and token balance-of.
betaUsable with named clients and explicit limits. Treat indexed lists, holder census, and protocol routes as beta until their reconciliation gates are complete.
experimentalPartner preview only. Do not depend on schema stability.
unsupportedNot 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 / OPTIONS may use the hosted browser session cookie or a bearer session token.
  • POST / DELETE must use Authorization: Bearer <session_token>.
  • GET /v1/me/api-keys lists metadata only.
  • POST /v1/me/api-keys issues 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/usage returns 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 address
  • GET …/contract/.../read → Starknet selector, not a Cairo name
  • GET …/verification 404 → 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: current or computed means the aggregate fields are available under the declared summary version.
  • summaryCacheStatus: stale means cached counts can lag the independently indexed transfer feed. Do not interpret an old latestTransferBlock or a cached zero count as current chain truth.
  • summaryCacheStatus: metadata_only means transfer aggregates are not known yet: transferCount, distinctFromCount, and distinctToCount are null. latestTransferBlock can 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-of fan-out.
  • walletSafe=true is scoped to the selected candidates and declared discovery coverage; it is not global discovery proof.
  • GET /v1/{chain}/token/{token}/holders is 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 messages
  • GET /v1/{chain}/contract/{address}/messages — canonical messages for one contract
  • GET /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, positional topic0..topic15, and block filters
  • GET /v1/{chain}/contract/{address}/events — contract-scoped event search with the same positional topic0..topic15 contract

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 fieldStarkscan field
itemsitems
lastPagestop when nextCursor is null
keyskeys (the complete canonical key array; topic0..topic3 are compatibility aliases)
nameeventName when attributed, otherwise null
selectortopic0
transactionHashtxHash
transactionNumbertxIndex
numberlogIndex
timestampUnix seconds from timestampIso
datadata

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: typed decodedFields are 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.json

Keep 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}/*, send X-Starkscan-Api-Key; browser-direct REST clients may use X-Api-Key.
  • The explorer's same-origin /v1 lane is trusted and distinct from the external API-host /v1 and app-host /api/v1 lanes.
  • 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 for POST / DELETE.
  • Log X-Request-Id when you open a support thread.
  • Expect client/server failures as JSON with code, message, docSlug, and requestId. The X-Request-Id header is canonical.
  • Log X-Starkscan-Route-Class when present so agents can back off by class.
  • Treat 503 as retryable when it indicates temporary unavailability, and honor Retry-After when present.
  • Sanity-check a host with npx -y @starkscan/cli doctor (base URL, auth, reachability, hosted MCP), plus a status call and a deliberately bad key to confirm 401.

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?

NeedDoc
Typed TSSDK
ShellCLI
MCPMCP quickstart
Starknet JSON-RPC providerStarkscan RPC
Click-onlyExplorer

Documented elsewhere on purpose

On this page