Caching Strategy
Caching is the single most effective lever for reducing API call volume, staying within rate limits, and improving the responsiveness of your integration. This page describes what to cache, how long to cache it, and how to know when to refresh.
Why cache
Most data served by the API is stable over the timescales that matter to a typical workflow. Identifiers do not change, structure changes slowly, and even datapoint values only change when a company restates or a new filing is processed. Re-fetching this data on every run wastes request budget and adds latency for no benefit. A good cache turns the majority of would-be API calls into local lookups.
What to cache
The four categories below are ordered from most stable (cache longest) to most volatile (refresh on a signal).
1. Company IDs — cache indefinitely
Store company_id mappings locally. These identifiers do not change, so once you have resolved a company to its ID you never need to look it up again.
- TTL: Effectively permanent.
- Refresh trigger: Only when onboarding a company you have not previously resolved.
2. Series IDs — cache and refresh weekly
Store the series structure for each company (which series exist and how they are organized). This structure is stable but can evolve as coverage expands.
- TTL: ~1 week.
- Refresh trigger: Scheduled weekly refresh, or when an expected series is missing.
3. Fundamentals — refresh on latest_datapoint_created_at
latest_datapoint_created_atStore datapoint values, but make the refresh event-driven rather than time-based. Only re-fetch a series' values when its latest_datapoint_created_at timestamp has changed; if the timestamp is unchanged, your cached values are still current.
- TTL: No fixed expiry.
- Refresh trigger: A change in
latest_datapoint_created_at. This is the lightweight check that lets you avoid re-pulling large value sets that have not actually changed.
4. Documents — cache for 30+ days
Full documents rarely change once filed, so they are safe to cache for long periods.
- TTL: 30+ days.
- Refresh trigger: Scheduled long-interval refresh, or an explicit indication that a document has been amended.
Summary
| Data | What to store | TTL | Refresh trigger |
|---|---|---|---|
| Company IDs | company_id mappings | Indefinite | New company onboarded |
| Series IDs | Series structure | ~1 week | Weekly schedule / missing series |
| Fundamentals | Datapoint values | Event-driven | latest_datapoint_created_at changes |
| Documents | Full documents | 30+ days | Long-interval schedule / amendment |
Recommended invalidation pattern
The most efficient pattern combines a cheap freshness check with a more expensive value fetch:
- Resolve identifiers from cache. Company and series IDs come straight from the local store — no API call.
- Poll the lightweight signal. Check
latest_datapoint_created_atfor the series you care about. - Refetch only on change. If the timestamp matches your cached value, serve from cache. If it has advanced, pull the updated fundamentals and update both the values and the stored timestamp.
- Refresh structure on schedule. Run the weekly series-structure refresh and the long-interval document refresh as background jobs, decoupled from the value-level checks.
This keeps the high-volume value lookups gated behind a single cheap comparison, which is what allows a busy integration to stay well under the per-minute request ceiling.
Operational notes
- Persist the cache across runs. Identifiers and structure should survive process restarts; re-resolving them on every cold start defeats the purpose.
- Key fundamentals by series and period. Cache values at a granularity that lets you refresh a single changed series without invalidating unrelated data.
- Store the timestamp you compared against. The cached
latest_datapoint_created_atis what makes the event-driven refresh possible — treat it as part of the cached record, not a throwaway. - Fail open, not stale-silent. If a freshness check fails, prefer surfacing the error or refetching over silently serving data you can no longer verify is current.
Updated about 15 hours ago