> For the complete documentation index, see [llms.txt](https://docs.frayme.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.frayme.ai/api-reference/rate-limits.md).

# Rate limits

Two independent ceilings govern every key: a per-minute burst limit and a monthly generation quota — each with its own 429 code.

## The two ceilings

| Ceiling           | Scope         | Measured over                                 | On breach                                 |
| ----------------- | ------------- | --------------------------------------------- | ----------------------------------------- |
| **Burst limit**   | Per API key   | Sliding 60-second window, requests per minute | `429 RATE_LIMITED` + `Retry-After` header |
| **Monthly quota** | Per workspace | Billing cycle, validated generations          | `429 QUOTA_EXCEEDED`, no `Retry-After`    |

They fail differently on purpose: a burst breach is a *pause*, a quota breach is a *stop*. Branch on `error.code` — see [the two 429s](/api-reference/errors.md#the-two-429s).

## Burst limits

Every authenticated request counts against your key's per-minute limit — including malformed requests, failed composes, and [idempotent replays](/api-reference/idempotency.md). The limit is enforced before anything else touches the pipeline, so a retry loop gone wrong is throttled instead of amplified.

The limit is plan-specific. Your key's live value is `plan.rateLimitPerMin` on [`GET /v1/me`](/api-reference/me-and-health.md):

```bash
curl https://api.frayme.ai/v1/me -H "Authorization: Bearer $FRAYME_API_KEY"
# → "plan": { "rateLimitPerMin": 120, … }
```

On breach, the response carries a `Retry-After` header in seconds. Honor it exactly — the window slides, so retrying earlier just extends the wait:

```ts
import { RateLimitError } from '@frayme/api';

try {
  await frayme.compose.create({ prompt });
} catch (err) {
  if (err instanceof RateLimitError) {
    await sleep((err.retryAfter ?? 5) * 1000); // parsed from Retry-After
    // retry — safe: the SDK's idempotency key means no double-billing
  } else {
    throw err;
  }
}
```

## Monthly generation quotas

Each plan includes a fixed number of generations per month. Caps are **hard** — there is no overage billing, and no surprise invoice: when the quota is spent, compose returns `429 QUOTA_EXCEEDED` until the cycle resets or you upgrade.

| Plan       | Price / mo | Generations / mo |
| ---------- | ---------- | ---------------- |
| Free       | $0         | 500              |
| Hobby      | $9         | 3,000            |
| Starter    | $29        | 12,000           |
| Pro        | $99        | 60,000           |
| Scale      | $299       | 200,000          |
| Enterprise | Custom     | Unlimited        |

All plans share the same features and burst behavior — the only lever is volume. Full plan details are on the [pricing page](/resources/pricing.md).

### What consumes quota

Exactly one thing: a **validated success** — a compose that ends in `compose.completed` (or a `200` envelope). Everything else is free:

* Failed composes (`error` events, `COMPOSITION_FAILED`, `MODEL_UNAVAILABLE`) — never billed
* [Idempotent replays](/api-reference/idempotency.md) — the original generation was billed once; replays are free
* `/v1/me` and `/v1/health` calls — never billed (but `/v1/me` does count against the burst limit)

So `generationsRemaining` only ever moves when you actually received a validated spec.

## Monitoring your quota

Poll [`GET /v1/me`](/api-reference/me-and-health.md) and alert before you hit the wall:

```ts
const me = await frayme.me();
if (me.plan.generationsRemaining < me.plan.monthlyGenerations * 0.1) {
  alertOps(`Frayme quota low: ${me.plan.generationsRemaining} left`);
}
```

Poll on a schedule (once a minute is plenty), not per request — `/v1/me` shares the key's burst limit.

{% hint style="info" %}
`QUOTA_EXCEEDED` is **not retryable** within the cycle. A backoff loop that treats it like `RATE_LIMITED` will hammer the API pointlessly for the rest of the month — branch on the code.
{% endhint %}

## Related

* [Errors](/api-reference/errors.md) — the full taxonomy, including both 429s
* [Idempotency](/api-reference/idempotency.md) — why retries can never double-bill
* [Pricing](/resources/pricing.md) — the plans behind the quotas


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.frayme.ai/api-reference/rate-limits.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
