Response headers
Every header the API sets, what it means, and which ones a browser can read.
On every response
| Header | Meaning |
|---|---|
X-Request-Id | A UUID identifying this request. Log it. It's repeated in error.request_id on failures, and it's what turns a support question into one log lookup. |
Content-Type | Always application/json; charset=utf-8. |
On every metered response
Metered means any authenticated /v1/... call — so everything except /health.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your token bucket's burst capacity (100 on the standard plan). |
X-RateLimit-Remaining | Whole tokens left after this request. |
X-Quota-Limit | Requests included in the current window (20,000/month; 5,000 in total on a trial). |
X-Quota-Remaining | Requests left in the window, across every key on the account. |
X-Quota-Reset | When the window ends — RFC 3339, UTC. Start of next month, or the trial's end. |
On a 429
Retry-After, in seconds. A rate-limit 429 gives a second or two; a quota 429 can give days.
Which one you got is in error.code — see errors.
Always honour Retry-After over your own backoff curve: we know when the bucket refills, your curve
is a guess.
On a per-endpoint trial 429
During a trial each endpoint has its own ceiling (1,000 requests; 20 on /matches/bulk). The 429
that reports it — error.code: "trial_endpoint_quota_exceeded" — carries two more headers:
| Header | Meaning |
|---|---|
X-Quota-Endpoint-Limit | The trial ceiling for the endpoint you called. |
X-Quota-Endpoint-Remaining | Always 0 — the header is only sent once the ceiling is reached. |
They appear on that response only, not on every metered response. Retry-After there runs to the
end of the trial, and your overall allowance still has requests left — another endpoint keeps
working. See rate limits.
Reading them from a browser
Custom headers are hidden from cross-origin fetch unless the server names them. The API sets:
Access-Control-Expose-Headers: X-Request-Id, X-RateLimit-Limit, X-RateLimit-Remaining,
X-Quota-Limit, X-Quota-Remaining, X-Quota-Reset, X-Quota-Endpoint-Limit,
X-Quota-Endpoint-Remaining, Retry-Afterso all nine are readable where CORS applies — including X-Request-Id, which is the one you want
on a failure, since the rate-limit and quota headers are absent on a 401. See
CORS.
Checking your position
curl -sS -D - -o /dev/null \
-H "Authorization: Bearer $ESPORTSODDS_API_KEY" \
"https://api.esportsodds.gg/v1/cs2/matches?limit=1" \
| grep -iE "x-quota|x-ratelimit|x-request-id"X-Quota-Remaining is the one to alarm on in your own monitoring.
What isn't set
No ETag, Last-Modified or conditional-request support, and no Cache-Control. Responses are not
cacheable by an intermediary — cache in your own layer with a TTL you choose, informed by
how often data actually changes.
Compression is negotiated on /v1/{game}/matches/bulk only (Accept-Encoding: gzip), where a page
is large enough for it to matter. Every other response is uncompressed JSON.
There is no X-RateLimit-Reset; the bucket refills continuously rather than at a boundary, so
Retry-After on a 429 is the meaningful signal.