Idempotency and retries
How router.africa protects you from double charges when a request is sent twice, and how to retry safely.
If a connection drops, you can’t tell whether a request ran. Retrying could run it, and charge for it, twice. router.africa protects you in two ways.
Automatic protection
You don’t need to do anything. If the same account sends the exact same request body twice within about 30 seconds, the second request is treated as a duplicate:
- If the first request is still running, the second returns
409with the reasonrequest_in_progress. Wait, then retry. - If the first request has finished, the second returns the stored response of the first. It is not run again and you are not charged again. The response has the header
x-idempotent-replay: true. It means the response is a stored replay and not a new run. The header is absent on a new run.
After the window, an identical request is a new request. This is so that sending the same prompt twice on purpose still works.
The Idempotency-Key header
router.africa adds an optional Idempotency-Key header. It isn’t part of the OpenAI API. Send a unique value for each logical request, such as a UUID, and reuse the same value when you retry it. Requests with the same key are treated as one request for 24 hours.
curl https://api.router.africa/v1/chat/completions \
-H "Authorization: Bearer $ROUTER_AFRICA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2a5e-3b7d-4c0e-9a41-0d2f8e7b1a90" \
-d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hello"}]}'import os
import uuid
from openai import OpenAI
client = OpenAI(base_url="https://api.router.africa/v1", api_key=os.environ["ROUTER_AFRICA_API_KEY"])
key = str(uuid.uuid4()) # create once per logical request, reuse on retries
client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"Idempotency-Key": key},
)import OpenAI from "openai";
import { randomUUID } from "node:crypto";
const client = new OpenAI({
baseURL: "https://api.router.africa/v1",
apiKey: process.env.ROUTER_AFRICA_API_KEY,
});
const key = randomUUID(); // create once per logical request, reuse on retries
await client.chat.completions.create(
{ model: "openai/gpt-4o-mini", messages: [{ role: "user", content: "Hello" }] },
{ headers: { "Idempotency-Key": key } },
);Which errors to retry
| Status | Retry? |
|---|---|
408, 500, 502, 503, 529 |
Yes, with backoff. |
429 |
Yes. Wait for Retry-After. |
409 |
Yes, after a short wait. |
400, 401, 403, 404, 413 |
No. Fix the request first. |
402 |
No. Add credits or raise the key’s cap first. |
The OpenAI SDKs retry connection errors and 408, 409, 429 and 5xx by themselves. Set the number of retries with max_retries (Python) or maxRetries (TypeScript). A request that fails with an error is not charged, and its hold is released.
See Errors and Rate limits.