Browse all documentation

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

REST
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"
ParameterRule
baseUppercase circulating ISO 4217 currency in the path.
currenciesRequired comma-separated list of 1–64 unique quote currencies. The base cannot appear.
start_dateInclusive date on or after 1971-01-01.
end_dateInclusive date on or after the start and not in the future.
limitObservation dates per page. Default 100; maximum 366.
cursorOpaque 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

Production-shaped example
{
  "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_query for invalid currencies, dates, or page size.
  • 400 invalid_cursor for malformed or filter-mismatched pagination state.
  • 401 invalid_api_key for a missing or invalid Bearer key.
  • 404 currency_not_supported when the selected currency is unavailable.
  • 429 quota_exhausted when the shared monthly quota is exhausted.
  • 503 upstream_unavailable when 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.