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

# Credits and budget

> Read the balance, the amount held by requests in flight, the period spend and the spend cap behind a workspace API key, before a request is refused for credits.

```http theme={"dark"}
GET https://api.runinfra.ai/v1/credits
```

RunInfra is prepaid. Every request is settled against a credit balance, and a request that cannot be covered is refused with `402 insufficient_credits`. This endpoint reads that balance up front, so an agent can pre-flight a long run, a gateway can decide when to top up, and a dashboard of your own can show the number without scraping a 402.

It is read-only and charges nothing. It is authenticated and rate-limited like every other endpoint, and the response is never served from an HTTP cache.

## Request

<RequestExample>
  ```bash cURL theme={"dark"}
  curl https://api.runinfra.ai/v1/credits \
    -H "Authorization: Bearer $RUNINFRA_GATEWAY_KEY"
  ```

  ```python Python theme={"dark"}
  import os
  import requests

  credits = requests.get(
      "https://api.runinfra.ai/v1/credits",
      headers={"Authorization": f"Bearer {os.environ['RUNINFRA_GATEWAY_KEY']}"},
      timeout=10,
  ).json()

  if credits["available_cents"] < 500:
      print("under $5.00 available, top up before the next run")
  ```

  ```typescript TypeScript theme={"dark"}
  const res = await fetch("https://api.runinfra.ai/v1/credits", {
    headers: { Authorization: `Bearer ${process.env.RUNINFRA_GATEWAY_KEY}` },
  });
  const credits = await res.json();

  if (credits.available_cents < 500) {
    console.log("under $5.00 available, top up before the next run");
  }
  ```
</RequestExample>

The OpenAI SDKs have no credits method, so call the endpoint with your HTTP client. The same key you use for inference works here, as long as it is a workspace key (see [Who can read it](#who-can-read-it)).

## Response

```json theme={"dark"}
{
  "object": "credits",
  "balance_cents": 5476,
  "held_cents": 34,
  "available_cents": 5476,
  "currency": "usd",
  "period": {
    "start": "2026-08-10T00:00:00.000Z",
    "spent_cents": 1500
  },
  "spend_cap": {
    "limit_cents": 20000,
    "hard": false,
    "used_cents": 1620,
    "remaining_cents": 18380,
    "gates_inference": false
  },
  "plan_tier": "pro",
  "as_of": "2026-08-27T10:41:12.318Z"
}
```

| Field                       | Type            | Value                                                                                                                                                                                                                                                                            |
| --------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `object`                    | string          | `credits`.                                                                                                                                                                                                                                                                       |
| `balance_cents`             | integer         | The stored ledger balance of the workspace, in US cents. Holds for requests in flight are already taken out of it. Can be negative when the workspace owes for usage that settled after the balance ran out. The same number a `402` reports as `details.current_balance_cents`. |
| `held_cents`                | integer         | Cents reserved by requests currently in flight. Informational: they are already inside `balance_cents`, so do not subtract them again. Each hold is refunded or recognised as spend when its request settles.                                                                    |
| `available_cents`           | integer         | `balance_cents` floored at zero. The amount a new request can be settled against right now.                                                                                                                                                                                      |
| `currency`                  | string          | Always `usd`.                                                                                                                                                                                                                                                                    |
| `period.start`              | string          | ISO 8601 start of the current billing period: your subscription period while the subscription is active or trialing, otherwise the calendar month in UTC.                                                                                                                        |
| `period.spent_cents`        | integer         | Settled spend in the current period. Only settled charges count; an open hold is not spend yet.                                                                                                                                                                                  |
| `spend_cap.limit_cents`     | integer or null | The monthly spend cap configured in Settings, or `null` when none is set. `null` means no cap, never zero.                                                                                                                                                                       |
| `spend_cap.hard`            | boolean         | `true` when reaching the cap refuses new work, `false` when it only sends alerts. Always `false` when there is no cap.                                                                                                                                                           |
| `spend_cap.used_cents`      | integer or null | What the cap is measured against: settled spend plus the declared maximum of any open benchmark or dual-run authorisation. `null` when there is no cap.                                                                                                                          |
| `spend_cap.remaining_cents` | integer or null | `limit_cents` minus `used_cents`, floored at zero, or `null` when there is no cap.                                                                                                                                                                                               |
| `spend_cap.gates_inference` | boolean         | Always `false`. The cap gates new optimization, benchmark and deployment work only; an inference request is never refused because of it. Stated explicitly so a pre-flight check does not refuse its own request for the wrong reason.                                           |
| `plan_tier`                 | string          | The entitled workspace tier, the same value the `X-RateLimit-Tier` header carries.                                                                                                                                                                                               |
| `as_of`                     | string          | ISO 8601 time the numbers were read.                                                                                                                                                                                                                                             |

Every amount is an integer in US cents. Nothing here is a dollar float, because the ledger stores cents and a float would be the first rounding in the chain.

## What decides whether a request runs

An inference request is admitted against `available_cents`. Holds for requests already in flight are already taken out of that number, so a long request on a thin balance can show a small `available_cents` and a positive `held_cents` at the same time; when the request settles, the unused part of its hold returns to the balance.

The spend cap does not enter this decision. It bounds new optimization, benchmark and deployment work, never inference, which is why `gates_inference` is always `false`.

A refused request answers `402` with `code: "insufficient_credits"` and `details.current_balance_cents`, the same balance this endpoint reports, plus a `topup_url`. See [Errors](/docs/api-reference/errors).

## Who can read it

A workspace API key: the ordinary key created in Settings with no deployment or endpoint scope. A key that can spend the balance can read it, because the number is already disclosed to that key inside every `402` it earns.

A key scoped to a single deployment or endpoint is refused with `403 credits_scope_violation`. Those keys are the shape you hand to your own customers, and they do not get your workspace's finances. Adding funds, changing the plan and reading invoices stay with the account owner in the dashboard; there is no write surface here.

## Freshness and limits

The response carries `Cache-Control: no-store` and is read from the ledger on every call. Use `as_of` rather than your own clock when you compare two reads.

Every read costs several ledger statements, so on top of your plan's [per-key rate limit](/docs/api-reference/rate-limits) this endpoint has its own allowance of 60 reads a minute per key. Poll it when you need a decision, not on a timer. The `x-ratelimit-*` headers on the response describe the plan bucket, so a boot-time read here also tells you your remaining request quota.

## Errors

| Status | `error.code`              | Meaning                                                                        |
| ------ | ------------------------- | ------------------------------------------------------------------------------ |
| `401`  | `invalid_api_key`         | The key is missing, malformed, revoked or expired.                             |
| `403`  | `credits_scope_violation` | The key is scoped to a deployment or endpoint. Use a workspace key.            |
| `405`  | `method_not_allowed`      | Only `GET` is served. The `Allow` header says so.                              |
| `429`  | `rate_limit_exceeded`     | The plan bucket or the 60-a-minute endpoint bucket. Honour `Retry-After`.      |
| `503`  | `limiter_unavailable`     | Rate limiting could not be evaluated. Retry shortly; nothing was charged.      |
| `503`  | `service_unavailable`     | The ledger could not be read. Retry with backoff; the balance was not changed. |
