Rate-limit & quota headers
Every response from the OSYRA edge gateway on a rate-limited route carries a set of
rate-limit response headers. They tell a client how much of its allowance is left
and when the window resets, so a well-behaved client can pace itself before it is
rejected rather than reacting to a 429 after the fact.
This page documents the header contract: the exact header names, what each value means, and how to read them. For the machine-readable error codes returned when you do exceed a limit, see the Error-Code Catalog. For how plan tiers and quotas behave in plain language, see the billing & quotas FAQ.
The headers
The gateway emits two parallel families of the same three values, plus a combined
header and (on rejection) a Retry-After. Both families are populated on every
rate-limited response — successful or rejected — so a client can read whichever family
it already supports.
| Header | Family | Value | Meaning |
| --- | --- | --- | --- |
| RateLimit-Limit | RFC 9333 (standards-track) | integer | Maximum requests permitted in the current window. |
| RateLimit-Remaining | RFC 9333 (standards-track) | integer | Requests still permitted in the current window (never below 0). |
| RateLimit-Reset | RFC 9333 (standards-track) | integer (seconds) | Seconds until the current window resets and RateLimit-Remaining is replenished. |
| RateLimit | RFC 9333 (combined) | structured | A single combined form: limit=<n>, remaining=<n>, reset=<seconds>. |
| X-RateLimit-Limit | Legacy (de-facto) | integer | Same value as RateLimit-Limit. |
| X-RateLimit-Remaining | Legacy (de-facto) | integer | Same value as RateLimit-Remaining. |
| X-RateLimit-Reset | Legacy (de-facto) | integer (seconds) | Same value as RateLimit-Reset. |
| Retry-After | RFC 9110 | integer (seconds) | Sent on a 429 rejection: seconds to wait before retrying. |
The header names above are pinned in the cross-service compatibility contract
(testdata/contracts/expected_values.json, http_headers.rate_limit) so that the Go
gateway and the Java services agree on the exact strings, and they are emitted by the
gateway's setRateLimitHeaders routine.
RFC 9333 vs. the legacy X-RateLimit-* family
RFC 9333 (RateLimit header fields for
HTTP) standardizes the un-prefixed RateLimit-Limit / RateLimit-Remaining /
RateLimit-Reset names. The X--prefixed names are the older de-facto convention that
many clients and SDKs still read.
OSYRA emits both families with identical values as a compatibility bridge:
- Prefer the standards-track
RateLimit-*names in new integrations. - The
X-RateLimit-*names are kept for clients that predate RFC 9333; they are not deprecated, but they may be retired in a future major version once the ecosystem has fully moved over. Do not depend on theX-family if you can read the un-prefixed one.
Reading the headers
A typical successful response on a rate-limited route:
HTTP/1.1 200 OK
RateLimit-Limit: 1000
RateLimit-Remaining: 998
RateLimit-Reset: 57
RateLimit: limit=1000, remaining=998, reset=57
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 57Read it as: the window allows 1000 requests; 998 remain; the window resets in 57 seconds.
When the allowance is exhausted, the gateway rejects with 429 Too Many Requests,
sets RateLimit-Remaining: 0, and adds Retry-After:
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 1000
RateLimit-Remaining: 0
RateLimit-Reset: 12
RateLimit: limit=1000, remaining=0, reset=12
Retry-After: 12The response body carries the structured error code — OSY-RATE-2001 for a generic
rate-limit rejection, or an OSY-RATE-201x quota code when you hit a plan quota rather
than a request-rate window. See the
OSY-RATE catalog
for the full list and the gRPC RESOURCE_EXHAUSTED mapping.
Recommended client behavior
- Back off on
429. HonorRetry-After(seconds). If it is absent, fall back toRateLimit-Reset. Add jitter so concurrent clients do not retry in lockstep. - Pace proactively. When
RateLimit-Remainingapproaches0, slow down before you are rejected instead of retrying through a storm of429s. - Treat
Resetas relative. Wake up at now + reset seconds; never parse it as a Unix timestamp. - Prefer the standards-track family. Read
RateLimit-*; only readX-RateLimit-*if your client cannot yet read the un-prefixed names.