# Credits and billing

> What a credit is, how a request is charged, what a hold is, and what happens when your credits run low.

Source: https://router.africa/docs/credits-and-billing

You pay for requests with **credits**. A credit is a unit of usage, like a token. Credits are shown as a plain number, with no currency, and they are used only for requests. They can't be withdrawn or moved to another account.

Credits belong to your workspace. You see your balance, and every request, in the dashboard.

## How a request is charged

Charges are based on the tokens the request used, multiplied by the model's published rate. The rate is locked when the request starts, so a later rate change doesn't affect a request already running.

1. **Hold.** Before the request runs, router.africa holds the credits it may need. The hold depends on the model's rate and on `max_tokens`.
2. **Run.** The request goes to the model.
3. **Settle.** You are charged for the input and output tokens actually used. The rest of the hold is released.

If a request fails before it finishes, the hold is released and you are not charged for it.

> **Tip:** Set `max_tokens` to what you need. If you leave it out, router.africa uses a default for the model. If you set it above the model's limit, it is lowered to that limit. A smaller `max_tokens` means a smaller hold.

A request can use slightly more than was held. You are charged the full amount and your balance stops at zero. The next request is then refused until you add credits.

## When credits run low

Available credits are your balance minus what is on hold. When they are too low for a request, the API returns `402` with the reason `insufficient_credits`, and the model is not called.

**curl**

```bash
curl -i 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"}]}'

# HTTP/2 402
# {"error":{"code":402,"message":"This workspace doesn't have enough credits for this request. Add credits at https://app.router.africa.","metadata":{"error_type":"payment_required","reason":"insufficient_credits"}}}
```

**Python**

```python
import os

from openai import OpenAI

client = OpenAI(base_url="https://api.router.africa/v1", api_key=os.environ["ROUTER_AFRICA_API_KEY"])

try:
    client.chat.completions.create(
        model="openai/gpt-4o-mini",
        messages=[{"role": "user", "content": "Hello"}],
    )
except openai.APIStatusError as e:
    if e.status_code == 402 and e.body["metadata"]["reason"] == "insufficient_credits":
        print("Add credits in the dashboard, then try again.")
    else:
        raise
```

**TypeScript**

```typescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.router.africa/v1",
  apiKey: process.env.ROUTER_AFRICA_API_KEY,
});

try {
  await client.chat.completions.create({
    model: "openai/gpt-4o-mini",
    messages: [{ role: "user", content: "Hello" }],
  });
} catch (err) {
  if (err instanceof OpenAI.APIError && err.status === 402) {
    console.log("Add credits in the dashboard, then try again.");
  } else {
    throw err;
  }
}
```

The SDKs don't retry a `402`. Don't retry it yourself until you have added credits.

A key can also reach its own spend cap. That returns `402` with a different reason. See [Spend caps and key scoping](https://router.africa/docs/spend-caps-and-key-scoping).

## Adding credits

Add credits in the dashboard. The fee for a top-up is shown before you pay.

{/* TODO: describe accepted payment methods and top-up limits once the Niobi top-up flow is final (P3 top-ups). */}
{/* TODO: low-balance email alerts exist in the plan; document thresholds once the dashboard setting is built. */}
