Integrations
Migrate an Existing Currency API Integration
Move a currency API integration to CurrencyCore: plan choice, bearer auth, currency and history coverage, response mapping, endpoint mapping, and a cutover checklist.
This guide is the provider-neutral path. It covers the decisions that apply whichever currency API you are leaving: which plan to start on, how authentication changes, what currency and history coverage you actually get, how to map response fields, and how to cut over without guessing.
Provider-specific mappings are in the Fixer guide, the CurrencyLayer guide, and the ExchangeRate-API guide. All integration guides are listed under Guides.
CurrencyCore is not a drop-in replacement at the wire level. The host, the auth header, the response envelope, and in some cases the base-currency semantics change. The ISO codes and the arithmetic do not. Plan for response mapping and a comparison pass, not only a host swap.
Provider-specific mappings
Each provider guide covers the same six areas: auth, base semantics, response shape, history, errors and cutover. Start with the one you are leaving.
| Provider | Auth style | Main response shape | What changes most | Guide |
|---|---|---|---|---|
| Fixer | access_key query parameter | latest and convert objects | Move the key to a bearer header and map the result envelope | Fixer |
| CurrencyLayer | access_key query parameter | quotes keyed by concatenated pair | Strip the base prefix and map to rates | CurrencyLayer |
| ExchangeRate-API | key in the URL path | conversion_rates map | Move the key out of the URL and drop the status envelope | ExchangeRate-API |
| Open Exchange Rates | app_id query parameter | rates map with an epoch timestamp | Use header auth and a UTC calendar date | Open Exchange Rates |
| currencyapi.com | apikey header or query | data wrapper | Unwrap data and use a bearer header | currencyapi.com |
| Frankfurter | keyless | rates map | Add auth, a base path and plan limits | Frankfurter |
If your provider is not listed, use the provider-neutral mapping below. The decisions are the same.
1. Choose the entry plan
Public CurrencyCore pricing was checked on 2026-11-10.
| Stage | Plan | What it includes | Main limits |
|---|---|---|---|
| Evaluation | Free, $0 | 1,000 requests per month, 100 AI credits, 3 seats | 60 requests/min, last 30 days of history, test keys only |
| Production entry | Starter, $9/mo | 50,000 requests per month, full history, live keys, 10 seats | 120 requests/min, no analytics endpoints |
| Analytics and BYOK | Growth, $29/mo | 500,000 requests per month, analytics endpoints, BYOK | 600 requests/min |
| High volume | Business, $199/mo | 10,000,000 requests per month, SSO and audit logs | 6,000 requests/min |
Use Free to verify codes and response shapes. Use Starter when the integration
needs live keys or history older than 30 days. Use Growth only when you need
/v1/history/analysis, /v1/volatility, /v1/correlation, /v1/drawdown,
/v1/safe-haven, /v1/mean-reversion, or /v1/ppp/analysis. See
Rate limits and plan quotas for the full table.
2. Move authentication out of the URL
Many providers take a key as a query parameter named access_key, apikey, or
key. CurrencyCore takes a header:
curl "https://api.currency-core.com/v1/rates/USD" \
-H "Authorization: Bearer cc_live_your_key"
x-api-key: cc_live_your_key is equivalent. There is no query-string
authentication. Remove the old query parameter from code, fixtures, logs,
browser calls, and shared dashboards. A missing or invalid key returns
401 unauthorized.
Test keys use the cc_test_ prefix and always run at the Free per-minute limit.
Live keys use cc_live_ and require a paid plan. Both count against the same
organization quota. See Authentication for key
creation, rotation, and environment behavior.
3. Diff the currency set before you switch
CurrencyCore quotes 160 fiat currencies. GET /v1/currencies returns 165
entries: the 160 fiat codes plus XAU, XAG, XPT, XPD, and XDR as
reference entries. The rate feed is fiat only, so those five entries are not
quoted rates.
Three practical consequences:
- A provider that advertises more than 160 currencies may include metals,
crypto, or regional codes that CurrencyCore does not quote. Check every code
you store against
/v1/currencies, not against the provider’s headline count. - There are no cryptocurrency rates. If your product prices BTC, ETH, or a stablecoin, keep a separate crypto provider.
- Precious metals are not in the rate feed. If your product needs
XAUorXAG, keep a metals provider or stay with a vendor that includes them.
Do the diff in code, not by reading a marketing page. Store the result in a fixture and fail a test if a required code disappears.
4. Understand history coverage and cadence
CurrencyCore history is not one uniform daily series from 1949. Coverage depends on the pair:
| Series | Coverage |
|---|---|
| GBP/USD and USD/INR annual parity | 1949 |
| Daily major pairs | 1971 |
| EUR pairs | 1999 |
Plan access is separate from data depth:
- Free can request the last 30 days. Older dates return
403 forbidden. - Starter and above can request the full dataset.
The update model is also different from a tick feed. CurrencyCore uses central-bank reference rates, polled hourly, with daily fixings underneath. There is no real-time quote and no 60-second update guarantee. If your product needs executable sub-minute prices, CurrencyCore is the wrong feed. Dates are UTC calendar dates.
When a requested date has no snapshot, the API resolves to the most recent
earlier one. date is the date actually used; requestedDate appears only when
it differs. asOf lists currencies whose rate was carried forward. Your adapter
should preserve those fields instead of inventing a fresh timestamp.
5. Map response shapes
The provider-neutral mapping is small, but it is not zero.
Rate snapshot
A snapshot with a rates map maps directly:
{ "base": "USD", "date": "2026-11-10", "rates": { "EUR": 0.9214, "INR": 84.07 } }
A provider that keys rates by concatenated pairs needs one transformation:
{ "source": "USD", "quotes": { "USDEUR": 0.9214, "USDINR": 84.07 } }
becomes rates.EUR and rates.INR when the base is USD. Strip the six-letter
prefix, not a fixed three-letter chunk, so codes of different lengths do not
break parsing.
Conversion
A single-result envelope becomes an array entry:
{ "result": 92.14, "rate": 0.9214 }
becomes:
{ "results": [{ "to": "EUR", "result": 92.14, "rate": 0.9214 }] }
Read results[0].result for the converted amount. Read results[0].rate for
the rate. If you send multiple targets in to, match each result by to
instead of assuming array position.
History
A provider that returns a map of dates to currency maps needs flattening:
{ "rates": { "2026-11-09": { "EUR": 0.92 }, "2026-11-10": { "EUR": 0.9214 } } }
becomes a dense date-to-rate map for one currency:
{ "currency": "EUR", "base": "USD", "rates": { "2026-11-09": 0.92, "2026-11-10": 0.9214 } }
CurrencyCore fills weekends and holidays by carrying the previous rate forward. That is deliberate. If your chart expects missing points, filter them in your adapter instead of treating the dense series as an error.
Field mapping
| Common provider field | CurrencyCore field | Note |
|---|---|---|
base or source | base | /v1/rates is always USD; use /v1/rates/{base} for another base |
date, timestamp, or time_last_update_utc | date | CurrencyCore returns a UTC calendar date, not an epoch |
rates | rates | Values are units of the target per 1 unit of base |
result or conversion_result | results[0].result | Conversion responses use an array |
quotes.USDEUR | rates.EUR | Strip the base prefix |
start_date and end_date | from and to | Inclusive UTC dates |
symbols or codes | /v1/currencies | CurrencyCore returns metadata, not only a code list |
success: true | HTTP 200 | CurrencyCore signals failure with an HTTP status and an error envelope |
Errors are uniform:
{ "error": { "code": "unknown_currency", "message": "Currency code 'XYZ' is not recognized." } }
Read the stable code, not the message text. See Errors for the
full catalog and retry guidance.
6. Map endpoints
| Common provider endpoint | CurrencyCore endpoint | Caveat |
|---|---|---|
| Latest rates | /v1/rates | USD base only |
| Latest rates with another base | /v1/rates/{base} | Base moves from query to path |
| Convert an amount | /v1/convert | Response uses results[], up to 25 targets |
| Dated snapshot | /v1/rates/{base}?date=YYYY-MM-DD | Free is limited to 30 days |
| Dated conversion | /v1/convert?date=YYYY-MM-DD | Same Free window |
| Time series | /v1/history | One currency per call, dense series |
| Change or fluctuation | /v1/history/analysis | Growth or higher, and not a field-for-field match |
| Symbols or supported codes | /v1/currencies | Public, no key, no quota |
| Country reference | /v1/countries | Public, no key, no quota |
| PPP conversion | /v1/convert?ppp=true | No equivalent in most FX-only providers |
| Natural-language query | /v1/ai | Separate AI credit pool |
/v1/rates and /v1/convert cover the core migration. Everything else is
optional and can be adopted later. The full parameter and response reference is
at API reference and in the OpenAPI specification.
7. Cut over in a controlled order
- Inventory calls. List every provider endpoint, base currency, date range, and consumer. Mark which calls need history and which only need the latest snapshot.
- Create the key. Use a test key on Free for the first pass. Move to a live key only when the account is on a paid plan.
- Diff currencies. Compare every stored code with
/v1/currencies. Decide what happens to unsupported codes before traffic moves. - Write the adapter. Map auth, base currency, date fields,
rates,results[], and the error envelope. Keep the old provider client behind the same interface so rollback is a switch, not a rewrite. - Run shadow traffic. Send the same logical request to both providers. Do not compare timestamps literally; compare the rate for the same fixing date.
- Compare with tolerances. Allow for provider-specific rounding and for carry-forward dates. Investigate every gap beyond your tolerance instead of averaging it away.
- Switch reads. Move low-risk read paths first. Keep writes and billing calculations on the old provider until the comparison looks stable.
- Watch the right errors.
401is auth,402is billing,403is a plan or date gate, and429is a quota or per-minute limit. HonorRetry-After. - Revoke the old key. Do not leave dual auth in production. Rotate or revoke the provider key once the new path has served real traffic.
- Record rollback. Keep the old adapter and the last known good config until the next billing cycle, even after cutover.
Caveats to decide before you start
- No metals or crypto quotes. The feed is fiat only.
- No sub-minute feed. Reference rates are polled hourly; fixings are daily.
- Base semantics differ.
/v1/ratesis USD. Use/v1/rates/{base}when your old provider defaulted to EUR or another base. - History is plan-gated. Free stops at 30 days even though the underlying dataset reaches 1949.
- Quota accounting is not identical. Only HTTP 2xx responses count toward the monthly quota. The per-minute limit counts every request, including errors and retries. See Rate limits.
- Analytics are not a rename. Provider “change” or “fluctuation” responses are not the same object as CurrencyCore’s analytics endpoints.
If these caveats are acceptable, the migration is mostly adapter work and comparison testing. If any of them is a hard product requirement, keep the provider that meets it instead of forcing an equivalence that does not exist.