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.

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 not part of the public preview docs contract until Starkscan exposes 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
Internal-onlyApp 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.

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 key.
  • 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. 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 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

  • 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 require exact=true, truncated=false, and completeness.reasonCode="complete".
  • GET /v1/{chain}/token/{token}/holders is 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 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

Raw events

  • GET /v1/{chain}/events — paginated raw events with optional address, selector/topic0, and from_block/to_block filters
  • GET /v1/{chain}/contract/{address}/events — contract-scoped event search with exact selector/topic0, topic1, topic2, and topic3 filters

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 fieldStarkscan field
itemsitems
lastPagestop when nextCursor is null
keys[topic0, topic1, topic2, topic3].filter(Boolean)
nameeventName when attributed, otherwise null
selectortopic0
transactionHashtxHash
transactionNumbertxIndex
numberlogIndex
timestampUnix seconds from timestampIso
datadata

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}/*, send X-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 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