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; 1,000 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.
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, Retry-Afterso all seven 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.
No compression negotiation either. Responses are 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.