Docs

Method support, rate limits, error codes, and the routing model behind Azul. Public traffic needs no key. Privileged keys raise every cap below.

Quickstart

Copy the URL, make your first request.

No key, no signup. Point any client at the endpoint and call eth_chainId, you should get back 0x2105 (decimal 8453).

POSThttps://rpc.baseazul.dev
curl -X POST https://rpc.baseazul.dev \  -H 'content-type: application/json' \  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'# => {"jsonrpc":"2.0","id":1,"result":"0x2105"}

The endpoint#

One HTTPS URL serves all standard JSON-RPC methods. Public traffic does not require an API key. Privileged keys exist for higher limits and access to restricted namespaces.

The endpoint is standard JSON-RPC 2.0. Content-Type must be application/json. Body is a single request object or an array (batch). Responses are gzip/zstd compressed when the client advertises support.

Method support#

All standard eth_*, net_*, and web3_* methods are available on the public path. Restricted namespaces are blocked unless you authenticate with an API key.

FieldValueSupport
eth_*All standard reads, writes, filtersFull support
net_*Full support
web3_*Full support
eth_sendRawTransactionSubmission optimized for lowest inclusion latencyFull support
eth_getLogsUp to 5,000-block window per callFull support
debug_*Blocked on public path. Available with API key.Not available on the public path
trace_*Parity-style traces not exposed.Not available on the public path
txpool_*Blocked on public path.Not available on the public path

Need debug_* or trace_* for production analytics? Request an API key.

Rate limits#

Per-IP limits on anonymous traffic. API-key holders bypass all of these.

FieldValue
Standard methods100 req/s sustained · burst 400
Heavy methods10 req/s sustained. eth_getLogs, large eth_call
Batch size50 requests per batched call
Max response body50 MB · 413 if exceeded
Daily egress (anon)1 TB raw JSON / day · 503 once exhausted
API keyBypasses every cap above
When you hit the cap
A throttled request returns -32005 with a Retry-After header. Back off with jitter; don’t spin. See pricing and when you need a key →

Error codes#

JSON-RPC errors follow standard codes. HTTP-level errors come back as raw responses.

CodeTrigger
-32000Method blocked on public path (restricted namespace).
-32005Rate limit exceeded. Back off or use an API key.
-32601Method not found on the underlying node.
-32602Invalid params. eth_getLogs window > 5,000 blocks is the usual culprit.
-32603Internal error / routing temporarily unavailable.
HTTP 413Response too large (>50 MB). Trim your request.
HTTP 503Daily egress quota exhausted (anonymous only). Use a key.

Performance#

Server-side latency from us-east-1. /status owns the live proof; these are the published reference values.

4 ms
p50 · median

server-side

15 ms
p95 · tail

server-side

~1s
Block tip

behind the head

Verify it yourself
These are watched in the open at /status, and you can reproduce them per /methodology.

Batching#

Batch up to 50 requests in a single POST. Useful for fan-out reads (multicall warmup, ENS resolution, parallel eth_call). One TLS handshake, lower tail latency.

Request
[  {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1},  {"jsonrpc":"2.0","method":"eth_getBalance","params":["0xabc…","latest"],"id":2},  {"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":3}]
Response
[  {"jsonrpc":"2.0","id":1,"result":"0x1414958"},  {"jsonrpc":"2.0","id":2,"result":"0x2386f26fc10000"},  {"jsonrpc":"2.0","id":3,"result":"0x2105"}]

eth_getLogs window#

Max range is 5,000 blocks per call. Wider ranges return -32602. Page through history by chunking the range and parallelizing requests, see Page through eth_getLogs.

Request an API key#

One key raises every cap on this page and opens restricted namespaces (debug_*, trace_*). Tell us your workload and we’ll set you up, a free key for normal development, no strings. (WebSocket subscriptions and streaming feeds are Edge early access; if you’re a fill-sensitive desk, see Edge below.)

Authenticate

Once you have a key, present it in the X-API-Key request header (or ?api_key= on the WebSocket URL):

curl
curl -s https://rpc.baseazul.dev \  -H 'content-type: application/json' \  -H 'X-API-Key: YOUR_KEY' \  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

Add a fallback#

Azul is single-region. For production, pair it with a paid provider. viem has a built-in fallback transport: the first URL wins, and the second takes over on error or timeout.

viemclient.ts
import { createPublicClient, http, fallback } from 'viem';import { base } from 'viem/chains';export const client = createPublicClient({  chain: base,  transport: fallback([    http('https://rpc.baseazul.dev'),                  // Azul, primary, fast, no logging    http('https://mainnet.base.org'),       // Coinbase default, last resort  ], { retryCount: 1, retryDelay: 100 }),});

Subscribe to new blocks#

Don’t poll eth_blockNumber in a tight loop. WebSocket subscriptions are Edge early access. If your workload depends on live tip-follow, request a key and we’ll bring you in. Here is the shape it takes:

viem
import { createPublicClient, webSocket } from 'viem';import { base } from 'viem/chains';const client = createPublicClient({  chain: base,  transport: webSocket('wss://rpc.baseazul.dev/ws?api_key=YOUR_KEY'),});const unwatch = client.watchBlockNumber({  onBlockNumber: (n) => console.log('new tip', n),});

Page through eth_getLogs#

Chunk wide ranges into 5,000-block windows and parallelize. Concurrent batches are accepted up to your per-IP cap.

js
async function getLogsRange(client, address, fromBlock, toBlock) {  const PAGE = 5000;  const tasks = [];  for (let start = fromBlock; start <= toBlock; start += PAGE) {    const end = Math.min(start + PAGE - 1, toBlock);    tasks.push(client.getLogs({ address, fromBlock: start, toBlock: end }));  }  return (await Promise.all(tasks)).flat();}

CORS#

The endpoint sets Access-Control-Allow-Origin: *. Browser fetch works from any origin. Preflight (OPTIONS) responds with 204 and a 24-hour max-age.

Routing model#

Azul picks a propagation path to the Base sequencer for every request, chosen for low latency and reliable inclusion. Reads and writes use separate paths, each tuned to its own workload.

The result is fast inclusion and failover that holds up at one transaction or a thousand.

Sandwich resistance#

Sandwich attacks skim value from your trades. They need a window: a pending interval during which a searcher can see your transaction and wedge their own ahead of it.

Azul minimizes that window. Transactions are not parked anywhere inspectable for longer than necessary; the routing prioritizes getting them included over making them visible.

Caveat
Nothing protects against a sequencer-internal observer. Anti-sandwich here means “minimize the public surface where someone can see your tx and react.”

What we don't log#

The RPC path retains nothing per-request, no IPs, request bodies, response bodies, or cross-request identifiers. The only counter is anonymous aggregate bytes/day for the egress cap, which resets daily and isn’t attributed to any IP or request. Full posture statement: /privacy.