Digitalyser.io for developers
Guides

Limits and quotas

Two different mechanisms answer 429. Telling them apart is the whole page.

A rate limit and a quota both return 429, and they mean opposite things. One resets within the minute; the other resets with the calendar month. Retrying is right for the first and useless for the second — the reason field tells you which one you hit.

The two mechanisms

Rate limitQuota
reasonrate_limitedquota_exceeded
Countsrequestswork performed — audits run, strategies regenerated
Window60 seconds, fixedthe calendar month, UTC
Resetswithin the minuteon the first of the month
Retrying helpsyes, after Retry-Afterno

Rate limit

Two counters run at once, and both must pass.

Per credential — 60 requests a minute by default. This is the number attached to your token; a plan can raise it.

Per project — 120 requests a minute, whatever the number of tokens. It exists so several credentials pointing at the same project cannot saturate it between them. An agency running one token per client is not affected; a single client running four tokens can be.

The response tells you where you stand

Every authenticated call carries the state of both counters, following the IETF RateLimit header fields:

RateLimit-Policy: "credential";q=60;w=60, "project";q=120;w=60
RateLimit:        "credential";r=42;t=17, "project";r=98;t=17

q is the quota, w the window in seconds, r what remains, t the seconds until the counter resets. r never goes below zero — exhausted reads as r=0, not as a negative number.

The quota shown is yours. It is the limit carried by the credential you presented, not the ceiling of some other plan.

On a refusal

HTTP/2 429
Retry-After: 17
Content-Type: application/problem+json

{ "type": "…/reference/errors#rate_limited", "status": 429, "reason": "rate_limited" }

Wait for Retry-After, do not guess. Guessing goes wrong in both directions: too short and you walk straight back into the wall, too long and you give up throughput you were entitled to.

Retry-After is the header to branch on. The RateLimit fields are still an IETF draft; this one has been a standard HTTP header from the start, and every client library already understands it.

The counter is shared across transports

The same credential is counted once, whether it calls through MCP or through /v1. Splitting your traffic between the two buys you nothing — which is deliberate: the limit belongs to the credential, not to the door it walks through.

Quota

A quota counts work performed, not requests. Reading is never quota'd; only the capabilities that start real work are.

Two of them carry a quota today:

  • audit.run.request — running an audit
  • editorial.strategy.regenerate.request — regenerating the editorial strategy

Both are marked writesOnInvoke in the catalogue: invoking them already starts the work and consumes the quota. There is no dry run. Ask before calling one, not after.

The counter is per project and resets at the start of each calendar month, UTC. The ceiling comes from the plan.

Ceilings by plan

These are the ceilings the API and MCP enforce today, generated from the platform configuration — not typed by hand. A 0 means the plan does not include that capability.

LimitFreemiumSoloSmall BusinessEntreprise & Agence
Active projects
projects
11530
Audit runs started by an agent
runs per calendar month, UTC
2520Unlimited
Strategy regenerations by an agent
regenerations per calendar month, UTC
2510Unlimited
Topic-idea generations by an agent
generations per calendar month, UTC
2510Unlimited
Content generations
generations per calendar month, UTC
0520Unlimited
Content patches
patches per calendar month, UTC
030120Unlimited
Chat conversation tokens
tokens per calendar month, UTC
00400,000Unlimited

There is no header for the remaining quota. Rate limits are observable before you hit them; quotas are not — you learn the ceiling by reaching it, or from the plan. If your integration needs to know in advance, say so: it is a gap we know about, not an oversight.

Telling them apart in code

Branch on reason, never on the status code alone:

if (res.status === 429) {
  const problem = await res.json();

  if (problem.reason === 'rate_limited') {
    // Transient. Wait exactly what the server asked for.
    await sleep(Number(res.headers.get('Retry-After')) * 1000);
    return retry();
  }

  // quota_exceeded — retrying this month changes nothing.
  return surfaceToUser(problem);
}

Treating quota_exceeded as transient produces a client that retries until the first of the month. That is the failure this page exists to prevent.

Was this page helpful?

On this page