# Rate limits

> How rate limiting behaves, what a 429 looks like, and how to back off.

Source: https://router.africa/docs/rate-limits

router.africa limits how fast each API key can send requests. The limits are set for your organization by router.africa and aren't published here. This page describes what you see when you hit one.

## What a limit looks like

You get `429` with `Retry-After`, the number of seconds to wait before you try again. `Retry-After` is always sent on `429`, `503` and `529`. If no other value applies, it is 5 seconds. Rate-limit headers such as `x-ratelimit-remaining` are not sent, so you can't read your remaining quota from a response.

| `reason` | Cause |
|---|---|
| `rate_limited` | The key reached its rate limit. |
| `concurrency_limited` | Your account has too many requests running at once. Wait for some to finish, then retry. |
| `edge_rate_limited` | Too many requests came from one IP address, or one address sent too many rejected keys in a minute. |

A request that hits a limit is not charged, and any credits held for it are released. See [Errors](https://router.africa/docs/errors) for the response bodies.

## Back off and retry

Wait for `Retry-After`, then retry. If a request fails again, wait longer each time and add a little random delay, so many clients don't all retry at the same moment. Send retries with an [`Idempotency-Key`](https://router.africa/docs/idempotency-and-retries) so a retry can't run twice.

**curl**

```bash
# curl waits for Retry-After on a 429 before it retries
curl --retry 4 https://api.router.africa/v1/chat/completions \
  -H "Authorization: Bearer $ROUTER_AFRICA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hello"}]}'
```

**Python**

```python
import os

from openai import OpenAI

# The SDK already retries 429 with backoff. max_retries sets how many times.
client = OpenAI(
    base_url="https://api.router.africa/v1",
    api_key=os.environ["ROUTER_AFRICA_API_KEY"],
    max_retries=4,
)

try:
    client.chat.completions.create(
        model="openai/gpt-4o-mini",
        messages=[{"role": "user", "content": "Hello"}],
    )
except openai.RateLimitError as e:
    wait = int(e.response.headers.get("retry-after", "5"))
    print(f"Still limited after retries. Try again in {wait} seconds.")
    time.sleep(wait)
```

**TypeScript**

```typescript
import OpenAI from "openai";

// The SDK already retries 429 with backoff. maxRetries sets how many times.
const client = new OpenAI({
  baseURL: "https://api.router.africa/v1",
  apiKey: process.env.ROUTER_AFRICA_API_KEY,
  maxRetries: 4,
});

try {
  await client.chat.completions.create({
    model: "openai/gpt-4o-mini",
    messages: [{ role: "user", content: "Hello" }],
  });
} catch (err) {
  if (err instanceof OpenAI.RateLimitError) {
    const wait = Number(err.headers?.["retry-after"] ?? 5);
    console.log(`Still limited after retries. Try again in ${wait} seconds.`);
  }
}
```

> **Tip:** Use a separate key for each app. Limits are applied per key, so one busy app doesn't slow the others.

If you need higher limits, contact support.
