The Arc mainnet API
You are on the testnet site
Indexed from block 0
Chain 5042 is indexed from its genesis block with no gaps, and the node behind it keeps full history. Any block, any transaction, any receipt, any balance, as far back as the chain goes.
Take none of that on trust: one call states it, and the figures below are read from that call.
Read from /v1/chain, and refreshed at most once an hour.
Base URL
One base URL, and it is this.
https://api-testnet.arc-scan.ioCallable straight from a browser: a response carries access-control-allow-origin: * and allows GET, HEAD and OPTIONS. It exposes ETag, the rate-limit headers and X-Request-Id, so a client can read them rather than guess. Only those three methods, though — a browser cannot POST here, which is why the MCP endpoint is not reachable this way. curl, a backend, a bot or an indexer are unaffected.
Start here
Every command on this page was run against the live service, and every response shown is what came back.
What is held, and how fresh it is
Chain constants, node capabilities and exactly which blocks are indexed. One call, and nothing else on this page has to be taken on trust.
curl -s -H 'User-Agent: my-app/1.0' https://api-testnet.arc-scan.io/v1/chain
It returned
{ "chain_id": 5042, "name": "Arc", "is_testnet": false, "mainnet_soon": false, "native": { "symbol": "USDC", "decimals": 18 }, "erc20_native": { "address": "0x3600000000000000000000000000000000000000", "decimals": 6 }, "block_time_ms": 506, "finality": "instant", "capabilities": { "ots": false, "ots_search": false, "trace": true, "debug": false, "archive": true, "tx_index": true, "holder_index": true, "verified_source": false, "verified_source_note": "<-- elided -->", "internal_transactions": true }, "index": { "available": true, "streams": [ { "stream": "balances", "genesis_block": 0, "last_contiguous_block": 13849844, "from_block": 0, "to_block": 13849844, "ranges": 1, "blocks_indexed": 13849845, "contiguous_from_genesis": true }, { "stream": "chain", "genesis_block": 0, "last_contiguous_block": 13849856, "from_block": 0, "to_block": 13849856, "ranges": 1, "blocks_indexed": 13849857, "contiguous_from_genesis": true }, { "stream": "traces", "genesis_block": 0, "last_contiguous_block": -1, "from_block": null, "to_block": null, "ranges": 0, "blocks_indexed": 0, "contiguous_from_genesis": false } ], "head_block": 13849856, "chain_blocks_indexed": 13849857, "chain_genesis_block": 0, "chain_first_indexed_block": 0, "covers_chain_from_block": 0, "complete_above_start_block": true, "unindexed_blocks_below_start": 0, "complete": true, "message": null }, "capabilities_note": null, "capabilities_note_phrases": [] }
Send a User-Agent
The call your existing Etherscan code already makes
The key goes in the query string as <mono>apikey</mono>, which is where Etherscan clients already put it.
curl -s -H 'User-Agent: my-app/1.0' "https://api-testnet.arc-scan.io/api?module=account\ &action=txlist&address=0x03a13352ef67977d1601ec1276e9bc27c0ee7b75\ &startblock=13800000&endblock=13830000&page=1&offset=1&sort=desc\ &apikey=YourApiKeyToken"
It returned
{ "status": "1", "message": "OK", "result": [ { "blockNumber": "13830000", "timeStamp": "1785842041", "hash": "0x3ae69af3843877c10d1aedde0fb299f0de358a76c4b704f338fc738b5d952ef5", "nonce": "88966", "blockHash": "", "transactionIndex": "0", "from": "0x92Df01DB3C69a49f4c4E694849E44C75D62B6752", "to": "0x03a13352eF67977d1601ec1276e9BC27C0Ee7b75", "value": "0", "gas": "598264", "gasPrice": "40000000000", "isError": "0", "txreceipt_status": "1", "input": "", "contractAddress": "", "cumulativeGasUsed": "348673", "gasUsed": "348673", "confirmations": "2933", "methodId": "0x38aaba3d", "functionName": "open" } ] }
Moving existing Etherscan code
A CSV of one address's transactions
Every export is about one address, so <mono>address</mono> is required and a block or date range narrows it. Four datasets — <mono>transactions</mono>, <mono>internal</mono>, <mono>logs</mono>, <mono>token-transfers</mono> — and <mono>/v1/export/limits</mono> states the caps before you meet them.
curl -s -H 'User-Agent: my-app/1.0' "https://api-testnet.arc-scan.io/v1/export/transactions/csv\ ?address=0x03a13352ef67977d1601ec1276e9bc27c0ee7b75\ &from_block=13800000&to_block=13800010"
It returned
block_number,block_time_utc,block_timestamp,tx_hash,tx_index,from,to,created_contract,direction,value_raw,value_18dec,fee_raw,fee_18dec,gas_limit,gas_used,effective_gas_price_raw,nonce,tx_type,method_id,input_size,status 13800001,2026-08-04T07:00:48Z,1785826848,0xb244d2791aa392fcc49e2c6bca9df0b6c402da2c1cd322f6356724fb77cc3131,0,0x8031885f93e31676132128557e987a011ff99d27,0x03a13352ef67977d1601ec1276e9bc27c0ee7b75,,in,0,0,13951720000000000,0.01395172,570664,348793,40000000000,141603,2,0x38aaba3d,516,success
New blocks are pushed, not polled
/v1/stream/head is a real text/event-stream: one event per committed block, about two a second on a half-second chain, each carrying the server's own clock so a consumer can correct for skew. Hold the connection open and the blocks arrive. There is nothing to poll and no interval to tune.
Follow the head of the chain
This command does not return. Two consecutive events from a live connection are shown below, three seconds apart.
curl -N -H 'User-Agent: my-app/1.0' https://api-testnet.arc-scan.io/v1/stream/head
It returned
event: head data: {"height": 13830746, "hash": "0xc580470583007f827ebfb3098b66d0682528c4ebaca4eb1213ab992c5952e65e", "timestamp": 1785842418, "tx_count": 1, "server_now": 1785842422, "available": true} event: head data: {"height": 13830751, "hash": "0x63d064be03dc0f5d530e10350b8dddd3de2cc8e6dbbffbe59306bb9fdc96c162", "timestamp": 1785842421, "tx_count": 1, "server_now": 1785842422, "available": true}
What is served
Checked one route at a time against the running mainnet service, rather than generated from a schema. The service publishes more routes than these; what is listed is what is supported.
| Endpoint | What it answers |
|---|---|
/v1/chain | Chain id, native token, node capabilities, and the exact block range held by each indexer stream. |
/v1/network/status/v1/network/validators | The chain head and its age in seconds, the measured block time, and the active validator set. |
/v1/blocks/v1/blocks/{ref}/v1/blocks/{ref}/txs | Blocks newest first, one block, and the transactions inside it. Every block names the validator that proposed it. |
/v1/txs/v1/txs/{hash}/v1/txs/{hash}/trace/v1/txs/{hash}/raw | Transactions newest first, one transaction with its status and revert reason, the internal call frames recovered for it, and the unmodified node objects behind it. |
/v1/address/{addr}/v1/address/{addr}/txs/v1/address/{addr}/logs/v1/address/{addr}/activity | An address with its balance and activity, the transactions it sent and received, the logs it emitted, and one merged timeline of everything that touched it. |
/v1/address/{addr}/tokens/v1/address/{addr}/facts/v1/address/{addr}/contract | Token balances held by an address, how it was funded and when it was first and last active, and, for a contract, its deployed bytecode. |
/v1/tokens/{addr}/v1/tokens/{addr}/holders/v1/tokens/{addr}/transfers | One token's on-chain facts, its holders ranked by balance, and its transfers. |
/v1/approvals/{addr} | Every ERC-20, ERC-721 and ERC-1155 approval an address has granted and not revoked. |
/v1/search/v1/search/suggest | Resolve a height, hash, address or token, with suggestions as you type. |
/v1/stats/summary/v1/stats/gas/v1/stats/tx-history | Network summary, gas statistics, and the daily transaction history over complete UTC days. |
/v1/charts/v1/charts/{metric}/v1/charts/{metric}/csv | A catalogue of twenty-four daily metrics, each readable as JSON or as CSV. |
/v1/explore/* | Chain-wide lists: accounts, contracts, tokens, token transfers, address labels, a directory of named entities, and top statistics over a chosen window. |
/v1/filter/limits/v1/filter/transactions/v1/filter/token-transfers | Query transactions and token transfers by address, method, amount, asset, status and range. The limits route states the bounds before you hit them. |
/v1/export/limits/v1/export/resolve/v1/export/{dataset}/csv | CSV for transactions, internal transactions, logs and token transfers, plus a route that maps a date range onto a block range.An export is always about one address: address is required, and a range narrows it rather than replacing it. |
/v1/actions/categories | The categories used to classify what a transaction actually did. |
/v1/stream/head | Server-sent events, one per committed block, each carrying the server's clock so a consumer can correct for skew. |
The Etherscan-shaped surface
One path, dispatching on module and action in the query string. Every answer is HTTP 200, so a strict client library cannot crash on a status code. Both GET and form-encoded POST are accepted, and module=proxy passes thirteen JSON-RPC methods straight through to the node.
| module | action | What it answers |
|---|---|---|
account | balance | Native balance, 18-decimal integer string |
account | balancemulti | Up to 20 addresses per call |
account | txlist | Transactions sent or received by an address, read from the index |
account | tokentx | Served from the scanned window; requires startblock and endblockThe token name, symbol and decimal fields come back empty on this chain. Read a token's scale from the token route, never from a transfer row. |
account | tokenbalance | Genuine ERC-20 query; returns the contract's own decimal scale |
account | addresstokenbalance | Every token balance held by one address |
block | getblockreward | blockReward is the sum of transaction fees; there is no issuance and no uncle reward |
block | getblocknobytime | Binary search over heights; immutable, cached permanently |
block | getblockcountdown | Uses the measured block time, not a constant |
transaction | getstatus | Execution status |
transaction | gettxreceiptstatus | Receipt status |
logs | getLogs | Range-clamped; an over-range request names the maximum span |
contract | getabi | The ABI of a contract verified on ArcscanNo contract on this chain is verified yet, so getabi answers NOTOK and getsourcecode answers an empty record. The deployed bytecode is served either way. |
contract | getsourcecode | Verified source, compiler settings and ABINo contract on this chain is verified yet, so getabi answers NOTOK and getsourcecode answers an empty record. The deployed bytecode is served either way. |
token | tokenholderlist | Holders and balances for one token |
stats | tokensupply | Total supply, read from the contract |
stats | ethsupply | Native supply, 18-decimal integer string |
stats | ethprice | The gas token is a dollar by construction, so this reports 1.00 |
stats | dailytxnfee | Total transaction fees per complete UTC day |
stats | dailynewaddress | Addresses seen for the first time, per complete UTC day |
proxy | eth_* | Block, transaction, call, code, storage, gas and raw-send methods |
Rate limit, errors and paging
Rate limit
A public caller gets a burst of 300 requests that refills at 60 a second. Every response states where you are, and a refusal is HTTP 429 carrying retry-after.
The bucket is keyed on the calling address as our edge sees it, so callers sharing one NAT or one cloud egress address share one bucket.
x-ratelimit-limit: 300 x-ratelimit-remaining: 299
Errors
On /api a refusal is HTTP 200 with status set to 0 and a sentence in result saying why, never a 404, so a strict client library cannot crash on it.
On /v1 a request rejected as invalid comes back through the edge as a 404 rather than a 400. Check the parameters before concluding that a route does not exist.
Paging
List routes are cursor-paged: an answer carries page.next, and passing it back as cursor returns the following page. Page size is capped at 100.
What this API does not answer
Published so you can build around it rather than discover it.
Internal transactions
Ask about one transaction and you get its internal calls: the per-transaction trace route answers 200 with the real frames, and account/txlistinternal returns the same frames when you pass txhash. Ask about an address or about the whole chain and you get a refusal instead, because Arcscan's own traces index holds no blocks on this deployment: /v1/explore/internal-txs answers 501 CAPABILITY_UNAVAILABLE with the missing capability named, account/txlistinternal by address refuses and says why, and the internal CSV export comes back with its header and no rows. That is a refusal you can detect, never a short list you cannot. State changes are a separate gap: the per-transaction state route still answers 501, because a state diff has to be produced by re-executing the transaction against the state as it stood before its block, and the endpoint this chain is read from does not serve the calls that needs.
How far one log query reaches
logs/getLogs answers on chain 5042 over a span of at most 10,000 blocks, and a request wider than that is refused with the maximum named rather than quietly answered from part of the range. account/tokentx answers too, and needs startblock and endblock spanning at most 200,000 blocks. Neither cap applies to our own index, which is where the wider questions are answered: logs per address on the address logs route, transfers per token on the token transfers route, and chain-wide through /v1/explore/token-transfers.
Verified source
Our verification provider does not cover chain 5042, so no mainnet contract is verified here today and none can be until it does. The deployed bytecode, the creation bytecode and the contract's creator are served regardless.
Off-chain data
No oracle prices, no market capitalisation, no logos, no websites, no curated exchange labels. The address labels published here are the ones provable from chain data — validators, system contracts, contracts we watched deploy — and there will not be a guessed one.
Actions that answer with a refusal
Each carries its own reason in the result rather than one generic error.
contract.getcontractcreation- The node behind chain 5042 does not expose the otterscan namespace. A contract's creator and creation transaction are on the address contract route.
account.getminedblocks- Arc has no mining and no block reward: blocks are proposed by a rotating BFT validator set, and the only value in a block is its transaction fees. The proposer column also carries no index, so this could not be answered without a full table scan. The blocks route names the validator instead.
account.tokennfttx- ERC-721 transfers are indexed, but not by address — the transfer table has no sender-and-recipient index, so filtering by an address would be a full scan. They are readable per token and chain-wide.
account.token1155tx- The same, for ERC-1155.
account.addresstokennftbalance- Our balances stream stores fungible balances only — one amount per token and holder, with no token-id dimension — so an NFT inventory cannot be read from it. The ERC-20 form of the question is answered.
token.tokeninfo- Most of this action's fields are off-chain: price, market capitalisation, website, social links. Arcscan holds no off-chain token data and consults no price oracle. The on-chain half — name, symbol, decimals, total supply, holder count — is served by the token route.
contract.verifysourcecode- Verification does not take Etherscan's flattened-source form fields. Submit Solidity standard JSON through the verification page, which is where that route is reachable.
Asking for a key
If you are building on this data, write and say who you are, what you are building, and roughly what volume you expect. That shapes what the key system has to do, and it is how you hear about a dedicated API address before it is announced.
Until then the base URL above serves you unauthenticated, and an apikey parameter is accepted and ignored, so an existing Etherscan client keeps working unchanged.
The full route-by-route reference, generated from the service's own schema, is on the API documentation page.