GET /companies/{identifier}/articles. Send a conditional request with the If-None-Match header, and the API returns 304 Not Modified if nothing has changed, saving bandwidth and reducing your monthly usage count.
How ETag caching works
1
Make a request
The response includes an
ETag header with a unique identifier for the current state of the resource.Response headers
2
Send the ETag on subsequent requests
Include the
If-None-Match header with the ETag value:3
Receive a 304 if unchanged
If the resource hasn’t changed, the API returns
304 Not Modified with no body:If-None-Match value from the previous response for the same URL, filters included.
Why use ETag caching
- Save bandwidth by not re-downloading unchanged data
- Reduce monthly usage since
304 Not Modifiedresponses do not count against your monthly quota - Faster responses with no response body to parse
What the ETag covers
An ETag on a list endpoint fingerprints the whole response, not one record. Four things go into it:- The request’s query string, so the same endpoint under different filters carries different ETags
- The
idandupdated_atof every record in the response - State that no record’s
updated_atreflects, such as whether a plan cap applied, on story routes which of a story’s articles you can see, and on calendar routes the name and ticker of each embedded issuer - A version token, bumped whenever a response body gains, loses, or renames a field
updated_at sits still. To detect changed records, walk updated_after instead.
Last-Modified and If-Modified-Since
Cached responses also carryLast-Modified, set to the newest updated_at in the record set. On a list it appears whenever the page is not empty. GET /stories/{id} and the events routes (GET /events, GET /events/{id}, and GET /companies/{identifier}/events) set an ETag and no Last-Modified, so If-None-Match is your only conditional request there. On the calendar routes Last-Modified also takes in the embedded issuers, so renaming a company moves the Last-Modified of every calendar response that carries it. Send it back as If-Modified-Since for the same 304. If-None-Match is the stronger check of the two, because Last-Modified has one-second resolution and does not see the state the ETag folds in beyond updated_at. Prefer If-None-Match, and send both only if your HTTP client does so by default.
Every cached response also sets Cache-Control: private, max-age=0, must-revalidate, so a shared cache will not serve your data to anyone else and every read revalidates.
When ETag caching is not available
Two parameters turn caching off, on any endpoint that otherwise supports it. Both sendCache-Control: no-cache, no-store and no ETag:
q, because search results are ranked per requestupdated_after, because sync walks are time-sensitive. See Real-time updates for the walk that replaces caching here
GET /search is never cached, for the same reason q is not. Every other list and single-resource endpoint supports conditional requests, and each one’s reference page declares the headers it accepts.