historical exchange rates API

Historical Exchange Rates API: Dated JSON and CSV

If you need a historical exchange rate inside a spreadsheet, a backtest, or an invoice reconciliation, the two things that matter are the date the rate belongs to and where that rate came from. FXpeek publishes dated reference rates sourced from ECB/Frankfurter as plain JSON and CSV, so a script can fetch a specific day and record both the value and the source alongside it. This page documents the endpoint shape, the fields, the date handling, and the mistakes that most often turn a rate lookup into an unreconcilable number.

What a historical rate actually is

A historical exchange rate is a reference value attached to a specific date, not a promise of what any bank, card network, or broker would have charged you that day. Reference rates come from a central bank or an aggregator that republishes central-bank data, and they are quoted once per business day.

That distinction matters for reconciliation. If you record an ECB reference rate next to an invoice, you have recorded a defensible third-party observation. If you record a card network's settlement rate, you have recorded what was actually charged. These are different numbers serving different purposes, and mixing them in one spreadsheet column makes neither number trustworthy.

Requesting a specific date

Historical rate requests should always name the date explicitly rather than asking for 'the latest'. A request for the latest rate is reproducible only on the day it was made; a request for a named date is reproducible forever.

Weekends and holidays are the usual failure point. Reference rates are published on business days, so a request for a Saturday should return either the preceding business day's value or an explicit empty result — never a silently invented number. Decide which behaviour you want and encode it in the caller, because the endpoint cannot know whether your use case tolerates a carried-forward value.

Field naming that survives a schema change

Store the pair direction as part of the field name, for example `jpy_to_gbp_rate` rather than `rate`. A column called `rate` becomes ambiguous the moment a second pair enters the same table, and ambiguous columns are where reconciliation errors hide.

Record the source and the source date in the same row as the value. A number without its provenance is not evidence; it is a claim. When an auditor asks where a figure came from six months later, the row itself should answer that question without needing a separate lookup.

CSV versus JSON

JSON is the better choice when a program consumes the data, because the structure carries the field names and the types. CSV is the better choice when a human will inspect or edit the data, because a spreadsheet is the most common place these numbers end up.

Exporting both from the same underlying query is worth the small extra effort. The two formats then agree by construction, and nobody has to wonder whether the CSV was generated from the same snapshot as the JSON.

Caching, rate limits, and reproducibility

Historical data does not change for dates that are already past, so it can be cached aggressively. The one thing that does change is the set of dates available: a request made today may reach further back than a request made a year ago if the source has since extended its history.

For reproducible analysis, pin both the query date and the retrieval date. The query date tells a reader what value you asked for; the retrieval date tells them when the answer existed in that form. Without both, a result cannot be replayed.

Choosing between a reference rate and an executable quote

A published reference rate and a quote you can actually transact on are different objects. The reference rate is an observation used for reporting, reconciliation, or comparison. It is usually derived from a published fixing or a market snapshot, and it is not a commitment to trade at that level. A provider quote includes spread, fees, settlement timing, and the provider's own risk position. Confusing the two is the most common source of disputes in expense reporting and invoice reconciliation.

When you build a workflow on top of an API, decide explicitly which of the two you need. If the goal is a consistent internal record, a dated reference is appropriate and reproducible. If the goal is to settle a payment, capture the provider quote with its timestamp and keep the reference only as context. Store both, label them clearly, and never let a downstream system silently substitute one for the other.

Handling missing days without inventing data

Exchange-rate sources publish on business days and skip weekends and market holidays. A request for a date that has no publication will either return an error or return the most recent prior observation. Both behaviours are defensible, but they mean different things, and a caller who does not check will silently attribute a Friday value to a Sunday date. Before trusting a response, confirm which behaviour the endpoint uses and whether it tells you the effective date.

The safe pattern is to request an explicit date range and inspect the dates that come back rather than assuming one row per calendar day. If a date is absent, record it as absent. Do not forward-fill in a way that makes the series look continuous, because a chart drawn from forward-filled data implies market movement that never happened. When a report needs a value for a non-publishing date, state the source date next to the figure so the reader can judge whether the substitution is acceptable.

What to record alongside a historical rate

FieldWhy it matters
pair_directionPrevents the most common reconciliation error: inverting the rate
source_dateThe date the reference value belongs to, not the date you fetched it
source_nameECB / Frankfurter or whichever publisher you used
retrieved_atWhen you actually pulled the value, for replay
valueThe rate itself, stored as a number not a formatted string

Related currency workflows

Frequently asked questions

Is a historical reference rate the rate I actually paid?

No. A reference rate is a published observation. What you paid was set by your bank, card network, or broker, and will differ — sometimes materially. Use reference rates to check and document, not to reconstruct a settlement you did not record.

What happens when I request a weekend date?

Reference rates are published on business days. A weekend request should return the preceding business day's value or an explicit empty result. Which of those you get depends on the caller, so decide the behaviour deliberately rather than discovering it later.

Can I use historical rates to forecast future rates?

Historical data shows ranges, unusual moves, and context. It does not turn a past pattern into a guaranteed forecast. Treat any model built on it as a description of what happened, not a prediction of what will.