> ## Documentation Index
> Fetch the complete documentation index at: https://docs.financialdatasets.rip/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> What gets cached, for how long, and what a cache hit means for cost.

`GET` REST responses are cached. A cache hit skips the Monid call entirely, so it costs nothing and returns faster than a fresh call would: measured on 2026-09-06, a cold ask took a median 5,256ms against 69ms once cached. Every response carries an `X-Cache` header telling you which one happened.

```bash curl theme={"theme":"css-variables"}
curl -si 'https://api.financialdatasets.rip/financial-metrics/snapshot?ticker=AAPL' \
  -H 'X-API-KEY: <your-api-key>' | grep -i x-cache
```

```
X-Cache: miss
```

Call the same URL again before the TTL runs out, and the header reads `X-Cache: hit`, with no Monid call behind it.

## What makes two requests "the same"

A cache entry is keyed on the request path plus the query parameters, sorted. Parameter order does not matter: `?ticker=AAPL&limit=1` and `?limit=1&ticker=AAPL` hit the same entry.

The cache is shared across callers by default. If someone asked for Apple's balance sheet a minute before you, your call is a hit and bills nothing. This is safe because a response never contains anything specific to the caller; cost and provenance only appear on a traced request, and traced requests never touch the cache. An operator who does not want callers to benefit from each other's spend can set `CACHE_PER_CALLER=1`, which adds the API key to the entry key.

## How long entries live

| Request                                             | TTL         | Why                                                            |
| --------------------------------------------------- | ----------- | -------------------------------------------------------------- |
| `/prices*`, `*/snapshot`, `/news*`                  | 60 seconds  | Prices and headlines move fast.                                |
| `/financials/*`, `/filings*`, `/financial-metrics*` | 600 seconds | Statements and filings change at most once a day.              |
| Everything else                                     | 300 seconds | A middle ground for routes without a strong reason either way. |
| Any request that names a closed period              | 24 hours    | The answer cannot change.                                      |

A request names a closed period when it carries an `accession_number`, a `year` before the current one, or an upper date bound (`end_date`, `report_period`, `report_period_lte`, `report_period_lt`, `filing_date`, `filing_date_lte`, `filing_date_lt`) before today. A lower bound alone (`start_date`, `report_period_gte`) does not count, because the newest period in the answer is still open. `period=ttm` never counts: a trailing-twelve-month row is priced at the latest close, so it always carries a live figure.

A day rather than forever, because a company can restate a closed period in a later filing.

## What never gets cached

Non-`GET` requests, `POST /financials/search/screener` is the only one, are never cached. Requests with the `X-Monid-Trace` header bypass the cache, since the point of tracing is to see the route taken. Error responses, anything with a status of 400 or higher, are never cached either. The MCP transport at `/mcp` and `/api` is never cached, regardless of method.

## Where entries live

By default the cache is in the server process: bounded to 4,096 entries, oldest evicted first, and gone on restart. Set `CACHE_URL` (and `CACHE_TOKEN` for Upstash, or a `redis://` / `rediss://` URL for Redis) and entries move to that store, where every machine shares them and a deploy does not throw them away. The hosted deployment at `api.financialdatasets.rip` runs this way. A store that cannot be reached counts as a miss, never an error; the cache is a latency and cost optimization, not a guarantee.
