CC API
Live mid-market exchange rates for 340+ fiat currencies, cryptocurrencies, stablecoins and metals, rate history, and how much a recipient really gets through each transfer service after fees. JSON over HTTPS, free, no API key.
https://currency-backend.currency-backend.workers.dev/v1Quickstart
Every endpoint is a plain GET request. Try one in a terminal:
No sign-up and no key. Responses are JSON, and errors have the same shape everywhere (see Errors). Requests from a browser work too: CORS is open.
curl "https://currency-backend.currency-backend.workers.dev/v1/convert?from=USD&to=EUR&amount=100"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/convert?from=USD&to=EUR&amount=100"); const { result } = await res.json(); console.log(result); // 89.05
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/convert", params={"from": "USD", "to": "EUR", "amount": "100"}) print(r.json()["result"])
{
"from": "USD",
"to": "EUR",
"amount": 100,
"rate": 0.89046402,
"result": 89.05,
"updated_at": "2026-10-17T22:17:28.819Z"
}Limits
The API is free, so it has hard limits. They keep it fast and available for everyone:
| Limit | Value |
|---|---|
| Requests per minute, per IP address | 30 |
| Requests per day, per IP address | 1,000 |
| Requests per day, all users together | 20,000 |
Days are counted in UTC: every counter resets at 00:00 UTC. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time of the reset).
Over a limit you get HTTP 429 with a Retry-After header. Wait instead of retrying in a loop: refused requests are counted too.
Use it well
- Rates are refreshed every 15 minutes — cache responses for at least 5 minutes.
- One /v1/rates call returns every currency: use it instead of a call per pair.
- Call the API from your server and cache the result there, not from every visitor's browser.
Errors
An error comes with an HTTP status and a JSON body with an error object:
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | missing_parameter | A required parameter is missing. |
| 400 | invalid_request_error | unknown_currency | A currency code isn't in /v1/currencies. |
| 400 | invalid_request_error | invalid_amount, invalid_parameter | A number is out of range. |
| 404 | not_found_error | not_found, history_unavailable | No such endpoint, or no history for the pair. |
| 429 | rate_limit_error | rate_limited | More than 30 requests a minute. |
| 429 | rate_limit_error | daily_limit_exceeded | 1,000 requests from your IP today. |
| 429 | rate_limit_error | daily_budget_exhausted | The shared daily budget is used up. |
| 503 | api_error | rates_unavailable | No fresh rate right now; retry in a minute. |
{
"error": {
"type": "invalid_request_error",
"code": "unknown_currency",
"message": "Unknown currency 'XYZ'. See /v1/currencies.",
"param": "to",
"docs": "https://ccvia.app/developers"
}
}/v1/ratesLatest rates
Mid-market rates of every currency against one base. Each rate is the median of several public sources (central banks, exchanges) and is refreshed every 15 minutes.
Parameters
baseoptionalCurrency the rates are quoted against. Default USD.
symbolsoptionalComma-separated codes to return. Default: all currencies.
Response
baseBase currency.
updated_atWhen the rates were refreshed (ISO 8601, UTC).
ratesUnits of each currency for 1 base, 8 significant digits.
curl "https://currency-backend.currency-backend.workers.dev/v1/rates?base=USD&symbols=EUR,GBP,JPY"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/rates?base=USD&symbols=EUR,GBP,JPY"); const data = await res.json(); console.log(data.rates.EUR); // 0.89046402
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/rates", params={"base": "USD", "symbols": "EUR,GBP,JPY"}) print(r.json()["rates"]["EUR"])
{
"base": "USD",
"updated_at": "2026-10-17T22:17:28.819Z",
"rates": {
"EUR": 0.89046402,
"GBP": 0.75063241,
"JPY": 159.29975
}
}/v1/convertConvert
Converts an amount at the mid-market rate: the rate banks trade at between themselves, with no fees or markup.
Parameters
fromrequiredCurrency you have.
torequiredCurrency you want.
amountoptionalAmount in from. Default 1.
Response
rateUnits of to for 1 from.
resultamount × rate, rounded to the currency's usual precision.
updated_atWhen the rate was refreshed.
curl "https://currency-backend.currency-backend.workers.dev/v1/convert?from=USD&to=EUR&amount=100"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/convert?from=USD&to=EUR&amount=100"); const { result } = await res.json(); console.log(result); // 89.05
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/convert", params={"from": "USD", "to": "EUR", "amount": "100"}) print(r.json()["result"])
{
"from": "USD",
"to": "EUR",
"amount": 100,
"rate": 0.89046402,
"result": 89.05,
"updated_at": "2026-10-17T22:17:28.819Z"
}/v1/quoteCompare transfer services
How much the recipient gets through each transfer service, exchange and wallet we track, after their fees and exchange rate markups. Sorted from best to worst. If no service converts the pair directly, you get chains through 2–3 services instead (RUB → USDT → USDS), the cheapest first.
Parameters
fromrequiredCurrency sent.
torequiredCurrency received.
amountoptionalAmount sent, in from. Default: about 100 USD.
chainsoptional1 — also return chains when direct services exist. Without direct services chains always come back.
langoptionalLanguage: en, es, pt, fr, de, it, ru, tr, ar, hi, id, zh, ja, ko. Default en.
Response
mid_rateMid-market rate, for reference.
providers[].receivedWhat the recipient gets, in to.
providers[].fee_pctTotal cost against the mid-market rate, in % of the amount sent.
providers[].estimatetrue when part of the cost is our estimate rather than a published tariff.
providers[].urlLink to the service.
chains[]When no service converts the pair directly (or with chains=1): up to 3 paths through 2–3 services via USDT, USDC, USD, EUR, BTC or ETH, the cheapest first. Three steps only when two aren't possible.
chains[].pathCurrencies along the way, e.g. RUB, USDT, USDS.
chains[].received, fee_pctWhat the recipient gets at the end, and the total cost against the mid-market rate of the whole pair.
chains[].legs[]Each step: from, to, amount, received, fee_pct and the provider (id, name, url). Every step is a service's own conversion; the next step starts with exactly what the previous one paid out.
converter_urlThe same comparison in the CC app.
curl "https://currency-backend.currency-backend.workers.dev/v1/quote?from=USDT&to=EUR&amount=500"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/quote?from=USDT&to=EUR&amount=500"); const { providers } = await res.json(); const best = providers[0]; console.log(`${best.name}: ${best.received} EUR (−${best.fee_pct}%)`);
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/quote", params={"from": "USDT", "to": "EUR", "amount": "500"}) for p in r.json()["providers"]: print(p["name"], p["received"], p["fee_pct"])
{
"from": "USDT",
"to": "EUR",
"amount": 500,
"mid_rate": 0.89046402,
"mid_result": 445.23,
"updated_at": "2026-10-17T22:17:28.819Z",
"providers": [
{
"id": "etherfi",
"name": "Ether.fi",
"website": "ether.fi",
"received": 444.56,
"fee_pct": 0.15,
"estimate": false,
"best": true,
"url": "https://ccvia.app/go/etherfi?from=USDT&to=EUR&src=api"
},
{
"id": "kucoin",
"name": "KuCoin",
"website": "kucoin.com",
"received": 443.65,
"fee_pct": 0.35,
"estimate": false,
"best": false,
"url": "https://ccvia.app/go/kucoin?from=USDT&to=EUR&src=api"
}
],
"converter_url": "https://ccvia.app/?from=USDT&to=EUR&amount=500"
}{
"from": "RUB",
"to": "USDS",
"amount": 10000,
"mid_rate": 0.01196115,
"mid_result": 119.61,
"updated_at": "2026-10-18T12:17:28.819Z",
"providers": [],
"chains": [
{
"received": 113.62,
"fee_pct": 5.01,
"estimate": false,
"best": true,
"path": [
"RUB",
"USDT",
"USDS"
],
"legs": [
{
"from": "RUB",
"to": "USDT",
"amount": 10000,
"received": 115.02,
"fee_pct": 3.84,
"estimate": false,
"provider": {
"id": "altyn",
"name": "Altyn Wallet",
"website": "altyn.one",
"url": "https://ccvia.app/go/altyn?from=RUB&to=USDT&src=api"
}
},
{
"from": "USDT",
"to": "USDS",
"amount": 115.02,
"received": 113.62,
"fee_pct": 1.22,
"estimate": false,
"provider": {
"id": "kucoin",
"name": "KuCoin",
"website": "kucoin.com",
"url": "https://ccvia.app/go/kucoin?from=USDT&to=USDS&src=api"
}
}
]
}
],
"converter_url": "https://ccvia.app/?from=RUB&to=USDS&amount=10000"
}/v1/historyRate history
Daily mid-market rates for up to a year, or weekly (Thursdays) for up to five years.
Parameters
fromrequiredBase currency.
torequiredQuote currency.
daysoptionalHow far back, 1–1825. Up to 365 — daily points, more — weekly. Default 30.
Response
intervalday or week. Weekly history isn't available for every pair; then you get daily points for the last year.
pointsdate (YYYY-MM-DD, UTC) and rate (units of to for 1 from), oldest first.
curl "https://currency-backend.currency-backend.workers.dev/v1/history?from=USD&to=EUR&days=7"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/history?from=USD&to=EUR&days=7"); const { points } = await res.json(); console.log(points.at(-1)); // { date, rate }
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/history", params={"from": "USD", "to": "EUR", "days": "7"}) for p in r.json()["points"]: print(p["date"], p["rate"])
{
"from": "USD",
"to": "EUR",
"interval": "day",
"points": [
{
"date": "2026-10-14",
"rate": 0.891987
},
{
"date": "2026-10-15",
"rate": 0.890861
},
{
"date": "2026-10-16",
"rate": 0.88987
}
]
}/v1/currenciesCurrencies
Every supported currency with its name and type.
Parameters
langoptionalLanguage of the names: en, es, pt, fr, de, it, ru, tr, ar, hi, id, zh, ja, ko. Default en.
Response
codeCode for the other endpoints (case-insensitive).
typefiat, crypto, stablecoin or metal.
decimalsDigits usually shown for amounts.
curl "https://currency-backend.currency-backend.workers.dev/v1/currencies"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/currencies"); const { currencies } = await res.json(); console.log(currencies.length);
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/currencies") print(len(r.json()["currencies"]))
{
"currencies": [
{
"code": "USD",
"name": "Dollar",
"type": "fiat",
"decimals": 2
},
{
"code": "BTC",
"name": "Bitcoin",
"type": "crypto",
"decimals": 8
},
{
"code": "USDT",
"name": "Tether",
"type": "stablecoin",
"decimals": 2
},
{
"code": "XAU",
"name": "Gold",
"type": "metal",
"decimals": 4
}
]
}/v1/providersProviders
Transfer services, exchanges and wallets compared in /v1/quote, with the currencies each of them accepts and pays out.
Response
sendCurrencies you can send from.
receiveCurrencies they pay out in.
urlLink to the service.
curl "https://currency-backend.currency-backend.workers.dev/v1/providers"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/providers"); const { providers } = await res.json(); console.log(providers.map(p => p.name));
import requests r = requests.get("https://currency-backend.currency-backend.workers.dev/v1/providers") print([p["name"] for p in r.json()["providers"]])
{
"providers": [
{
"id": "kucoin",
"name": "KuCoin",
"website": "kucoin.com",
"url": "https://ccvia.app/go/kucoin?src=api",
"send": [
"EUR",
"USDT",
"BTC"
],
"receive": [
"EUR",
"USDT",
"BTC"
]
}
]
}/v1/usageUsage
How much of today's limits your IP address has used. The call itself counts as a request.
Response
per_ipYour requests today and your daily limit.
sharedAll users' requests today and the shared limit.
reset_atWhen the counters reset.
curl "https://currency-backend.currency-backend.workers.dev/v1/usage"
const res = await fetch("https://currency-backend.currency-backend.workers.dev/v1/usage"); const { per_ip } = await res.json(); console.log(per_ip.limit - per_ip.used, "requests left today");
import requests u = requests.get("https://currency-backend.currency-backend.workers.dev/v1/usage").json() print(u["per_ip"]["limit"] - u["per_ip"]["used"], "requests left today")
{
"date": "2026-10-17",
"reset_at": "2026-10-18T00:00:00.000Z",
"per_ip": {
"used": 12,
"limit": 1000
},
"shared": {
"used": 4810,
"limit": 20000
},
"per_minute": 30
}Terms
- Free for personal and commercial projects. Please credit CC with a link to ccvia.app next to the data.
- Rates are indicative mid-market rates, not an offer. Provider fees are estimated from published tariffs and our own measurements; the final amount is shown in the provider's app.
- Limits may change. Incompatible changes will come as a new version (/v2); v1 will keep working.
- No warranty: build your product so that it survives a few minutes without rates.
OpenAPI
Machine-readable description of the API (OpenAPI 3.1): import it into Postman, Insomnia or a code generator.