Documentation
Data Quality and Carried-Forward Rates
Read dense history correctly: carried-forward weekends, absent pre-listing dates, UTC dates, and the difference between a calendar row and a fresh fixing.
A daily history response is dense, not a list of fixes. It has one entry per calendar day in the window, with weekends and holidays carrying the last published trading day forward. Charting is easy; analysis is not: a row can be a fresh fixing, a repeat, or absent because the currency had no quote yet.
| Date (UTC) | Rate | Change | What the row is |
|---|---|---|---|
| 2026-11-06 (Fri) | 1.0842 | Fresh fixing | |
| 2026-11-07 (Sat) | 1.0842 | 0 | Carried forward |
| 2026-11-08 (Sun) | 1.0842 | 0 | Carried forward |
| 2026-11-09 (Mon) | 1.0861 | +0.0019 | Fresh fixing |
Dates before a currency’s first quote are absent, not zero. Annual parity starts in 1949 for GBP/USD and USD/INR; most majors have daily data from 1971, USD/INR from 1973, EUR pairs from 1999, and many Asian pairs from 1981. A missing early date is a coverage boundary, not a data gap.
Detect and record carried-forward values
/v1/rates, /v1/rates/{base}, and /v1/convert separate the requested day (requestedDate) from the day actually used (date). When present, asOf maps a currency to the earlier date its value came from. History has no asOf, so detect carry-forward by comparing consecutive values and skipping weekends and holidays. An unchanged value is evidence, not proof; a genuinely unchanged fixing looks identical.
Store the effective rate_date, your fetch’s observed_at, and source_as_of when supplied. Include observation time in the primary key so a refetch does not overwrite history. All dates are UTC; convert for display only.
Do not treat every calendar row as a fresh fixing. Averaging a dense series overweights weekends, and volatility over calendar days treats a three-day carry as three observations. Filter to changed values or business days, and document the convention.
A fixing is not versioned. There is no revision feed or revision number. If a refetch returns a different value for the same date, keep both observations, flag the later one, and recompute dependents. The website annual dataset is separate: FRED H.10 annual series plus documented parities, while API history uses the Frankfurter-seeded history store. Do not compare them without checking units and definitions.
Detect repeats in code
The API does not mark a repeat, so detect it by comparing consecutive values.
This function returns every calendar row with a repeat flag. A repeat is
evidence of a carried-forward value, not proof: a genuinely unchanged fixing
looks identical.
type RateRow = {
date: string;
rate: number;
repeat: boolean;
};
export function markRepeats(rates: Record<string, number>): RateRow[] {
let previous: number | undefined;
return Object.entries(rates).map(([date, rate]) => {
const repeat = previous !== undefined && rate === previous;
previous = rate;
return { date, rate, repeat };
});
}
export function observedOnly(rows: RateRow[]): RateRow[] {
return rows.filter((row) => !row.repeat);
}
Use observedOnly before averaging or computing volatility over observed
fixings. Keep the full array for a dense chart that shows weekends and holidays.
Document which convention each dashboard or report uses.