Docs › Currencies API
The Currencies API is a read-only currency conversion and exchange rate API. Every endpoint returns JSON, authenticates via a single bearer token, and responds instantly. No SDK is required.
Overview
The Currencies API turns one currency amount into another and exposes the exchange-rate
tables behind it. It's a small, process-style surface: three GET
endpoints (convert, rates, and
symbols) that respond instantly; a request never
waits on an upstream provider. If you're after a currency converter API or a spot
exchange-rate lookup, this is the whole surface. Both fiat and crypto codes are supported (e.g.
USD, EUR,
BTC, ETH).
-
Read-only. All endpoints are
GET. There is no POST/PUT/DELETE surface. -
JSON-only responses.
Content-Type: application/jsonon every response. Successful payloads sit under a top-leveldatakey with the result rows indata.objects; failures come back undererrorsas an array of objects carrying amessage. -
Explicit freshness. Rates refresh on your
plan's cadence (see Freshness & as_of), and every object carries
an
as_ofstamp of when its rate was sourced. -
Case-insensitive codes.
usdandUSDare equivalent on input; codes are always echoed uppercase.
Try it now
Two ways to try the Currencies API right now. Neither requires an account.
1. Fire a live request from this page. Click the button below. The response slides open in the Explorer panel.
// Demo key: no account required.
const response = await fetch(
'https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100',
{ headers: { 'Authorization': 'Bearer rc_live_demo' } }
);
const data = await response.json();
2. Run it from your terminal.
Click the copy button below, then paste anywhere
curl lives.
curl "https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100&pretty=1" \
-H "Authorization: Bearer rc_live_demo"
The demo key returns a real, correctly-shaped sample plus a
data._demo notice block; it never touches your account or quota.
Quick start
Sign up to get an API key, then paste this into your terminal (swap out the API key placeholder):
curl "https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100" \
-H "Authorization: Bearer YOUR_API_KEY"
You'll get a single-object data.objects array back with the
converted result. From there, browse the
endpoint sections for the full surface, or the
Response objects reference to see every field.
Base URL & versioning
All endpoints live under:
https://api.restcountries.com/currencies/v1
The v1 path segment is the API version. It's the current, stable
version; backwards-compatible additions (new fields, new endpoints, new currencies) ship inside
it without a bump. See Versioning.
Append ?pretty to any request to pretty-print the JSON response,
which is handy while exploring from a browser or terminal.
Business-plan customers also get a dedicated EU-region endpoint at a separate
hostname (eu-west-1) for in-region routing.
Convert
Converts an amount from one currency into one or more targets.
from and to are required;
amount is optional and defaults to 1, so
omitting it returns the unit rate as result.
to accepts a comma-separated list of up to 5
currencies, and the response carries one object per target, in the order
requested. The call is all-or-nothing: an unknown
from or any unknown to, or more than 5
targets, fails the whole request with a 400. Both fiat and
crypto codes are accepted.
Path examples
Query parameters
from
string
required
USD).
Accepted case-insensitively. Also accepted under the alias
base.
to
string
required
EUR or EUR,CAD).
Returns one object per target. More than 5 targets fails the request.
amount
number
1 when omitted.
Example
curl "https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100&pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
)
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); });
import requests
response = requests.get(
'https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_API_KEY']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);
require 'net/http'
require 'json'
uri = URI('https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
package main
import (
"encoding/json"
"io"
"net/http"
)
req, _ := http.NewRequest("GET", "https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]interface{}
json.Unmarshal(body, &data)
use reqwest::blocking::Client;
use serde_json::Value;
let client = Client::new();
let response = client
.get("https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100")
.header("Authorization", "Bearer YOUR_API_KEY")
.send()?;
let data: Value = response.json()?;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100"))
.header("Authorization", "Bearer YOUR_API_KEY")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_KEY");
var data = await client.GetFromJsonAsync<JsonElement>(
"https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/currencies/v1/convert?from=USD&to=EUR&amount=100")!)
request.addValue("Bearer YOUR_API_KEY", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let json = try JSONSerialization.jsonObject(with: data)
{
"data": {
"objects": [
{
"from": { "code": "USD", "name": "United States dollar", "symbol": "$" },
"to": { "code": "EUR", "name": "Euro", "symbol": "€" },
"amount": 100,
"rate": 0.8772645,
"result": 87.72645,
"as_of": 1782691200
}
]
}
}
With multiple targets, data.objects holds one object per
to currency, each with its own
rate and result.
Rates
Returns the full exchange-rate table for a base currency: every supported currency expressed as
units per one unit of the base. Supply the base as the path slug
(/rates/USD) or as the base query
parameter (/rates?base=USD); when both are given, the path wins. A
bare /rates with no base resolves to a
400. Every key in the returned
rates map is itself a queryable base, so any code you see here you
can also request a table for.
Path examples
Query parameters
base
string
required
/rates/USD); the path slug takes precedence when
both are present. Also accepted under the alias from.
Example
curl "https://api.restcountries.com/currencies/v1/rates/USD?pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/currencies/v1/rates/USD',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/currencies/v1/rates/USD',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
)
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); });
import requests
response = requests.get(
'https://api.restcountries.com/currencies/v1/rates/USD',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/currencies/v1/rates/USD');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_API_KEY']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);
require 'net/http'
require 'json'
uri = URI('https://api.restcountries.com/currencies/v1/rates/USD')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
package main
import (
"encoding/json"
"io"
"net/http"
)
req, _ := http.NewRequest("GET", "https://api.restcountries.com/currencies/v1/rates/USD", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]interface{}
json.Unmarshal(body, &data)
use reqwest::blocking::Client;
use serde_json::Value;
let client = Client::new();
let response = client
.get("https://api.restcountries.com/currencies/v1/rates/USD")
.header("Authorization", "Bearer YOUR_API_KEY")
.send()?;
let data: Value = response.json()?;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.restcountries.com/currencies/v1/rates/USD"))
.header("Authorization", "Bearer YOUR_API_KEY")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_KEY");
var data = await client.GetFromJsonAsync<JsonElement>(
"https://api.restcountries.com/currencies/v1/rates/USD");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/currencies/v1/rates/USD")!)
request.addValue("Bearer YOUR_API_KEY", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let json = try JSONSerialization.jsonObject(with: data)
{
"data": {
"objects": [
{
"base": "USD",
"as_of": 1782691200,
"rates": {
"EUR": 0.8772645,
"GBP": 0.75560016,
"CAD": 1.42319257,
"JPY": 162.17946627
}
}
]
}
}
Abbreviated above; the live rates map carries one entry per
supported currency, and every code in it is itself a queryable base.
Symbols
Returns the supported-currency catalog: one object per currency
(code, name,
symbol), sorted by code.
symbol is null where the currency has no
conventional sign (e.g. XAU, gold). Takes no parameters beyond the
key. Every code listed here is usable as a from,
to, or base elsewhere in the API, so this
is the canonical list of what the Currencies API supports.
Path examples
Example
curl "https://api.restcountries.com/currencies/v1/symbols?pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/currencies/v1/symbols',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/currencies/v1/symbols',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
)
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); });
import requests
response = requests.get(
'https://api.restcountries.com/currencies/v1/symbols',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/currencies/v1/symbols');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_API_KEY']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);
require 'net/http'
require 'json'
uri = URI('https://api.restcountries.com/currencies/v1/symbols')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_KEY'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
package main
import (
"encoding/json"
"io"
"net/http"
)
req, _ := http.NewRequest("GET", "https://api.restcountries.com/currencies/v1/symbols", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var data map[string]interface{}
json.Unmarshal(body, &data)
use reqwest::blocking::Client;
use serde_json::Value;
let client = Client::new();
let response = client
.get("https://api.restcountries.com/currencies/v1/symbols")
.header("Authorization", "Bearer YOUR_API_KEY")
.send()?;
let data: Value = response.json()?;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.restcountries.com/currencies/v1/symbols"))
.header("Authorization", "Bearer YOUR_API_KEY")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_API_KEY");
var data = await client.GetFromJsonAsync<JsonElement>(
"https://api.restcountries.com/currencies/v1/symbols");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/currencies/v1/symbols")!)
request.addValue("Bearer YOUR_API_KEY", forHTTPHeaderField: "Authorization")
let (data, _) = try await URLSession.shared.data(for: request)
let json = try JSONSerialization.jsonObject(with: data)
{
"data": {
"objects": [
{ "code": "AED", "name": "United Arab Emirates dirham", "symbol": "د.إ" },
{ "code": "EUR", "name": "Euro", "symbol": "€" },
{ "code": "GBP", "name": "Pound sterling", "symbol": "£" },
{ "code": "USD", "name": "United States dollar", "symbol": "$" },
{ "code": "XAU", "name": "Gold (troy ounce)", "symbol": null }
]
}
}
Abbreviated above; the live catalog returns one object per supported currency, sorted by
code.
Response objects
Every endpoint answers with a data.objects array. The three
endpoints each return a distinct object shape, described below. Currency codes are always
uppercase on output regardless of request casing.
Convert object
from
object
{ code, name, symbol } object,
matching the symbols shape.
to
object
{ code, name, symbol } object.
amount
number
amount you sent,
or 1 by default).
rate
number
as_of timestamp.
result
number
amount × rate.
as_of
integer
Rates object
base
string
as_of
integer
rates
object
base). Every key is itself a queryable base.
Symbol object
code
string
USD,
BTC).
name
string
United States dollar).
symbol
string
$,
€), or null where the currency
has no conventional sign.
Freshness & as_of
How often rates update depends on
your plan: free plans get 24-hour updates, and paid plans get 1-hour
updates. Every convert and
rates object carries an as_of timestamp
naming when its rate was sourced, so you always know how fresh a number is.
as_of is a unix timestamp in seconds (e.g.
1782691200) marking which refresh a value came from, so it
reflects your plan's update cadence.
Errors
Error responses use standard HTTP status codes plus a JSON body that names what went wrong.
Each entry in the errors array includes a
message describing the failure.
{
"errors": [
{
"message": "A \"to\" currency is required (for example ?to=EUR)."
}
]
}
Most currency-specific failures are 400s raised while validating
the request, each with a plain-language message:
convertis missingfromorto.from, or anytotarget, names a currency that isn't supported.- The
tolist has more than 5 targets. amountisn't a valid number.ratesresolved no base (a bare/rates), or the base currency isn't supported.
| Status | Meaning |
|---|---|
200 | OK. |
400 | Malformed request: missing or unsupported
from/to/base, too many
to targets, or a non-numeric amount (see the list above). |
401 | Missing, unrecognized, revoked, or
expired API key, across both Authorization header and ?api-key=
query-param forms. |
403 | Forbidden. Returned when the account is frozen (e.g. monthly request limit exceeded). Frozen accounts clear on the monthly billing anniversary or when you upgrade. |
404 | Path doesn't match a known endpoint. |
405 | HTTP method not allowed. Currencies
endpoints are GET-only. |
410 | API version is no longer active. |
429 | Too Many Requests. Returned by the edge rate limiter when sustained traffic exceeds 20 requests per 10 seconds. |
500 | Unhandled server error. |
Webhooks
Subscribe one or more URLs to receive a signed POST when Currencies API data changes: new
rates publishing (rates.updated), a currency joining
or leaving the supported set (currency.added,
currency.removed), or a currency's metadata changing
(currency.updated). Webhooks are a Business plan feature.
Full reference (event types, payload schemas, signature verification, delivery and retry semantics, idempotency, and local testing) lives on its own page: /docs/webhooks.
Data sources
Exchange rates refresh on your plan's cadence (every 24 hours on free
plans, every hour on paid plans), and every object carries an
as_of stamp naming the refresh
its rates came from. Coverage spans both fiat and crypto currencies.