Caching and freshness
How often each dataset is refreshed, how long you can cache it and how to avoid downloading what you already have.
Each dataset refreshes on its own schedule. Lists carry an updatedAt that tells you when that dataset last changed.
Responses carry cache-control with a max-age that matches how fast the data can change, plus stale-while-revalidate. Calling again sooner will usually give you the same answer, so cache for at least that long.
cache-control: private, max-age=20, stale-while-revalidate=120Cached responses also carry an etag. Send it back in if-none-match and, if nothing changed, you get an empty 304 Not Modified instead of the full body.
let etag = null;
let latest = null;
async function poll() {
const response = await fetch("https://api.donutstats.io/v1/auction/summary", {
headers: {
"x-api-key": process.env.DONUTSTATS_API_KEY,
...(etag ? { "if-none-match": etag } : {}),
},
});
if (response.status === 304) return latest;
etag = response.headers.get("etag");
latest = await response.json();
return latest;
}304s still count
A 304 saves bandwidth and parsing, not rate limit. It is still a request. See rate limits.
GET /v1/players/{username} is only cached when no live lookup ran. Responses that waited for a lookup are marked no-store.
When fresh data stops coming in, for example during a DonutSMP outage, DonutStats stops writing price and economy history until it is back. History series then have a gap rather than a flat line of stale values, so draw missing hours as missing.
Totals that cover the last 24 hours, like paidOut24h in economy history, are reported as null while that window still contains a gap, because the true figure is unknown.
