Headers, CORS & policy

Response headers

Every header the API sets, what it means, and which ones a browser can read.

On every response

HeaderMeaning
X-Request-IdA 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-TypeAlways application/json; charset=utf-8.

On every metered response

Metered means any authenticated /v1/... call — so everything except /health.

HeaderMeaning
X-RateLimit-LimitYour token bucket's burst capacity (100 on the standard plan).
X-RateLimit-RemainingWhole tokens left after this request.
X-Quota-LimitRequests included in the current window (20,000/month; 1,000 on a trial).
X-Quota-RemainingRequests left in the window, across every key on the account.
X-Quota-ResetWhen 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-After

so 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.

On this page