Rate limits

Per organization, per minute, and published rather than discovered as a random failure.

Policy

DimensionValue
ScopePer organization (api:{org_id})
WindowFixed 1-minute window
Applies toAll partner API requests after a valid token resolves and entitlement passes

Per-plan ceiling (resolved server-side by org_api_rate_limit()):

PlanRequests / minute
Freelancer Solo, legacy solo60
Freelancer Studio, legacy studio*, Change Practitioner120
Freelancer Agency, Change Consultant / Firm / Team300

Every paid tier has API access — migration 20261112000600 moved api_access off the

top tier. The differentiator is volume, not access, which is why this table exists

and why the numbers are published rather than discovered as random failure.

All endpoints share one org bucket: /api-list-clients, /api-create-deliverable,

/api-ingest-request, and every /api-v1/* route.

Response headers

Successful /api-v1/* responses carry the IETF RateLimit fields, in both the

current and the superseded shape:

RateLimit-Policy: "api";q=300;w=60
RateLimit: "api";r=287;t=41
RateLimit-Limit: 300
RateLimit-Remaining: 287
RateLimit-Reset: 41
X-Stria-Api-Version: v1
  • RateLimit-Policy and the single-line RateLimit field are the current shape in

draft-ietf-httpapi-ratelimit-headers: q is the quota, w the window in

seconds, r the quota remaining, t the seconds until the window resets.

  • RateLimit-Limit, -Remaining and -Reset are the superseded fields. They are

still sent because they are published here and clients depend on them; they will

be retired on a documented Sunset date, not silently.

r and t are measured, read off the counter the request just incremented, not

estimated. They were previously omitted for a good reason — enforce_rate_limit

raises on breach and reports nothing, so any figure would have been invented — and

that reason no longer applies since consume_rate_limit returns the counter it

writes.

Behaviour

  1. Token is validated (format + hash + not revoked).
  2. api_access entitlement is checked.
  3. consume_api_rate_limit(org_id) resolves the plan ceiling and spends one

request against it in a single call.

  1. Over the ceiling within the window → 429 with Retry-After set to the

measured seconds remaining in the window.

Step 3 is one call on purpose. Until migration 20261115000100 the ceiling was

resolved in one place and enforced in another: every request was throttled at the

flat 60 while the response advertised the org's plan ceiling, so a Studio or Agency

subscriber received RateLimit-Limit: 120 or 300 and 429s at 60. Resolving and

spending in the same statement makes the number enforced and the number published

the same number by construction.

Failed auth (401) does not consume quota — the limiter runs only after a successful

token lookup and entitlement check, so an invalid token cannot exhaust another org's

budget.

429 response

{
  "error": "Rate limit exceeded. Please wait a few minutes and try again.",
  "code": "rate_limit_exceeded",
  "retry_after_seconds": 41,
  "limit": 300
}

retry_after_seconds matches the Retry-After header and both carry the real time

left in the window. Back off by that value plus jitter.

Client guidance

  • Treat 429 as temporary. Back off at least Retry-After seconds, plus jitter.
  • For a nightly reconciliation, prefer updated_since with a large limit over many

small requests — one page of 200 costs one request.

  • For Figma batch export, serialise uploads and stay well under the ceiling.

Operational notes

  • Counters live in rate_limit_counters, purged by the stria-purge-rate-limits cron

job.

  • Edge Functions call enforce_rate_limit as service_role.
  • Ceilings may be raised without notice. A reduction is a breaking change and will

appear in changelog.md with notice, per

compatibility.md.

More reference

  • Authentication — How tokens are minted, stored, presented and revoked.
  • Endpoints — Every request and response shape, with worked examples.
  • Outbound webhooks — Signed HTTPS POSTs when work is signed off, a change order moves, or an invoice is paid — plus how to verify one and how to survive a duplicate.
  • Errors — What each status means, what the body carries, and which ones to retry.
  • Compatibility and versioning — What may change without notice, what may not, and how you are told.
  • API changelog — Every change, dated, including the ones that were corrections.

OpenAPI specification · API access is on every paid plan · Start your 14-day trial