Issue Nº 48 — Sep 29, 2026
A wrong match ID costs the same quota as a right one
Issue forty-eight covers OpenDota, a free, keyless Dota 2 statistics API in the Games & Comics category. This session queried it live. Every request shows two quota counters in its headers, and failed lookups spend them like good ones. Three different bad IDs all return the same 404 body. CORS answers with whatever origin you send, not a wildcard.
Three different bad IDs give one identical 404
This session sent GET https://api.opendota.com/api/players/99999999999, GET https://api.opendota.com/api/matches/1 and GET https://api.opendota.com/api/matches/abc. Each returned HTTP 404 and the body {"error":"Not Found"}.
The first ID is a player that does not exist. The second is a well-formed match ID that the API does not hold. The third is not a number at all. The body does not tell them apart, so a client must check the ID format itself before it sends the request.
A 404 spends the same quota as a 200
Every response in this session carried x-rate-limit-remaining-minute and x-rate-limit-remaining-day headers. A valid request for GET https://api.opendota.com/api/players/1 returned 59 and 2997.
The three 404 requests came next, and the minute counter fell to 58, 57 and 56. The day counter fell to 2996, 2995 and 2994. A following GET https://api.opendota.com/api/heroes returned 200 with 55 and 2993.
Each failed lookup used one unit of both counters, the same as a request that worked. A script that guesses match IDs will use up its daily allowance on misses.
CORS answers with the origin you send, and the preflight is free
This session sent GET https://api.opendota.com/api/heroes with the header Origin: https://greatapis.com. The response carried access-control-allow-origin: https://greatapis.com and access-control-allow-credentials: true. A second request with Origin: https://evil.example got that same origin echoed back. A request with no Origin header got no access-control-allow-origin header at all.
The API never returns a wildcard *. Browser code on any site can read the data, which is fine for public statistics. Do not assume that the header shows an allow-list.
A preflight OPTIONS request to the same path returned HTTP 204 and lists GET, HEAD, PUT, PATCH, POST and DELETE as allowed methods. It carried no x-rate-limit headers. In this session, the preflight did not show a quota cost.
Dota 2, by the numbers
60 requests/minute and 3,000 requests/day per IP, unauthenticated (an API key raises the limit) -- live x-rate-limit-remaining-minute/x-rate-limit-remaining-day headers on api.opendota.com/api/status.
- GET/players/{account_id}
- GET/heroStats
- GET/heroes
- GET/matches/{match_id}