# Migrate from ExchangeRate-API

> Move an ExchangeRate-API integration to CurrencyCore: URL key to bearer auth, latest and pair mapping, conversion_rates mapping, history depth, pricing, and caveats.

Canonical: https://currency-core.com/docs/guides/migrate-exchangerate-api/
Updated: 2026-11-11
Published: 2026-11-11

---
ExchangeRate-API is a clean starting integration: a latest-rates endpoint, a pair
conversion endpoint, and a code list. Moving to CurrencyCore keeps the same ISO
codes and the same units-per-base arithmetic. The key moves out of the URL path,
the status envelope disappears, and history access follows a different plan
model.

Public ExchangeRate-API pricing and coverage below were checked on
**2026-11-10**. Verify the vendor's current plans before you budget.

## What changes at a glance

| Area | ExchangeRate-API | CurrencyCore |
|---|---|---|
| Currencies | 165 currencies | 160 fiat currencies |
| Free quota | 1,500 requests/mo, daily updates | 1,000 requests/mo, 60/min |
| Entry paid plan | Pro, $10/mo | Starter, $9/mo, full history |
| Update cadence | Daily on Free, hourly on Pro, 5 minutes on Business | Central-bank reference rates polled hourly, daily fixings |
| History | 1990 for limited pairs | GBP/USD and USD/INR parity from 1949, daily majors from 1971, EUR pairs from 1999 |
| Auth | API key in the URL path, or bearer | `Authorization: Bearer` or `x-api-key` |
| Rate envelope | `conversion_rates` | `rates` |
| Conversion | `conversion_result` | `results[0].result` |

CurrencyCore does not offer a 5-minute update tier. Its published cadence is
hourly polling of central-bank reference rates, with daily fixings underneath.
If five-minute updates are a hard requirement, ExchangeRate-API's Business plan
is the closer fit.

## 1. Move the key out of the path

ExchangeRate-API puts the key in the URL:

```bash
curl "https://v6.exchangerate-api.com/v6/YOUR_KEY/latest/USD"
```

CurrencyCore takes a header:

```bash
curl "https://api.currency-core.com/v1/rates/USD" \
  -H "Authorization: Bearer cc_live_your_key"
```

Remove the key segment from the path, from logs, and from any URL that is stored
or shared. Use a test key on Free while integrating. Live keys require a paid
plan. See [Authentication](/docs/authentication/).

## 2. Map endpoints

| ExchangeRate-API endpoint | CurrencyCore endpoint | Mapping note |
|---|---|---|
| `/v6/{key}/latest/{base}` | `/v1/rates/{base}` | Base stays in the path |
| `/v6/{key}/pair/{from}/{to}/{amount}` | `/v1/convert?from=&to=&amount=` | Read `results[0].result` |
| `/v6/{key}/history/{base}/{year}/{month}/{day}` | `/v1/rates/{base}?date=YYYY-MM-DD` | Use `/v1/history` for a series |
| `/v6/{key}/codes` | `/v1/currencies` | Public, no key, no quota |

A single historical date maps to a dated snapshot. If your code loops over dates
to build a chart, replace the loop with one `/v1/history` call for the same
window and one currency. That is fewer requests and a dense series.

## 3. Map the response envelope

An ExchangeRate-API latest response:

```json
{
  "result": "success",
  "provider": "https://www.exchangerate-api.com",
  "time_last_update_utc": "Tue, 10 Nov 2026 00:02:31 +0000",
  "base_code": "USD",
  "conversion_rates": { "USD": 1, "EUR": 0.9214, "INR": 84.07 }
}
```

The CurrencyCore equivalent:

```json
{
  "base": "USD",
  "date": "2026-11-10",
  "rates": { "USD": 1, "EUR": 0.9214, "INR": 84.07 }
}
```

`base_code` becomes `base`, `conversion_rates` becomes `rates`, and the update
timestamp becomes the UTC calendar `date`. The numbers are still units of the
target per 1 unit of base, so the arithmetic does not change.

An ExchangeRate-API pair conversion:

```json
{
  "result": "success",
  "base_code": "USD",
  "target_code": "INR",
  "conversion_rate": 84.07,
  "conversion_result": 8407
}
```

becomes:

```json
{
  "from": "USD",
  "amount": 100,
  "date": "2026-11-10",
  "results": [{ "to": "INR", "result": 8407, "rate": 84.07 }]
}
```

The amount is `results[0].result` and the rate is `results[0].rate`. If you send
several targets in `to`, match by `results[].to`.

| ExchangeRate-API field | CurrencyCore field | Note |
|---|---|---|
| `result` | HTTP status | `success` is not repeated in the body |
| `base_code` | `base` | Base is also in the request path |
| `conversion_rates` | `rates` | Same units-per-base meaning |
| `conversion_result` | `results[0].result` | Conversion is an array |
| `conversion_rate` | `results[0].rate` | Effective rate |
| `time_last_update_utc` | `date` | UTC calendar date, not a formatted timestamp |
| `time_next_update_utc` | none | There is no next-update promise |

Errors use the common CurrencyCore envelope:

```json
{ "error": { "code": "invalid_input", "message": "..." } }
```

Read the stable `code`. See [Errors](/docs/errors/).

## 4. Check history and currency coverage

ExchangeRate-API advertises **165 currencies**, history from **1990 for limited pairs**. CurrencyCore advertises **160 fiat
currencies** with a different history model:

- GBP/USD and USD/INR annual parity from 1949.
- Daily major pairs from 1971.
- EUR pairs from 1999.
- Free access limited to the last 30 days.
- Full history on Starter and above.

Do not assume a pair that starts in 1990 at ExchangeRate-API has a continuous
daily series in CurrencyCore from 1949. Check your specific pair against
[`/v1/history`](/docs/history-endpoint/) and the
[history coverage guide](/docs/guides/history-coverage/).

Also diff every stored code against [`/v1/currencies`](/docs/currencies/).
CurrencyCore returns 165 entries, but only 160 are fiat. `XAU`, `XAG`, `XPT`,
`XPD`, and `XDR` are reference entries, not quoted rates. There are no crypto
rates.

## 5. Cut over

1. Create a test key and delete the key segment from the request builder.
2. Replace `/latest/{base}` with `/v1/rates/{base}`.
3. Replace `/pair/{from}/{to}/{amount}` with `/v1/convert` and read
   `results[0]`.
4. Replace per-date history loops with one `/v1/history` call per currency.
5. Map `conversion_rates` to `rates` and `base_code` to `base`.
6. Shadow a sample and compare the same fixing dates. Allow for provider
   rounding and carry-forward dates.
7. Switch reads, watch `401`, `403`, and `429`, then revoke the old key.
8. Keep the old adapter until the next billing cycle for rollback.

For the provider-neutral checklist, see
[Migrate an existing integration](/docs/guides/migration/).

## Related

- [Migration overview](/docs/guides/migration/)
- [OpenAPI specification](/docs/openapi/)
- [Authentication](/docs/authentication/)
- [Errors](/docs/errors/)
- [Rate limits](/docs/rate-limits/)
- [All integration guides](/docs/guides/)
- [`/rates` endpoint](/docs/rates-endpoint/)
- [`/convert` endpoint](/docs/convert-endpoint/)
- [`/history` endpoint](/docs/history-endpoint/)
