Served by services/faster. In the compose deployment the API shares the UI's
origin (nginx proxies /api/, /stats, /health, /ready and /ws).
Conventions:
- Hashes and addresses: lower-case
0xhex. Inputs are accepted in any case. - Block numbers, gas, counts, nonces, indexes: JSON numbers.
- Wei amounts and prices (
value,gas_price,balance, fee fields): decimal strings, since they exceed 2^53. - Times:
timestamp/block_timestampare unix seconds;first_seen/last_seenare RFC 3339. - Errors:
{"error": "..."}with 400 (bad input), 404, 429 (rate limited, withRetry-After), 500, 503, 504 (request took over 15 s). - Blocks and txs more than
REORG_DEPTHbelow the head are served withCache-Control: public, max-age=86400, immutable; newer data withno-cache.
| Endpoint | |
|---|---|
GET /api/blocks?limit=20 |
newest blocks (limit ≤ 100) |
GET /api/blocks/range/{start}/{end} |
blocks in a range (≤ 1000), ascending |
GET /api/blocks/{number|hash} |
one block with up to 500 transactions inline |
GET /api/blocks/{number|hash}/transactions?offset=0&limit=100 |
a block's transactions, in index order (limit ≤ 1000) |
Block fields: number, hash, parent_hash, timestamp, miner,
gas_used, gas_limit, base_fee_per_gas, difficulty,
transactions_count, logs_count, state_root, transactions_root,
receipts_root, size. The detail adds transactions and
transactions_truncated (more than 500 txs: page the rest).
| Endpoint | |
|---|---|
GET /api/transactions?limit=20 |
newest transactions (limit ≤ 100) |
GET /api/transactions/{hash} |
one transaction with its logs and block_hash |
GET /api/transactions/address/{address}?limit=20&before= |
an address's transactions, newest first |
GET /api/transactions/pending/list |
always [] (no mempool view) |
Transaction fields: hash, block_number, block_timestamp,
transaction_index, from_address, to_address (null for creations),
value, gas, gas_price (price actually paid: the receipt's
effectiveGasPrice when the node reports it), effective_gas_price,
max_fee_per_gas, max_priority_fee_per_gas, gas_used, nonce,
input_data, status (1, 0, or null for pre-Byzantium), contract_address,
logs_count, transaction_type.
Logs: log_index (block-level), address, topics, data.
| Endpoint | |
|---|---|
GET /api/addresses?limit=20&offset=0 |
addresses by transaction count, highest first |
GET /api/addresses/{address}?limit=25&before= |
summary + transactions, newest first |
GET /api/addresses/{address}?limit=25&page=N |
same, offset paging (cost grows with depth) |
GET /api/addresses/search/by-pattern?pattern=0xab12 |
addresses starting with a hex prefix (≥ 3 hex chars) |
Address fields: address, transaction_count (distinct transactions it is
party to: sender, recipient or created contract), first_seen, last_seen,
is_contract, bytecode, bytecode_size, deploy_info (hash,
block_number, block_timestamp, from_address of the creating tx),
balance (live from the node, cached 2 s; null if the node is unavailable),
transactions, total_transactions, page, limit, next_cursor.
Paging with cursors: pass the response's next_cursor ("<block>:<index>")
as before= to get the next page; next_cursor is null on the last page.
This costs the same at any depth (0.16 ms at 100K deep vs 13 ms with page=).
ERC-20, ERC-721 and ERC-1155 transfers are decoded from logs (Transfer,
TransferSingle, TransferBatch). Balances are derived from transfers, so
they are exact when indexing starts at block 0 (START_BLOCK=0). Name,
symbol and decimals are read from the contract once, when the token is first
seen.
| Endpoint | |
|---|---|
GET /api/tokens?limit=50&offset=0 |
tokens by transfer count: address, type, name, symbol, decimals, holders, transfers, first_block |
GET /api/tokens/{address} |
one token |
GET /api/tokens/{address}/transfers?limit=25&before= |
its transfers, newest first |
GET /api/tokens/{address}/holders?limit=50&offset=0 |
largest holders (value: balance, or NFT count for ERC-721) |
GET /api/addresses/{address}/token-transfers?limit=25&before= |
transfers in or out of an address |
GET /api/addresses/{address}/tokens |
tokens held (value, and token_id for ERC-1155) |
Transfer fields: block_number, block_timestamp, transaction_hash,
log_index, batch_index (position in an ERC-1155 batch), token,
token_type, token_symbol, token_decimals, from_address, to_address,
token_id, value (raw integer string), cursor. Lists return
{"items": [...], "next_cursor": ...}; pass next_cursor as before=.
GET /api/transactions/{hash} includes token_transfers.
GET /api/search?q= resolves a block number, transaction or block hash,
address (type: "token" if it is a token contract) or token name / symbol
prefix: [{"type": "block" | "transaction" | "address" | "token", "value": ...}].
With TRACE_CALLS=true the indexer also fetches debug_traceBlockByNumber
(callTracer) for every block and stores each call made inside a transaction.
This needs a node that serves traces for every indexed block (an archive node
for history).
GET /api/transactions/{hash}includesinternal_transactionsGET /api/addresses/{address}/internal?limit=25&before=— newest first, cursor paging
Fields: transaction_index, trace_index (depth-first order within the
tx), depth (1 = called by the tx itself), type (CALL, DELEGATECALL,
STATICCALL, CALLCODE, CREATE, CREATE2, SELFDESTRUCT), from_address,
to_address, value, gas, gas_used, error; list items add
block_number, block_timestamp, transaction_hash, cursor.
Solidity contracts are verified from standard-JSON input with the exact compiler release. faster downloads the official solc binary for that release (binaries.soliditylang.org, checked against the published sha256), compiles, and compares with the chain:
- runtime code: immutables masked, library placeholders taken from the chain;
- creation code: when the contract was deployed by a transaction, that transaction's input must match the compiled creation code too (so a different constructor or initializer cannot verify); the remainder is recorded as constructor arguments;
fullmatch: identical including the metadata hash;partial: identical code, different metadata (e.g. comments), or a contract created by another contract (runtime code only).
| Endpoint | |
|---|---|
POST /api/contracts/{address}/verify |
{"compiler_version": "v0.8.24+commit.e11b9ed9", "contract_name": "src/Token.sol:Token", "standard_json": {...}} → {"status": "verified", "match_type": ...} or {"status": "failed", "error": ...} |
GET /api/contracts/{address} |
name, file, compiler, match_type, abi, sources, settings, constructor_args, verified_at |
Once verified, GET /api/transactions/{hash} adds decoded_input
(name, signature, params[{name, type, value}]) for calls to the contract and
decoded on its logs (params also carry indexed); address details carry
verified: {name, match_type, compiler}.
Foundry: forge verify-contract --verifier blockscout --verifier-url https://<host>/api <address> <path:Name> (uses the Etherscan-style actions
below). Hardhat: set the network's apiURL to https://<host>/api.
GET /api?module=...&action=... with Etherscan's envelope
({"status":"1","message":"OK","result":...}, all values strings):
| module / action | parameters |
|---|---|
account / balance |
address |
account / balancemulti |
address (comma-separated, ≤ 20) |
account / txlist |
address, startblock, endblock, page, offset, sort (asc default) |
account / tokentx, tokennfttx, token1155tx |
address and/or contractaddress, same paging |
account / txlistinternal |
address or txhash (requires TRACE_CALLS=true) |
block / getblocknumberbytime |
timestamp, closest (before / after) |
transaction / gettxreceiptstatus |
txhash |
proxy / eth_blockNumber |
— |
contract / verifysourcecode (POST) |
contractaddress, sourceCode (standard JSON), codeformat=solidity-standard-json-input, contractname, compilerversion, constructorArguements → guid |
contract / checkverifystatus |
guid → Pending in queue / Pass - Verified / Fail - ... |
contract / getabi, getsourcecode |
address |
page × offset is capped at 10,000 as on Etherscan.
GET /stats:
{"total_blocks":"2202","block_height":"2201","latest_block_timestamp":"1792675773",
"total_transactions":"1100001","total_addresses":"1350","total_logs":"219560",
"avg_block_time":"1.000","tps":"500.00"}avg_block_time is over the last 100 blocks; tps is the latest block's
transactions divided by the average block time of the last 20 blocks.
GET /health—{"status":"ok","timestamp":...,"connectedClients":N}(liveness)GET /ready— 200 when the store is withinREADY_MAX_LAGblocks of the node, else 503; body{"ready","head","tip","lag"}
Connect to /ws (or /). The server sends JSON text frames:
type |
data |
when |
|---|---|---|
init |
{stats, blocks: [20 newest], txs: [20 newest]} |
on connect, and after a chain reorganisation |
block |
{hash, number, timestamp, miner, gas_used, transactions_count} |
each committed block (≤ 50 per commit) |
tx |
{hash, block_number, from_address, to_address, value, status} |
newest txs of a commit (≤ 20), oldest first |
stats |
as /stats |
after each commit and every 5 s |
Clients may send {"type":"ping"} and get {"type":"pong"}. The server
pings every 20 s and closes connections that are silent for 60 s, block a
write for 10 s, or fall more than 256 messages behind; reconnecting yields a
fresh init.
/api/clob/* (orders, fills, cancels by trader, pair tape, per-block lists,
order lookup by hash, archive stats) is documented in
CLOB_ARCHIVE.md. It needs CLICKHOUSE_URL; without it
these routes answer 503.