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

# Rate Limits

> Requests are limited per API key and per source IP, whichever is reached first. Exceeding a limit returns 429.

Requests are rate limited **per API key** and **per source IP** — whichever limit is reached first applies.

Exceeding either returns `429`.

## Why two limits

A per-key limit protects the account from one runaway integration. A per-IP limit protects the API from a host running many keys.

<Warning>
  Sharing one outbound IP across several keys means the IP limit can bite before any individual key's limit does. If you run multiple integrations from one host, budget against the IP.
</Warning>

## Handling 429

```json theme={null}
{
  "errors": [
    {
      "message": "Too many requests. Please try again later.",
      "code": "429"
    }
  ]
}
```

The `code` is the string `"429"`. Branch on the HTTP status.

Back off exponentially with jitter, and cap the number of attempts.

```typescript theme={null}
async function withRetry<T>(fn: () => Promise<Response>, attempts = 5): Promise<Response> {
  for (let i = 0; i < attempts; i++) {
    const res = await fn();
    if (res.status !== 429 && res.status < 500) return res;
    const delay = 2 ** i * 1000 + Math.random() * 1000;
    await new Promise((r) => setTimeout(r, delay));
  }
  throw new Error('rate limited: retries exhausted');
}
```

<Warning>
  Retrying immediately on `429` makes it worse. Every rejected request still counts.
</Warning>

## Staying under the limit

<AccordionGroup>
  <Accordion title="Do not poll for payment status">
    `GET /v1/payment-link` is for reconciliation, not for watching a single link. Polling one link in a tight loop is the fastest way to a `429`.

    Poll on a sensible interval — seconds, not milliseconds — and back off when nothing has changed.
  </Accordion>

  <Accordion title="Create links on demand, not in advance">
    A link created when the customer reaches checkout is one request. Pre-generating batches multiplies request volume for links that may never be used.
  </Accordion>

  <Accordion title="Use one key per service">
    Separate keys give each integration its own budget, and let you revoke one without taking down the rest.
  </Accordion>

  <Accordion title="Cache what does not change">
    A created link's `id` and `url` never change. Store them rather than re-reading them.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={3}>
  <Card title="Errors" icon="triangle-alert" href="/api-reference/errors">
    Which codes retry.
  </Card>

  <Card title="Pagination" icon="list-ordered" href="/api-reference/pagination">
    Paging without hammering.
  </Card>

  <Card title="Moove API Keys" icon="key" href="/manage/moove-api-keys">
    One key per service.
  </Card>
</CardGroup>
