> Source: https://wexa.ai/docs/administration/usage-quota-and-billing

# Usage, quota and billing

Quota is a request ceiling the gateway enforces, and credits are a balance held in the main application. Anyone can read their own consumption; only an owner-tier administrator reaches billing.

1. Open Settings, then Usage & Cost (`/settings/usage`), to see your consumption.
2. If a call fails with `429`, read the message: `for project` and `for organization` name different buckets. Wait for the `Retry-After` header, then retry.
3. For credits, open Settings, then Billing (`/settings/billing`), which only the owner tier can reach.

## Limits

| Limit | Behavior |
|---|---|
| Per project | 120 requests per sliding 60 seconds by default. |
| Per organization | 600 requests per sliding 60 seconds by default, shared by every project. |
| Ceiling `0` | Unlimited; that bucket is not checked. |
| Chat completions | 60 requests a minute per API key, on that endpoint only. |
| Agent limit | Set on your account; at the limit `create-agent` and `create-process-flow` fail with `ValidationError` (400). |

**More details**

### Where quota sits

These are different questions from whether an action is permitted, so quota is checked at stage `S3:rate-quota` of the [lifecycle](/docs/governance/audit-and-lifecycle), before the arguments are looked at and well before the [policy engine](/docs/governance/policy-and-approvals). A permitted call can be refused for want of quota, and a call policy would deny can be refused for quota first.

### How it works

1. **Every governed call consumes one unit** from two buckets at once, the project's and the organization's, before it executes.
2. **Both buckets are counted over a sliding window**: calls in the last sixty seconds, not since a clock minute began, so a burst gets no free reset.
3. **A full bucket refuses the call** with `429` and a `Retry-After` header.
4. **Credits are separate.** They are never read on the governed call path.

| Bucket | Shipped default | Setting |
|---|---|---|
| Per project | **120** requests per window | `QUOTA_PER_PROJECT` |
| Per organization | **600** requests per window | `QUOTA_PER_ORG` |
| Window | 60 seconds | deployment configuration |

A ceiling of `0` means unlimited, and that bucket is not checked at all.

### How usage is counted

- **One governed tool call is one request**, whatever the work. A [`query-context`](/docs/context/query-context) returning one node and one returning fifty count the same. Nothing measures tokens, result size, graph writes or compute.
- **Both buckets are consumed, or neither.** A call refused by the project ceiling spends no organization unit, so one runaway project cannot drain the organization through its refusals.
- **A refused call consumes nothing**, so retrying into a `429` in a tight loop does not push recovery away. Only a call that passes `S3` counts, so a policy denial at `S5` or an approval parked at `S6` has already spent one.
- **A resumed call consumes another unit**, since redemption of a resume token happens after `S3`. Budget gated operations at twice the request count.
- **The surface makes no difference.** The Wexa MCP server, REST and both SDKs hit the same two buckets.

The gateway records the count and ceiling a call saw onto its [trace](/docs/governance/observability), so a run slowed by a nearly-full bucket can be seen to have been.

### Check your consumption

Console: Settings, then Usage & Cost (`/settings/usage`). API: the documentation tool's governance topic returns it, to any caller.

**Read your quota usage**

**Python SDK**

```python
docs = fabric.docs(topic="governance")
print(docs["quota"])
```

**TypeScript SDK**

```ts
const docs = await fabric.docs({ topic: 'governance' })
console.log(docs.quota)
```

**REST API**

```bash
curl -X POST https://fabric.wexa.ai/v1/docs \
  -H "Authorization: Bearer $FABRIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"topic":"governance"}'
```

```json
"quota": { "project_window_used": 114, "project_window_max": 120,
           "org_window_used": 114,     "org_window_max": 600 }
```

### When a ceiling is reached

The call is refused with `429`. This is a real refusal, captured on the 121st call inside the window:

**A quota refusal**

**REST API**

```
HTTP/1.1 429 Too Many Requests
Retry-After: 24

{"error":"S3:rate-quota",
 "error_description":"quota exceeded for project proj_d18 (retry after 24s)",
 "lifecycle_id":"qlc_000126"}
```

**Wexa MCP server**

The refusal arrives as a tool result with `isError` set, so the model can read it and wait. The retry hint is in the text; the `Retry-After` header is on REST only.

```json
{
  "content": [
    { "type": "text",
      "text": "error (S3:rate-quota): quota exceeded for project proj_d18 (retry after 24s)" }
  ],
  "isError": true
}
```

**TypeScript SDK**

```ts
import { QuotaExceeded, isRetryable } from '@wexa-fabric/sdk'

try {
  await fabric.saveContext({ nodes })
} catch (e) {
  if (e instanceof QuotaExceeded) {
    // isRetryable(e) is true; the SDK honours Retry-After when retry is enabled.
  }
}
```

**Python SDK**

```python
from wexa import QuotaExceeded

try:
    fabric.save_context(nodes=nodes)
except QuotaExceeded as e:
    print(e.retry_after)
```

- **The message names the bucket.** `for project …` is one noisy project, `for organization …` is every project at once.
- **`Retry-After` is computed, not constant.** It is the time until the oldest call in the window ages out, the earliest moment the call could succeed. Sampled over one recovery it read `24`, then `6`, then the call succeeded. Both SDKs treat quota exhaustion as retryable and wait that long; it is one of only two error classes either retries, the other being an upstream failure, and the only one either retries a resumed call through. See [errors and retries](/docs/get-started/errors-and-retries).
- **The refusal is audited** as a rejection at stage `S3:rate-quota`, so "we were throttled at 14:05" is checkable for as long as the trail survives. See [audit and compliance](/docs/governance/audit-and-lifecycle#limits-and-behavior).

### Limits and behavior

| Limit | Behavior |
|---|---|
| Project and organization request ceilings | 120 and 600 per sliding 60 seconds by default; the organization ceiling is shared, so at the defaults five projects at full rate exhaust it before any reaches its own |
| Chat completions | The OpenAI-compatible `/v1/chat/completions` allows **60 requests a minute per API key**, in fixed one-minute windows. It applies to that endpoint only, per key rather than per project, on top of the other two |
| Organization agent limit | Set on your organization's account; an agent from `create-agent` counts once, as does a process flow from `create-process-flow` |

When the agent limit is full, both calls fail with a `ValidationError` (400) carrying `Customer has exceeded number of coworkers`. That is a limit, not a bad request, so retrying will not help; ask whoever manages your plan for a higher one.

It is checked when the agent or process flow is created, not at `S3:rate-quota`, and uses no request quota.

To plan against the ceilings:

- **Batch where the tool lets you.** A single [`save-context`](/docs/context/save-context) carrying fifty nodes costs one unit; fifty calls of one node cost fifty.
- **Expect an agent to spend more than its useful calls.** A policy denial or approval gate has still consumed quota and the resume consumes more.
- **Watch the organization ceiling**, not only the project one.

### Where credits live

Credits are a real part of the Wexa product: a balance is held against a workspace, top-ups are purchased, and consumption is reported in the product's billing and utilization views. They are held and spent through the `/credits/*` endpoints rather than any of the four surfaces.

No lifecycle stage consults a balance, no tool call deducts from one and no Wexa surface returns one or refuses a call for want of credits.

- **The balance answers "what did we buy"**, the quota counters "how hard are we calling". Neither answers the other.
- **A drained balance does not stop governed calls**, and exhausted quota does not preserve credit. Do not use one as a safety net for the other.
- **The balance is shown at Settings, then Billing (`/settings/billing`)**, an owner-tier capability. See [members and roles](/docs/administration/members-and-roles).

### Reconcile a bill

Reconciliation is possible but partial. Wexa can corroborate three things:

1. **That calls happened, and which.** Every governed call writes an audit event naming the tool, actor, project and lifecycle identifier, so counting `*.executed` events per project is a defensible usage figure.
2. **That refusals happened.** Rejections at `S3:rate-quota` are recorded separately, so throttled traffic is not counted as served.
3. **Which project each call belonged to**, for reading though not as a cryptographic assurance: the project identifier is outside the audit hash chain.

It cannot supply three others:

1. **Anything predating the last restart.** The audit trail is not durable, so unless you archive it, last month's usage record does not exist. This is the largest obstacle.
2. **A per-call cost.** No cost, token count or credit amount is attached to any event on the governed path.
3. **A tie between a call and a credit movement.** No identifier is shared between the audit trail and the credits ledger.

A workable routine:

1. **Archive `GET /v1/audit` continuously** into your own storage, following the archiving routine in [audit and compliance](/docs/governance/audit-and-lifecycle#limits-and-behavior).
2. **Count `*.executed` events per project per period** as your usage denominator.
3. **Take the credit ledger from the main application** as your cost numerator.
4. **Reconcile at project level, not call level**, and say so. The per-call join does not exist.

### Related

- [Credential management](/docs/administration/credentials): the per-key ceiling and who should hold a key.
- [Lifecycle and audit](/docs/governance/audit-and-lifecycle): where `S3` sits among the stages, in order, and why that decides what is safe to retry.
- [Observability](/docs/governance/observability): reading the usage a call saw and finding the run that exhausted a bucket.
