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)RateChangeWhat the row is
2026-11-06 (Fri)1.0842Fresh fixing
2026-11-07 (Sat)1.08420Carried forward
2026-11-08 (Sun)1.08420Carried forward
2026-11-09 (Mon)1.0861+0.0019Fresh 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.