Compression and Caching
Large public reads such as GET /markets are hundreds of kilobytes of JSON. Ask for compression, and let the Cache-Control header on each response tell you how fresh it is.
Compression
Send Accept-Encoding: gzip and the API returns the body gzip-compressed with Content-Encoding: gzip. GET /markets drops from about 900 KB to about 120 KB.
- Browsers and Node.js
fetchsend the header and decompress automatically. - Rust
hypercall-clientenables gzip in its HTTP client, so every REST call asks for it and decodes it transparently. - Python
requestsandhttpxsend the header and decompress by default. Plainurllibdoes neither, so you get the uncompressed body. - curl needs
--compressed.
Responses under about 1 KB are sent uncompressed. WebSocket frames are not affected.
Cache-Control
Every read tells you whether a shared cache may keep it.
| Response header | Where you see it | What it means |
|---|---|---|
public, max-age=N | Public market data: /markets, /instruments, /instrument-specs, /exchange-info, greeks, option and expiry summaries, historical theos, pool reads, /version | Any cache may reuse the body for up to N seconds. Most are 1 to 15 seconds; historical data can be longer. |
public, max-age=N, stale-while-revalidate=M | Some of the reads above | After N seconds a cache may keep serving the old body for up to M more seconds while it fetches a fresh one in the background. |
no-store | Wallet-scoped and authenticated reads: portfolio, open orders, fills, trades filtered by wallet, MMP config, and similar | Never stored by any cache. Every request reaches the API. |
| none | Everything else, including /orderbook and all writes | Not cached. |
Treat a public read as up to max-age (plus any stale-while-revalidate) seconds old. When you need the latest state for a trading decision, read it from the WebSocket channels or from a wallet-scoped read, which are never cached.
Error responses (4xx, 5xx) never carry public and are never cached. A cache does not keep serving an expired body while the API is returning errors; you get the error.
Requests that always reach the API
A cached body is never returned to a request that carries any of these, and nothing such a request receives is stored:
AuthorizationorCookieX-Hypercall-SignatureorX-Hypercall-Expires-At-Ms(signed reads such as MMP config)X-Hypercall-FenceorX-Hypercall-Fence-Wait(read-after-write consistency, see Market Maker Protection)X-Hypercall-Gateway-TokenorX-Admin-Key- any method other than
GETorHEAD, and WebSocket upgrades
Read-after-write fences and caching never mix. A route that honors X-Hypercall-Fence is no-store, and a public route never waits on a fence.
X-Cache-Status
Responses that pass through the Hypercall edge cache carry X-Cache-Status:
| Value | Meaning |
|---|---|
HIT | Served from the edge cache, within max-age. |
MISS | Fetched from the API. If the response was public, it is now cached. |
BYPASS | The request carried a header from the list above, so it went straight to the API and nothing was stored. |
EXPIRED | The cached copy was past its lifetime, so the API was asked again. |
STALE or UPDATING | Served from cache inside the stale-while-revalidate window while a fresh copy is fetched. |
Writes and other non-GET requests have no X-Cache-Status. The header is informational: do not branch trading logic on it. Not every environment has the edge cache yet. A response without the header came straight from the API.