Skip to main content

HTTP Caching (ETags)

The API supports standard HTTP conditional request headers. If you're polling endpoints or building anything that checks data repeatedly, ETags let you skip the download entirely when nothing has changed.


How ETags Work​

Every successful response includes an ETag header — a weak fingerprint of the response data, such as W/"a1b2c3d4e5f60718". On your next request, send that value back exactly as received, including the W/ prefix. If the data is unchanged, the server replies with 304 Not Modified and an empty body. You use your cached copy and save the bandwidth.

First request: GET /v2/funding-goals → 200 OK + ETag: W/"a1b2c3d4e5f60718"
Second request: GET /v2/funding-goals + If-None-Match: W/"a1b2c3d4e5f60718"
→ 304 Not Modified (data unchanged — use your copy)
→ 200 OK + new ETag (data changed — read the new body)
tip

Best candidates for ETag polling.

/v2/funding-goals, /v2/versions, and /v2/roadmap/* change infrequently — ETags are especially valuable here. If you poll more often than the refresh interval for that endpoint, 304 responses tell you nothing has changed without downloading the body.


If-None-Match​

The most common pattern. Send the ETag from a previous response; get a 304 if nothing changed, or a fresh 200 with new data if it has.

GET /v2/users/dymerz HTTP/1.1 Host: apiv2.starcitizen-api.com x-api-key: If-None-Match: W/"a1b2c3d4e5f60718"

ResponseWhat it meansAction
304 Not ModifiedData unchangedUse your cached copy — no body to parse
200 OKData changedRead the new body, store the new ETag

Store the ETag response header value alongside your cached data. On every subsequent request for the same resource, send it back via If-None-Match. The comparison is an exact string match, so a single ETag value is supported (not a comma-separated list or *).


Freshness Headers in the Response Body​

Successful responses for RSI data also include timestamp fields that give you application-level freshness information — no header parsing needed:

{
"success": true,
"message": "ok",
"updatedAt": "2026-05-31T10:00:00.000Z",
"expiresAt": "2026-05-31T11:00:00.000Z",
"data": { ... }
}
WhereFormatWhat it tells you
updatedAt body fieldISO 8601When the data was last scraped from RSI
Last-Modified headerHTTP-dateSame as updatedAt — for HTTP-level conditional caching
expiresAt body fieldISO 8601When the cache expires (the next request re-scrapes it)
Expires headerHTTP-dateSame as expiresAt — HTTP-native expiry signal

updatedAt and Last-Modified reflect the same moment. expiresAt and Expires reflect the same moment — updatedAt plus the endpoint's refresh interval.

tip

Schedule your next poll precisely.

Use expiresAt to know exactly when the data might change. There is no point re-fetching before that timestamp unless you pass ?refresh=true — and polling after it is still free, because the automatic re-scrape costs no quota.


Putting It Together: Efficient Polling​

Here's a complete pattern for polling funding stats without burning quota:

First request — no ETag yet:

GET /v2/funding-goals?chart=day HTTP/1.1 Host: apiv2.starcitizen-api.com x-api-key:

Save the ETag from the response header and the expiresAt from the body.

Subsequent requests — send the ETag back, and only poll after expiresAt:

GET /v2/funding-goals?chart=day HTTP/1.1 Host: apiv2.starcitizen-api.com x-api-key: If-None-Match: W/"a1b2c3d4e5f60718"

ResponseWhat to do
200 OKData changed — read new body, store new ETag and expiresAt
304 Not ModifiedNothing changed — keep your cached copy

This combination — smart cache + ETags + expiresAt scheduling — never spends quota and never downloads a response you already have. Each poll still counts toward the per-minute request limit.