Historical FX API reference
Request genuine exchange-rate observations by base, currencies, and inclusive date range through REST or MCP.
The history operation returns the current accepted stored series in oldest-first order. It uses the same Narwhal key and shared monthly quota as current FX and Economics requests.
Request
curl --request GET \
--url "https://api.narwhalapi.com/v1/fx/rates/USD/observations?currencies=EUR,NZD&start_date=2026-08-20&end_date=2026-08-28&limit=100" \
--header "Authorization: Bearer $NARWHAL_API_KEY"| Parameter | Rule |
|---|---|
base | Uppercase circulating ISO 4217 currency in the path. |
currencies | Required comma-separated list of 1–64 unique quote currencies. The base cannot appear. |
start_date | Inclusive date on or after 1971-01-01. |
end_date | Inclusive date on or after the start and not in the future. |
limit | Observation dates per page. Default 100; maximum 366. |
cursor | Opaque value from the previous page. Reuse it only with the same filters. |
The MCP tool is fx_get_rate_history. Its currencies input is a JSON array; every other field has the same meaning.
Response
{
"base": "USD",
"start_date": "2026-08-20",
"end_date": "2026-08-28",
"observations": [
{
"date": "2026-08-20",
"rates": [
{ "currency": "EUR", "rate": "0.85609" },
{ "currency": "NZD", "rate": "1.681" }
]
},
{
"date": "2026-08-21",
"rates": [
{ "currency": "EUR", "rate": "0.85477" },
{ "currency": "NZD", "rate": "1.6714" }
]
},
{
"date": "2026-08-24",
"rates": [{ "currency": "EUR", "rate": "0.85734" }]
}
],
"next_cursor": null,
"has_more": false
}Each rate is a decimal string. One unit of base equals the returned amount of currency on that observation date.
Pagination
limit counts dates, so a page never splits one date's currency records. When has_more is true, pass next_cursor back with the exact same base, currencies, start date, and end date. The cursor is filter-bound; changing those values returns 400 invalid_cursor.
There is no overall date-span cap. Full-history backfills use the same bounded page repeatedly until next_cursor is null.
Missing dates and currencies are not fabricated
Only genuine stored observation dates appear. Weekends, holidays, and other missing publication dates are omitted. Narwhal does not forward-fill them.
A date can contain only a subset of the requested currencies. In the example, NZD has no record on August 24 while EUR does. That is a truthful sparse observation, not a partial-response error.
Errors
400 invalid_queryfor invalid currencies, dates, or page size.400 invalid_cursorfor malformed or filter-mismatched pagination state.401 invalid_api_keyfor a missing or invalid Bearer key.404 currency_not_supportedwhen the selected currency is unavailable.429 quota_exhaustedwhen the shared monthly quota is exhausted.503 upstream_unavailablewhen the accepted historical data cannot be read safely.
Coverage and limits
Coverage begins at different dates by currency. The earliest supported stored series begins in 1971; most daily coverage begins in 1999 or 2000. Coverage can expand without changing this contract.
This Interface returns the current accepted historical series, not every earlier publication vintage. It omits source, provider, licence, derivation, staleness, and market-session fields. Read the FX methodology for the public data policy and the FX product page for all live operations.