Budget your requests
20,000 a month is about 28 an hour. Here is the arithmetic, not a reassurance.
Your monthly allowance is 20,000 requests across roughly 720 hours — about 28 requests an hour, for everything. Design against that number; you will almost never reach the per-second rate limit.
The one fact that decides your budget
Fixtures and odds move at completely different speeds. Measured over a recent 7-day window of production data:
| Endpoint | Writes per hour |
|---|---|
/v1/cs2/matches | ~6 |
/v1/cs2/odds | ~665 |
That's roughly 110×. Polling both on the same timer spends most of your allowance re-reading a fixture list that hasn't changed.
Two 30-day plans
| Strategy | Requests/month | Share of plan |
|---|---|---|
| Both endpoints every 5 minutes | 17,280 | 86% |
| Odds every 5 minutes, fixtures hourly | 9,360 | 47% |
Same odds freshness, a bit over half the cost. And note what the first row means honestly: a 5-minute poll on both consumes most of the plan before you make a single detail call.
Work out your own number
requests/month = (60 / minutes_between_polls) × 24 × 30 × endpoints_polledSome anchors:
| Interval | Requests/month (one endpoint) |
|---|---|
| Every minute | 43,200 — over budget on its own |
| Every 5 minutes | 8,640 |
| Every 15 minutes | 2,880 |
| Hourly | 720 |
| Daily | 30 |
Spending less
Open a WebSocket instead of polling for changes. One ticket mint is one request; the connection then pushes score and odds changes for as long as it stays open at no further request cost. For anything change-driven this is strictly cheaper than a timer — see connect a WebSocket.
Poll fixtures slowly. Hourly is ample at ~6 writes/hour; for many uses a daily schedule refresh is fine.
Filter server-side. ?status=live, ?date_from=/?date_to=, ?tournament= cost the same one
request as an unfiltered call and save you paging through rows you'd discard.
Ask for detail only where it exists. ?has=rounds,depth filters the list to matches that
actually have those sub-resources, so you never spend a request discovering there was nothing.
Raise limit. Walking 1,000 teams at limit=100 is 10 requests; at limit=500 it's 2.
Expand instead of following links. ?include=teams,tournament,odds turns four requests into
one — see fetching a full match.
Know where you stand
Every response carries your position, so you never have to count client-side:
curl -sS -D - -o /dev/null -H "Authorization: Bearer $ESPORTSODDS_API_KEY" \
"https://api.esportsodds.gg/v1/cs2/matches?limit=1" | grep -i x-quotaX-Quota-Remaining is the number to alarm on. Wire it into your own monitoring at, say, 20% — the
dashboard also emails at 80% and 100% if you leave those notifications on.
During a trial the allowance is 1,000, counted from when the trial started rather than by calendar month. That's sized for evaluating the API, not for bulk-loading history.