Documentation
Caching Reference Rates
Cache CurrencyCore rates by date and base, use long TTLs for past dates, and keep today's cache within your business tolerance.
Serving a stale rate can misprice an invoice or a checkout. The safer default is to cache snapshots by date and base, keep historical dates as long-lived entries, and hold today’s snapshot for only as long as your business can tolerate a stale value.
CurrencyCore rates are central-bank reference rates polled hourly, and the underlying fixings are daily. Most values change once per business day, but that is not a promise of intraday freshness. Historical snapshots are stable enough to cache for a long time, but they are not versioned. If a source revises a fixing, keep both observations and flag the change. In a currency’s history, dates before its first quote are absent rather than filled.
| Data | Cache key | Suggested lifetime |
|---|---|---|
| Historical snapshot | base + date | Long lived; store the observation date |
| Today’s snapshot | base + date | A few minutes, chosen from your tolerance |
/countries, /currencies | none | 24 hours; public and unmetered |
Do not build a conditional-request layer. /v1 does not send ETag or Last-Modified, so If-None-Match and If-Modified-Since will not produce a 304. Caching is yours to implement.
Pick the TTL for today’s snapshot from the decision it supports. A display-only dashboard can tolerate more staleness than an invoice that fixes a total. If you need the latest fixing as soon as it appears, shorten the TTL or refresh on a schedule that matches the hourly poll.
type Snapshot = {
base: string;
date: string;
rates: Record<string, number>;
requestedDate?: string;
asOf?: Record<string, string>;
};
const cache = new Map<string, { expiresAt: number; body: Snapshot }>();
async function getSnapshot(base: string, date: string, key: string) {
const id = `${base}:${date}`;
const hit = cache.get(id);
if (hit && hit.expiresAt > Date.now()) return hit.body;
const url = new URL(`https://api.currency-core.com/v1/rates/${base}`);
url.searchParams.set("date", date);
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
if (!res.ok) throw new Error(`rates ${res.status}`);
const body = (await res.json()) as Snapshot;
const today = new Date().toISOString().slice(0, 10);
const ttl = date < today ? 30 * 86_400_000 : 5 * 60_000;
cache.set(id, { expiresAt: Date.now() + ttl, body });
return body;
}
A long cache on a historical date is reasonable, but keep the observation date and handle a source revision. A cache miss outside your plan window is not a glitch. Free history is the last 30 days, and older dates return 403, so do not retry that response as though more waiting would help.