# RPC Batching and Retries

> Reduce RPC calls and retry temporary failures without duplicate work.

Canonical: https://nolimitnodes.com/docs/guides/rpc-batching-retries

Batch independent reads. Retry only temporary failures.

## Send a batch

A JSON-RPC batch is an array of request objects. Give each request a unique ID.

```javascript
const requests = [
  {
    jsonrpc: "2.0",
    id: "slot",
    method: "getSlot",
    params: [{ commitment: "confirmed" }],
  },
  {
    jsonrpc: "2.0",
    id: "health",
    method: "getHealth",
  },
  {
    jsonrpc: "2.0",
    id: "epoch",
    method: "getEpochInfo",
    params: [{ commitment: "confirmed" }],
  },
]

const response = await fetch("https://rpc.nln.clr3.org", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.NLN_API_KEY,
  },
  body: JSON.stringify(requests),
})

const results = await response.json()
const byId = Object.fromEntries(results.map((item) => [item.id, item]))
console.log(byId.slot.result)
```

Response order does not need to match request order. Match each result by ID.

## Pick retryable failures

Retry these responses:

- HTTP `429`
- HTTP `500`
- HTTP `502`
- HTTP `503`
- HTTP `504`
- Network timeout
- Connection reset before a response

Do not retry these responses without changing the request:

- HTTP `400`
- HTTP `401`
- JSON-RPC `-32601`
- JSON-RPC `-32602`

## Use bounded backoff

```javascript
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

async function rpc(body, attempt = 0) {
  const response = await fetch("https://rpc.nln.clr3.org", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": process.env.NLN_API_KEY,
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(10_000),
  })

  if ([429, 500, 502, 503, 504].includes(response.status) && attempt < 4) {
    const base = 250 * 2 ** attempt
    const jitter = Math.floor(Math.random() * 150)
    await wait(base + jitter)
    return rpc(body, attempt + 1)
  }

  const result = await response.json()
  if (!response.ok || result.error) {
    throw new Error(result.error?.message ?? `HTTP ${response.status}`)
  }
  return result.result
}
```

## Protect transaction submission

Treat `sendTransaction` differently from reads.

- Keep the same signed transaction across retries.
- Query the signature before a second submission.
- Stop after the blockhash expires.
- Do not build a new transaction inside the retry loop.
