> ## Documentation Index
> Fetch the complete documentation index at: https://docs.walletlink.social/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits

> Three windows, the headers that report them, and how to back off correctly.

Limits apply in three windows at once: per minute, per day, and per month. Exceeding any one of them returns `429`, even if the other two have room.

See [plans](/api-reference/introduction#plans) for your request limits; which preset serves you follows the largest unexpired pack the account holds. Rate limits are counted in their own units, not in match credits: a 50-wallet batch consumes 50 of your per-minute allowance while billing only the wallets that resolved, and a free [estimate](/api-reference/estimate) weighs identically to the batch it previews. A [job](/api-reference/jobs) submission weighs one unit however long its list, and its status poll weighs nothing. [Credits](/api-reference/introduction#credits) shows both meters side by side.

## Headers

Every response carries the current state:

| Header                  | Meaning                                                              |
| ----------------------- | -------------------------------------------------------------------- |
| `X-RateLimit-Limit`     | Rate-limit units allowed in the current window.                      |
| `X-RateLimit-Remaining` | Units left in it.                                                    |
| `X-RateLimit-Reset`     | When the window resets, as a **Unix timestamp in seconds**.          |
| `X-Matches-Available`   | Match credits available **before** this call’s matches were debited. |

`X-RateLimit-Reset` is seconds since the epoch, not seconds from now. Convert it rather than treating it as a duration.

`X-Matches-Available` is the balance the request was admitted with, not the balance it left behind: what a call costs is not known until it resolves, so subtract the matches the response reports to know what remains. It rides on success responses, on `402` (where it reads `0`) and on `429`; it is absent on `401`, where no account was identified, and for the legacy unmetered accounts, which have no balance to report.

## Handling 429

```json theme={null}
{
  "error": "Rate limit exceeded. Try again in 42 seconds",
  "code": "RATE_LIMIT_EXCEEDED"
}
```

The response also carries the rate limit headers, so read `X-RateLimit-Reset` to know exactly when to retry.

<Warning>
  Retry with exponential backoff and jitter. Retrying immediately on a shared
  minute boundary is how a fleet of workers turns one 429 into a synchronized
  stampede that keeps every one of them limited.
</Warning>

A worked example of the whole loop:

```js theme={null}
async function lookup(wallets, key) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch('https://walletlink.social/api/v1/batch', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${key}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ wallets }),
    });

    if (res.status !== 429) return res.json();

    // Header is epoch seconds, not a duration.
    const resetAt = Number(res.headers.get('X-RateLimit-Reset')) * 1000;
    const wait = Math.max(resetAt - Date.now(), 1000) + Math.random() * 1000;
    await new Promise((r) => setTimeout(r, wait));
  }
  throw new Error('rate limited after 5 attempts');
}
```

## Staying under the limit

Batch aggressively. One 50-wallet batch and 50 single lookups consume the same 50 rate-limit units and bill the same matches, but the batch is one round trip instead of fifty, and it is far faster.

Deduplicate before submitting. Duplicates are not billed, because billing is on matches after deduplication, but they do consume your per-minute rate limit, so sending them costs you throughput rather than credits.

Cache your own results. The underlying records change on the order of days, not seconds. Re-resolving a wallet that already matched spends a match credit for information you already hold. Re-resolving one that missed costs nothing, but it still spends rate limit.

Check [`/v1/usage`](/api-reference/usage) rather than guessing. It reports all three windows and your match balance, and costs nothing.
