Most ExchangeRate-API v6 code needs three changes to use exchangerate.dev: update the URL, move the key from the path to an Authorization header and replace conversion_rates with rates. Responses still begin with "result": "success". Free includes 5,000 requests a month, and you can test the API without a key.
Matching endpoints
Five of ExchangeRate-API's six v6 endpoints have a match. Latest, pair, history and supported codes map directly. The request quota endpoint maps to /v1/account, which takes a dashboard session token instead of the API key. Enriched data has no equivalent. ExchangeRate-API puts the key in the URL; exchangerate.dev uses a bearer token, keeping the key out of URLs. The ExchangeRate-API details come from its public docs, checked on 19 Oct 2026.
| ExchangeRate-API v6 | exchangerate.dev | Notes |
|---|---|---|
GET /v6/KEY/latest/USD | GET /v1/latest/USD | Send the key in the Authorization header. Filter with ?symbols=EUR,GBP |
GET /v6/KEY/pair/EUR/GBP/AMOUNT | GET /v1/convert/EUR/GBP/AMOUNT | Both accept an optional amount. Returns rate and converted |
GET /v6/KEY/history/USD/YEAR/MONTH/DAY | GET /v1/YYYY-MM-DD/USD | One date in the path. Free: 30 days; Basic: 1 year; Plus: from 1999 |
GET /v6/KEY/codes | GET /v1/currencies | Every currency code with its name and decimals, no key needed |
GET /v6/KEY/quota | GET /v1/account | Quota, plan and monthly reset. Needs a dashboard session token, not the API key |
GET /v6/KEY/enriched/USD/JPY | No equivalent | We return the rate only, without the country, locale or flag details |
Matching fields
The top-level response structure stays the same. The rates object has a different name, timestamps use ISO 8601 in UTC instead of Unix seconds, and each rate includes its source and whether it is stale.
| ExchangeRate-API | exchangerate.dev | Change |
|---|---|---|
result | result | Unchanged |
base_code | base | New name |
conversion_rates | rates | New name |
time_last_update_unix / _utc | data_updated_at (UTC ISO 8601) | ISO 8601 in UTC; timestamp records when the response was served |
time_next_update_unix / _utc | – | No fixed next update; live rates refresh during the trading day |
target_code, conversion_rate, conversion_result | to, rate, converted | New name |
| – | source, sources, stale, market_session | Added: each rate's source, stale status and market state |
Compare the requests
A typical migration changes one URL, one header and one field name. Use symbols to request only the currencies you need and keep the response small.
const res = await fetch("https://v6.exchangerate-api.com/v6/YOUR-API-KEY/latest/USD")const data = await res.json()const eur = data.conversion_rates.EURconst res = await fetch("https://api.exchangerate.dev/v1/latest/USD?symbols=EUR", { headers: { Authorization: `Bearer ${process.env.EXCHANGERATE_API_KEY}` },})const data = await res.json()const eur = data.rates.EURimport os, requestsres = requests.get( "https://api.exchangerate.dev/v1/latest/USD", params={"symbols": "EUR,GBP"}, headers={"Authorization": f"Bearer {os.environ['EXCHANGERATE_API_KEY']}"}, timeout=10,)data = res.json()rates = data["rates"] # was data["conversion_rates"]curl "https://api.exchangerate.dev/v1/convert/EUR/GBP/250" \ -H "Authorization: Bearer $EXCHANGERATE_API_KEY"Matching errors
ExchangeRate-API returns "result": "error" with an error-type. exchangerate.dev returns "result": "error" with a code, a message and a matching HTTP status. Your code can handle errors using the status alone.
| ExchangeRate-API error-type | exchangerate.dev | Action |
|---|---|---|
unsupported-code | 400 unsupported_base or invalid_pair | Look up the currency code in /v1/currencies |
malformed-request | 400 invalid_request or bad_date | Check the path, date format (YYYY-MM-DD) and amount |
invalid-key | 401 invalid_api_key | Verify the key or rotate it in the dashboard |
inactive-account | – | No equivalent; the key works after email confirmation at signup |
quota-reached | 429 quota_exceeded | Wait for Retry-After or choose a plan with more quota |
Fixing the inactive-account error
According to ExchangeRate-API's docs, inactive-account means the account's email address hasn't been confirmed. Confirming it resolves the error there. With exchangerate.dev, you confirm your email once at signup, then the key in your dashboard is ready to use. The data endpoints also work without a key while you wait.
Try a call without a key
You can run this request before signing up. Keyless calls use Free plan data, with limits of 10 requests a minute and 100 an hour per IP address.
curl "https://api.exchangerate.dev/v1/latest/USD?symbols=EUR,GBP,JPY"Request limits and history
Free includes 5,000 requests a month, 10 a minute, rates delayed by 15 minutes and 30 days of history. Older dates require Basic (one year) or Plus (from 1999). ExchangeRate-API's docs list historical data only on its Pro, Business and Volume plans.