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.
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
- 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
- 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
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 not part of the public preview docs contract until Starkscan exposes 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 |
| Internal-only | App or ops; not your integration contract |
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 key.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. Use that same key across RPC, REST, the TypeScript SDK, the CLI, and MCP; 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/token-holdings"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
balance-of— you know the contract address.token-holdings— you care about the wallet; symbols like USDC can point at more than one live contract.- Big screens: avoid N×
balance-of; use holdings. - Gate on holdings only when
completeness.complete=true; legacy clients should also requireexact=true,truncated=false, andcompleteness.reasonCode="complete". GET /v1/{chain}/token/{token}/holdersis a partner-tier indexed holder census for token-contract-first analytics. It is not a substitute for transfer-history replay and 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"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
Raw events
GET /v1/{chain}/events— paginated raw events with optionaladdress,selector/topic0, andfrom_block/to_blockfiltersGET /v1/{chain}/contract/{address}/events— contract-scoped event search with exactselector/topic0,topic1,topic2, andtopic3filters
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 indexed exact-match filters. Use repeated address and selector/topic0 on the global route, or exact selector/topic0..topic3 on the contract route. key, keys, and global topic1..topic3 filters are rejected instead of ignored; use the contract-scoped route for later topic slots. Do not treat these routes as arbitrary event-data scans; bound the block window and page with nextCursor.
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 }, not { items, lastPage }.
Field mapping for Voyager adapters:
| Voyager field | Starkscan field |
|---|---|
items | items |
lastPage | stop when nextCursor is null |
keys | [topic0, topic1, topic2, topic3].filter(Boolean) |
name | eventName when attributed, otherwise null |
selector | topic0 |
transactionHash | txHash |
transactionNumber | txIndex |
number | logIndex |
timestamp | Unix seconds from timestampIso |
data | data |
eventName is attribution, not executable correctness proof: Starkscan prefers verified ABI names, then same-class verified ABI names, then curated selector names. If your indexer needs protocol-specific decoded values, keep decoding from raw topic* and data[] yourself until a decoded-events contract is documented for that protocol.
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.
Every request
- For data-plane routes under
/v1/{chain}/*, sendX-Starkscan-Api-Key. - 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