API reference
The complete REST and WebSocket contracts: authentication, limits, response fields, endpoints, and errors. If you are making your first request, the quickstart is the faster place to begin.
On this page
Introduction
exchangerate.dev is a REST API for indicative foreign exchange rates. It provides live intraday rates on trading days for actively traded currencies, with source, market_session, and freshness fields on every response. Try the free exchange rate API anonymously, or start with a free key: no credit card required.
market_session and source fields show the market state and data source for each response.Quickstart
Make your first data call without signup. The standalone 60-second quickstart has the request, exact response shape, and optional API-key step.
Authentication
You can evaluate data routes without a key. Pass your key in the Authorization: Bearer header for account-tier quotas and attribution. Anonymous access is limited to 12 requests/minute and 100 requests/hour per IP. Never embed an account key in public browser JavaScript; use anonymous access or a server-side proxy. Account quota resets at 00:00 UTC on the 1st.
WebSocket clients authenticate in their first message. Do not put an API key in the WebSocket URL. Anonymous streaming is unavailable.
Rate limits
Anonymous limits are per IP; account limits are per key and calendar month. On 429 the response includes Retry-After.
For new accounts, stream caps are separate from REST requests per minute: Free: 0 concurrent connections, 0 pairs per connection; Basic: 0 concurrent connections, 0 pairs per connection; Pro: 0 concurrent connections, 0 pairs per connection; Realtime: 10 concurrent connections, 100 pairs per connection. Existing accounts keep their earlier limits; the authenticated WebSocket frame reports effective caps. An accepted stream connection uses one monthly quota unit.
Response metadata
Keep this metadata with the rate. It is the API's freshness and derivation context.
| Field | Meaning and applicability |
|---|---|
source and sources | source is the least-fresh contributing rate; sources maps returned currencies. Enum values: live (intraday indicative), ecb_daily and fred_daily (daily reference), banxico_daily only on /v1/rate/usd-mxn-banxico, and dof_daily only on /v1/rate/usd-mxn-dof. The two Mexican-peso reference values never apply to latest, historical, convert, or range routes. |
market_session | open, weekend, or interbank_closed; it describes the market state for the response. |
data_updated_at, effective_at, and timestamp | data_updated_at is the oldest contributing observation. effective_at maps each currency to its own observation time on latest and batch-convert responses. timestamp is when this response was served. Times are UTC ISO-8601 strings. |
derived_symbols, derived, and derivation_bps_max | derived_symbols lists triangulated rates in multi-currency responses. Single-pair responses use derived; derivation_bps_max can be null, including for a derived pair without a fixed ceiling, so use derived to detect triangulation. |
notice | A short reminder that rates are indicative, not for settlement. Daily-reference responses include the reference-data notice. |
Operation registry
Every public integration operation is listed once by method and OpenAPI path. See the OpenAPI 3.1 schema or ReDoc for complete schemas.
Path and query base values must match when both are sent; conflicts return 400.
| Operation | Purpose | Parameters | Request | Response | Endpoint errors |
|---|---|---|---|---|---|
POST /v1/account/checkout-session | Open Stripe checkout or subscription management for the signed-in account. | session auth; JSON body: plan (basic/pro), interval (monthly/yearly) | POST /v1/account/checkout-session | CheckoutResponse | authentication, checkout in progress, or billing unavailable |
GET /v1/latest | Latest rates with USD as the default base. | base, path_base, symbols (query; optional) | /v1/latest?base=USD&symbols=EUR,GBP | LatestResponse | data and limit errors |
GET /v1/latest/{path_base} | Latest rates with a path base. | path_base (path); base, symbols (query); path/query base must match | /v1/latest/USD?symbols=EUR,GBP | LatestResponse | conflicting base, data, and limit errors |
GET /v1/{date} | Historical snapshot for a date. | date (path); base, path_base, symbols (query) | /v1/2024-01-15?base=EUR&symbols=USD,GBP | HistoricalResponse | bad date or unavailable data |
GET /v1/{date}/{path_base} | Historical snapshot with a path base. | date, path_base (path); base, symbols (query); path/query base must match | /v1/2024-01-15/EUR?symbols=USD,GBP | HistoricalResponse | conflicting base, bad date, or unavailable data |
GET /v1/convert/{from_ccy}/{to_ccy} | Convert one unit or a query-supplied amount. | from_ccy, to_ccy (path); amount (query, default 1) | /v1/convert/USD/EUR?amount=100 | ConvertResponse | invalid pair, amount, or unavailable data |
GET /v1/convert/{from_ccy}/{to_ccy}/{amount} | Convert a path-supplied amount. | from_ccy, to_ccy, amount (path) | /v1/convert/USD/EUR/100 | ConvertResponse | invalid pair, amount, or unavailable data |
POST /v1/convert | Convert several amounts in one request. | JSON body: from and pairs (required) | POST /v1/convert | ConvertBatchResponse | invalid request, amount, or unavailable data |
GET /v1/range | Daily time series with JSON, CSV, or Parquet output. | symbols, start_date, end_date required by API validation; base, cursor, format optional | /v1/range?base=USD&symbols=EUR,GBP&start_date=2024-01-01&end_date=2024-06-30 | RangeResponse | missing parameter, bad date, or unavailable data |
GET /v1/timeseries | Market observations from 1m through 4h, or existing daily reference history at 1d. | symbols, interval, from, to required; base defaults to USD; cursor and format optional | /v1/timeseries?base=USD&symbols=EUR,JPY&interval=5m&from=2026-09-14T12:00:00Z&to=2026-09-14T13:00:00Z | TimeseriesResponse | unsupported interval, missing parameter, bad date, or unavailable data |
GET /v1/indices/{index_slug} | Latest complete value for the six-currency reconstruction or 13-currency USD strength index. | index_slug (path): dxy-reconstructed or usd-strength | /v1/indices/usd-strength | IndexSnapshotResponse | unknown index or incomplete/misaligned basket |
GET /v1/indices/{index_slug}/timeseries | Complete aligned index observations at a public interval. | index_slug (path); interval, from, to required; cursor optional | /v1/indices/usd-strength/timeseries?interval=1d&from=2026-01-02T00:00:00Z&to=2026-09-15T23:59:59Z | IndexTimeseriesResponse | unknown index, unsupported interval, bad date, or unavailable data |
GET /v1/currencies | List supported currencies and their display facts. | none | /v1/currencies | CurrenciesResponse | standard endpoint errors |
GET /v1/rate/{slug} | Return one flat pair quote by slug. | slug (path) | /v1/rate/eur-usd | RateResponse | invalid pair or unavailable data |
POST /v1/account/provision | Create an account organization, with optional first-key creation. | session auth; optional JSON body: defer_key, signup_attribution, marketing_consent | POST /v1/account/provision | ApiKeyCreatedResponse | authentication or account errors |
GET /v1/account | Read account, active key, and monthly usage. | session authentication | /v1/account | AccountResponse | authentication or account errors |
GET /v1/account/keys | List the signed-in account's current and retiring API keys. | session authentication | /v1/account/keys | key summaries and five-key limit | authentication or account errors |
POST /v1/account/keys | Create, rename, rotate, or revoke an API key in the signed-in account. | session auth; JSON body: action, optional id, request_id, name | POST /v1/account/keys | managed key | authentication, validation, or key-limit errors |
GET /v1/account/usage/daily | Read daily usage for up to 90 days. | session auth; days (query, optional) | /v1/account/usage/daily?days=30 | DailyUsageResponse | authentication or account errors |
DELETE /v1/account | Cancel billing and delete account data. | session authentication | DELETE /v1/account | DeleteAccountResponse | authentication, account, or billing errors |
POST /v1/keys/rotate | Rotate the active API key with a grace period. | session authentication | POST /v1/keys/rotate | ApiKeyCreatedResponse | authentication or account errors |
DELETE /v1/keys | Revoke the active API key. | session authentication | DELETE /v1/keys | success envelope | authentication or account errors |
POST /v1/consent | Record acceptance of a policy version. | session auth; JSON body: policy_version (required) | POST /v1/consent | ConsentResponse | authentication or validation errors |
GET /v1/latest
Latest rates for all supported pairs, or a filtered subset via ?symbols=.
GET /v1/{YYYY-MM-DD}
Returns the reference rate as published on that date. Published coverage starts on 1999-01-04. The historical data guide covers weekends, ranges, pagination, and downloads.
GET /v1/convert
Single or batch currency conversion. The POST form accepts multiple pairs in one call.
GET /v1/rate/{slug}
Returns a flat, single-pair quote by slug (e.g. eur-usd). This differs from /v1/convert, which nests per-pair results under conversions[] and adds amount/converted. Slug is case-insensitive; malformed or unknown pairs return invalid_pair.
GET /v1/range
Daily time-series for a date window. JSON responses are capped at 366 rows/page with keyset pagination; pass format=csv or format=parquet to download the full window as a single file.
GET /v1/timeseries
Start with a working anonymous example on the intraday exchange rate API page.
Aligned rate observations for 1m, 5m, 15m, 1h, 4h, or 1d. Intraday rows are market observations. The daily interval continues to use the existing official reference history; it is never replaced with a market close.
JSON pages contain up to 1,000 timestamps. Maximum windows are 7 days for 1m, 31 days for 5m, 93 days for 15m, 366 days for 1h, and 1,830 days for 4h or 1d. Missing market observations stay missing. For intraday derived crosses, both required legs must be in the same interval bucket and their closing observations must be no more than five seconds apart. The derived value keeps the oldest contributing observation timestamp; it is a derived rate observation, not an exact synchronized OHLC close. The row timestamp is its bucket start;effective_at maps each symbol to its actual observation time. Candle backfills have null observation times and serve direct rates only. CSV and Parquet use SYMBOL_effective_at columns. A null observation timestamp and a missing row indicate unavailable data; clients should not forward-fill them.
Market collection began on 2026-09-14. The example above uses a populated trading-hour window from that day. Retention is not the amount of history already collected: 1m rows are retained for 30 days; 5m and coarser rows have no scheduled expiry. Availability still varies by pair and window, and the current open bucket can change.
Pair and interval coverage
Market history and WebSocket cover 14 currencies: AUD, CAD, CHF, EUR, GBP, JPY, MXN, NOK, NZD, PLN, SEK, SGD, USD, ZAR. Both directions are supported. EUR/USD and USD/EUR count as one unique pair in this table; self-pairs are excluded. Latest-rate freshness on the currency table does not establish intraday or stream eligibility.
| Interval | Unique pairs | Data |
|---|---|---|
1m | 91 | Market observations |
5m | 91 | Market observations |
15m | 91 | Market observations |
1h | 91 | Market observations |
4h | 79 | Market observations |
1d | 465 | Daily reference across 31 currencies |
| WebSocket | 91 | Live market observations |
At 4h, the 12 non-USD SGD crosses are excluded; USD/SGD and SGD/USD remain supported. HKD, THB, TRY and other currencies outside the list above have daily reference coverage but are not in the market-history or WebSocket scope. There is no 2h interval. An eligible pair may still have missing or stale observations.
An unknown interval returns HTTP 400 unsupported_interval. A known interval with an unsupported pair returns HTTP 400 unsupported_pair_interval with rejected_pairs; mixed requests fail in full. A supported request with no observations returns HTTP 200 success, data: [], has_more: false and next_cursor: null. An empty result is not a service error.
GET /v1/indices
Two versioned USD indices share the same source and alignment rules as the rate API.dxy-reconstructed applies the published six-currency basket formula to exchangerate.dev observations. It is an independent reconstruction, not official or licensed ICE data. usd-strength is an equal-weight geometric basket of 13 currencies with 2026-01-02 = 100.
Latest responses include the value, methodology version, weights, constituent rates, effective times, and log contributions. History accepts every public interval and the same maximum windows as /v1/timeseries. Every constituent must be present. Intraday timestamps must be within 30 seconds; otherwise the point stays missing and the weights are not redistributed.
Start with the basket comparison and worked API examples; use this reference for the index response fields and limits.
WebSocket /v1/stream
Connect to wss://api.exchangerate.dev/v1/stream, authenticate, then subscribe by base currency and symbol list. The same 14 currencies above form 91 unique pairs (182 directions). Authenticate within 10 seconds of opening the connection. The service sends an initial snapshot, rate updates, heartbeat frames, and explicit stale or unavailable states. Each requested pair emits at most one update per second.
Run this example on a server with Node.js 22 or later and set EXCHANGERATE_API_KEY in its environment. Keep the key off public browser pages. Derived stream pairs require both legs to be fresh and no more than five seconds apart; otherwise the pair is unavailable or stale.
Send {"action":"unsubscribe","base":"EUR","symbols":["JPY"]} to remove pairs, or {"action":"unsubscribe_index","index":"usd-strength"} to remove an index. Index subscriptions do not consume the pair allowance. Reconnect with exponential backoff after a network failure. Error codes distinguish invalid authentication, exhausted monthly quota, connection and subscription caps, malformed messages, and temporary stream unavailability.
Build a reconnecting server-side WebSocket client with the integration guide, then use this reference for every frame and error.
GET /v1/currencies
Full list of supported currencies with name, type, decimal precision, minor units, and derived flag. This endpoint takes no parameters.
GET /v1/account
Your org, active key, and current-month usage.
Errors
Every REST error uses the same envelope. A 429 response also includes a Retry-After header.
Data errors: invalid_pair, unsupported_base, unsupported_pair_interval, bad_date, invalid_amount, invalid_request, missing_parameter, data_unavailable, no_data_for_date, live_unavailable, source_unavailable, and service_unavailable. Time-series validation also emits unsupported_interval and timeseries_disabled.
WebSocket subscriptions reject an unknown currency with unknown_currencyand a pair outside live coverage with unsupported_stream_pair. Mixed valid and invalid symbol lists are rejected as one request; no partial subscription is created.
Authentication and limit errors: invalid_api_key, auth_unavailable, forbidden, rate_limited, ip_rate_limited, quota_exceeded, and quota_unavailable.
Python
Use any HTTP client. This standard-library example includes a timeout and reads the same response shown above. The Python integration guide has a compact production example.
Node
Use built-in fetch or another HTTP client. This example checks the HTTP status and uses a ten-second timeout. The JavaScript conversion guide covers server-safe use.
Example response
Numbers and timestamps change as rates update; the response shape and freshness fields are stable.
MCP server
Use exchangerate.dev as a tool in any MCP-compatible AI agent. The dedicated MCP quickstart has setup, authentication, and the tool table.