Public RPC
https://rpc.arc-scan.io is a free JSON-RPC endpoint for Arc mainnet that fronts several independent providers, so a single provider’s outage is not visible to you.
https://rpc.arc-scan.ioThe endpoint#
One hostname, one chain, no credential. It answers the ordinary read methods, refuses a short list of others by policy, and identifies itself as arcscan-rpc-gateway/1 when you ask it web3_clientVersion — which is how you can tell you reached us and not some other endpoint your wallet had configured.
| Property | Value |
|---|---|
| URL | https://rpc.arc-scan.io |
| Chain | Arc mainnet only — chain id 5042, hex 0x13b2 |
| Transport | HTTPS POST only. A GET answers 405 with allow: POST, OPTIONS. |
| Authentication | None. No key, no account, no signup, no header. |
| Browser calls | Allowed — every answer carries access-control-allow-origin: *. |
| Cost | Free, and rate limited per caller. |
Mainnet only
This endpoint serves chain5042 and nothing else — ask it net_version and it says 5042. There is no public Arcscan JSON-RPC endpoint for Arc Testnet. If you are working against testnet, use testnet.arc-scan.ioOpens in a new tab for the explorer and your own node or provider for RPC.Your first call#
There is nothing to sign up for, so the first call is the whole of getting started. Ask which chain you are talking to:
curl -s -X POST https://rpc.arc-scan.io \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId"}' { "jsonrpc": "2.0", "id": 1, "result": "0x13b2" }
And the head of the chain. Arc produces a block roughly every half second, so this number moves while you read it.
curl -s -X POST https://rpc.arc-scan.io \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber"}' { "jsonrpc": "2.0", "id": 1, "result": "0xe2a22d" }
Batches#
A JSON array of calls is answered with an array of results, in id order. Up to 50 entries and 131,072 bytes per request; over either cap the whole request is rejected with 413 before any of it runs.
curl -s -X POST https://rpc.arc-scan.io \ -H 'content-type: application/json' \ -d '[{"jsonrpc":"2.0","id":1,"method":"eth_chainId"}, {"jsonrpc":"2.0","id":2,"method":"eth_blockNumber"}]' [ { "jsonrpc": "2.0", "id": 1, "result": "0x13b2" }, { "jsonrpc": "2.0", "id": 2, "result": "0xe2a22e" } ]
A batch is not a snapshot
The two heights above differ by one. Each entry is served independently and the chain moves between them, so a batch never gives you a consistent view of a single moment. When you need several facts about one block, pin them to a block number or hash rather than tolatest.What it answers#
The ordinary eth_ read methods work: block, transaction and receipt lookups, eth_getBalance, eth_call, eth_getLogs, eth_gasPrice, eth_estimateGas. Four methods are answered by the endpoint itself with no outbound request — eth_chainId, net_version, web3_clientVersion and eth_accounts, which returns an empty array because we hold no accounts.
Rather than publish a list of every method that works, ask: anything not refused below is forwarded. A method no source offers comes back as -32601 with data.reason of method_not_served, which is a different fact from a policy refusal and says so.
What it refuses#
Refusals are deliberate, and each one states who decided and why. The reason is machine readable: error.data.reason is refused_by_policy and error.data.policy is one of the slugs below — which is what a client should read to decide whether asking again later could ever help. For every slug here, it cannot.
| policy | Why | Measured examples |
|---|---|---|
no_custody | This endpoint holds no keys and will not forward a signing request. | eth_sendTransaction, eth_sign, eth_signTypedData_v4 |
namespace | A whole namespace that is not served here. | debug_traceTransaction, ots_getApiLevel |
cost | The answer is very large and this endpoint is metered. | trace_block |
no_transport | HTTP only. There is no subscription transport here to serve it over. | eth_subscribe, eth_unsubscribe |
single_leg | Only one of the sources behind this endpoint offers it, so publishing it would promise a capability that could disappear without warning. | eth_getProof |
opacity | The answer would describe a node this endpoint does not operate, so there is no honest value to give. | net_peerCount, net_listening |
curl -s -X POST https://rpc.arc-scan.io \ -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_sendTransaction","params":[{}]}' { "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "arc-scan.io does not serve eth_sendTransaction on this endpoint. This is our own policy decision, not a limitation of the Arc network. This endpoint holds no keys and will not forward a signing request. Sign locally and use eth_sendRawTransaction.", "data": { "reason": "refused_by_policy", "method": "eth_sendTransaction", "policy": "no_custody", "documentation": "https://arc-scan.io/developers" } } }
A refusal is HTTP 200
Every refusal above answers200 OK with the error inside the JSON-RPC error object, which is what the JSON-RPC specification asks for. So a monitor that watches HTTP status alone will call all of this healthy. Read error.code and error.data.reason, not the status line.Sending a transaction#
We hold no keys, so every method that would require custody of one is refused — sign locally instead. An already-signed transaction can be broadcast: eth_sendRawTransaction is forwarded, and it is deliberately sent to exactly one provider and never retried, because a proxy that retried a broadcast could submit your transaction twice.
Read the error, do not resubmit blindly
already known and nonce too low come back from the node unchanged rather than being smoothed over, and both usually mean your transaction is already in flight. A malformed payload comes back as -32602 with the node’s own decode message.Failover#
Behind the one hostname are several independent Arc mainnet providers. A call that fails on one is retried on another, a provider that starts erroring or rate limiting us is cooled off and skipped, and none of that is visible in your response: you get an answer, or an honest error, and never a hint about which source served you. We do not publish who they are.
Two consequences worth knowing
A method offered by only one provider is refused rather than served (thesingle_leg row above) — a capability that would vanish the moment one source cooled is not a capability we will promise. And if no source could answer at all, you get -32603 with data.reason of unreachable and a message saying that nothing about the result should be assumed, rather than an empty result that would read as “nothing happened on chain”.Limits and errors#
Requests are rate limited per caller. We are not publishing the threshold here — the only figure we could quote is a configured one, and what you actually meet is the deployed edge — so treat a 429 as the signal, honour its retry-after, and back off rather than tuning against a number. The size caps are exact, and are stated in the refusal itself.
HTTP/1.1 429 Too Many Requests retry-after: 1 content-type: application/json { "jsonrpc": "2.0", "id": null, "error": { "code": -32005, "message": "arc-scan.io is rate limiting requests from this client. Retry shortly.", "data": { "reason": "edge_rate_limited", "retry_after_seconds": 1, "scope": "client" } } }
| HTTP · JSON-RPC code | What happened |
|---|---|
405 · -32600 | You used GET. JSON-RPC requests must use POST. |
413 · -32600 | The request exceeded 131072 bytes or 50 batch entries; data.reason is request_too_large and both caps are in the message. |
429 · -32005 | Too many requests from one caller. data.reason is edge_rate_limited; honour retry-after. |
200 · -32601 | Either a method we refuse (data.reason is refused_by_policy, with a policy slug) or one no source offers (method_not_served). |
200 · -32602 | Your parameters were rejected. This one is the node’s own message, passed through unchanged. |
200 · -32603 | No answer was obtained at all — data.reason is unreachable. The message says so explicitly, and nothing about the result should be assumed. |