Docs › Countries API
The Countries API is a read-only API for country data. Every endpoint returns JSON, authenticates via a single bearer token, and runs against the same dataset as the demos on the homepage. No SDK is required.
Overview
The Countries API exposes one resource (countries) across a small surface of
endpoints. The dataset covers 90+ normalized fields per country (codes, capitals, currencies,
languages, borders, geography, memberships, locale conventions, and links). Static fields are
reviewed weekly against ISO and UN sources; dynamic fields such as
population sync every 4 hours. Premium dynamic fields such as
leaders use the same cadence and are available on paid plans.
-
Read-only. All endpoints are
GET. There is no POST/PUT/DELETE surface. -
JSON-only responses.
Content-Type: application/jsonon every response. Response shapes follow the conventions of the JSON:API specification: successful payloads sit under a top-leveldatakey (with response metadata nested insidedata.metawhen relevant), and failures come back undererrorsas an array of objects carrying amessage. We don't claim strict spec compliance, but if you've worked with JSON:API the shape will feel familiar. -
Stable v5. Breaking changes will ship as a new version (e.g.
/v6), with indefinite support for previous versions. See Versioning.
Try it now
Two ways you can try out the Countries 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/countries/v5/names.common/Canada',
{ 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/countries/v5/names.common/Canada?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/countries/v5/names.common/Canada" \
-H "Authorization: Bearer YOUR_API_KEY"
You'll get a single-record data.objects array back. From there,
jump to the endpoint sections for the full surface, or browse the
Field reference to see what's in each record.
Base URL & versioning
All endpoints live under:
https://api.restcountries.com/countries/v5
The v5 path segment is the API version. REST Countries commits to
backwards-compatible additions within a major version (new fields, new endpoints, new query
params). 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.
List
Returns a paginated list of all countries. Defaults to limit=25;
max limit is 100 on the free plan, or up to 500 on a paid plan.
Adding q or any searchable
property as a query parameter narrows the list. See Search.
Path examples
Query parameters
limitintegeroffsetintegerqstring{property}stringregion, names.common) used as a
substring filter. Combinable with q and other property
filters; all clauses AND together.
response_fieldsstringresponse_fields=names.common,codes.alpha_2,flag.emoji
returns objects with exactly those three keys). Comma-separated; ordering doesn't
matter. Omit the parameter to return every field. See
Field reference for the full property list.
response_fields_omitstringresponse_fields_omit=names.translations returns the full
object minus that branch). Comma-separated dot-paths; ordering doesn't matter. Composes
with response_fields: when both are present, the response is
first projected to response_fields, then
response_fields_omit prunes from that subset (so the omit
list wins on conflict).
Useful for trimming heavy nested branches like
names.translations without listing every other field you
want.
prettyboolean?pretty) and truthy values
(true, 1)
enable; anything else (including
false and 0)
leaves the response compact.
Example
curl "https://api.restcountries.com/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3&pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3',
{ 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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3');
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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3')
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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3", 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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3")
.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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3"))
.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/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/countries/v5?response_fields=names.common,codes.alpha_2,flag.emoji&limit=3")!)
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": [
{ "names": { "common": "Afghanistan" }, "codes": { "alpha_2": "AF" }, "flag": { "emoji": "🇦🇫" } },
{ "names": { "common": "Albania" }, "codes": { "alpha_2": "AL" }, "flag": { "emoji": "🇦🇱" } },
{ "names": { "common": "Algeria" }, "codes": { "alpha_2": "DZ" }, "flag": { "emoji": "🇩🇿" } }
],
"meta": { "total": 249, "count": 3, "limit": 3, "offset": 0, "more": true }
}
}
Search
Free-text search across every searchable property. Substring match,
case-insensitive. + and %20 are both
decoded as spaces. An empty q returns
400.
Combinable with one or more property filters as
/countries/v5?q=...&{property}=... to AND-narrow the result set.
For searches scoped to a named property bundle (e.g. all name fields, all code fields), see
Aggregate. For a single-property search, see
Search by property.
Path examples
Query parameters
q
string
One-of required
400
if explicitly empty (e.g. ?q=).
{property}
string
One-of required
region=Europe,
names.common=ger). Multiple property filters AND together;
combining with q AND-narrows further.
limitintegeroffsetintegerresponse_fieldsstringresponse_fields=names.common,codes.alpha_2,flag.emoji
returns objects with exactly those three keys). Comma-separated; ordering doesn't
matter. Omit the parameter to return every field. See
Field reference for the full property list.
response_fields_omitstringresponse_fields_omit=names.translations returns the full
object minus that branch). Comma-separated dot-paths; ordering doesn't matter. Composes
with response_fields: when both are present, the response is
first projected to response_fields, then
response_fields_omit prunes from that subset (so the omit
list wins on conflict).
Useful for trimming heavy nested branches like
names.translations without listing every other field you
want.
prettyboolean?pretty) and truthy values
(true, 1)
enable; anything else (including
false and 0)
leaves the response compact.
Example
curl "https://api.restcountries.com/countries/v5?q=germ®ion=Europe&pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/countries/v5?q=germ®ion=Europe',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/countries/v5?q=germ®ion=Europe',
{ 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/countries/v5?q=germ®ion=Europe',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/countries/v5?q=germ®ion=Europe');
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/countries/v5?q=germ®ion=Europe')
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/countries/v5?q=germ®ion=Europe", 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/countries/v5?q=germ®ion=Europe")
.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/countries/v5?q=germ®ion=Europe"))
.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/countries/v5?q=germ®ion=Europe");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/countries/v5?q=germ®ion=Europe")!)
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": [
{ "names": { "common": "Germany" }, "region": "Europe", "flag": { "emoji": "🇩🇪" } }
],
"meta": { "total": 1, "count": 1, "limit": 25, "offset": 0, "more": false }
}
}
Aggregate
Search across a named bundle of properties in one call. An object matches if any one of the
underlying properties contains q (substring, case-insensitive). Two
aggregates are predefined:
| Aggregate | Searches across |
|---|---|
name | names.common, names.official,
names.alternates, names.native |
code | codes.alpha_2, codes.alpha_3,
codes.ccn3, codes.fips, codes.gec,
codes.fifa, codes.cioc |
Note: names.translations is intentionally
excluded from the name aggregate. Translations cover dozens of
languages per country, so substring matches there produce too many false positives to be useful
in a generic name search. Use Search by property against
names.translations directly if you need to query translations
specifically.
Empty q returns 400. If the path
segment isn't a known aggregate name, the request falls through to
Search by property.
Path examples
Query parameters
q
string
Required
400 if explicitly
empty.
limitintegeroffsetintegerresponse_fieldsstringresponse_fields=names.common,codes.alpha_2,flag.emoji
returns objects with exactly those three keys). Comma-separated; ordering doesn't
matter. Omit the parameter to return every field. See
Field reference for the full property list.
response_fields_omitstringresponse_fields_omit=names.translations returns the full
object minus that branch). Comma-separated dot-paths; ordering doesn't matter. Composes
with response_fields: when both are present, the response is
first projected to response_fields, then
response_fields_omit prunes from that subset (so the omit
list wins on conflict).
Useful for trimming heavy nested branches like
names.translations without listing every other field you
want.
prettyboolean?pretty) and truthy values
(true, 1)
enable; anything else (including
false and 0)
leaves the response compact.
Example
curl "https://api.restcountries.com/countries/v5/name?q=can&pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/countries/v5/name?q=can',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/countries/v5/name?q=can',
{ 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/countries/v5/name?q=can',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/countries/v5/name?q=can');
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/countries/v5/name?q=can')
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/countries/v5/name?q=can", 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/countries/v5/name?q=can")
.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/countries/v5/name?q=can"))
.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/countries/v5/name?q=can");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/countries/v5/name?q=can")!)
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": [
{ "names": { "common": "Canada" }, "flag": { "emoji": "🇨🇦" } },
{ "names": { "common": "Cape Verde" }, "flag": { "emoji": "🇨🇻" } },
{ "names": { "common": "Cameroon" }, "flag": { "emoji": "🇨🇲" } }
],
"meta": { "total": 3, "count": 3, "limit": 25, "offset": 0, "more": false }
}
}
Read by property
{value} segment must equal the property's value in full. For
substring matching, use Search by property.
Look up countries by any readable property. Works equally well for unique identifiers
(codes.alpha_2/CA, names.common/Canada),
group properties (region/Europe), and reverse lookups on array
fields (borders/CAN returns the countries that border Canada).
+ and %20 are both decoded as spaces, so
capitals/new+delhi,
capitals/new%20delhi, and
capitals/new delhi all resolve.
Append ?q=... to further narrow the matched set against the
searchable properties (e.g. /region/Europe?q=germ).
Path examples
Query parameters
qstring/region/Europe?q=germ).
limitintegeroffsetintegerresponse_fieldsstringresponse_fields=names.common,codes.alpha_2,flag.emoji
returns objects with exactly those three keys). Comma-separated; ordering doesn't
matter. Omit the parameter to return every field. See
Field reference for the full property list.
response_fields_omitstringresponse_fields_omit=names.translations returns the full
object minus that branch). Comma-separated dot-paths; ordering doesn't matter. Composes
with response_fields: when both are present, the response is
first projected to response_fields, then
response_fields_omit prunes from that subset (so the omit
list wins on conflict).
Useful for trimming heavy nested branches like
names.translations without listing every other field you
want.
prettyboolean?pretty) and truthy values
(true, 1)
enable; anything else (including
false and 0)
leaves the response compact.
Example
curl "https://api.restcountries.com/countries/v5/capitals/Tokyo?pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/countries/v5/capitals/Tokyo',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/countries/v5/capitals/Tokyo',
{ 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/countries/v5/capitals/Tokyo',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/countries/v5/capitals/Tokyo');
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/countries/v5/capitals/Tokyo')
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/countries/v5/capitals/Tokyo", 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/countries/v5/capitals/Tokyo")
.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/countries/v5/capitals/Tokyo"))
.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/countries/v5/capitals/Tokyo");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/countries/v5/capitals/Tokyo")!)
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": [
{ "names": { "common": "Japan" }, "codes": { "alpha_2": "JP" }, "flag": { "emoji": "🇯🇵" } }
],
"meta": { "total": 1, "count": 1, "limit": 25, "offset": 0, "more": false }
}
}
Returns 404 for non-existent or non-readable properties (e.g.
population, flag.emoji,
government_type). See Field reference for the
per-field readable / searchable flags.
Search by property
Substring search within a single searchable property.
/countries/v5/names.common?q=ger finds every country whose common
name contains "ger". Case-insensitive. An empty q returns
400; a non-existent or non-searchable property returns
404.
Premium searchable properties, such as leaders, are available on
Personal plans and above. Free-plan accounts receive 403 when
searching or filtering by those properties.
If the path segment is the name of an aggregate (name or
code) instead of a real property, the search fans out across that
aggregate's underlying property list. See Search.
Path examples
Query parameters
q
string
Required
400 if explicitly empty.
limitintegeroffsetintegerresponse_fieldsstringresponse_fields=names.common,codes.alpha_2,flag.emoji
returns objects with exactly those three keys). Comma-separated; ordering doesn't
matter. Omit the parameter to return every field. See
Field reference for the full property list.
response_fields_omitstringresponse_fields_omit=names.translations returns the full
object minus that branch). Comma-separated dot-paths; ordering doesn't matter. Composes
with response_fields: when both are present, the response is
first projected to response_fields, then
response_fields_omit prunes from that subset (so the omit
list wins on conflict).
Useful for trimming heavy nested branches like
names.translations without listing every other field you
want.
prettyboolean?pretty) and truthy values
(true, 1)
enable; anything else (including
false and 0)
leaves the response compact.
Example
curl "https://api.restcountries.com/countries/v5/names.common?q=ger&pretty=1" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
'https://api.restcountries.com/countries/v5/names.common?q=ger',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const data = await response.json();
fetch(
'https://api.restcountries.com/countries/v5/names.common?q=ger',
{ 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/countries/v5/names.common?q=ger',
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
data = response.json()
$ch = curl_init('https://api.restcountries.com/countries/v5/names.common?q=ger');
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/countries/v5/names.common?q=ger')
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/countries/v5/names.common?q=ger", 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/countries/v5/names.common?q=ger")
.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/countries/v5/names.common?q=ger"))
.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/countries/v5/names.common?q=ger");
import Foundation
var request = URLRequest(url: URL(string: "https://api.restcountries.com/countries/v5/names.common?q=ger")!)
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": [
{ "names": { "common": "Germany" }, "flag": { "emoji": "🇩🇪" } },
{ "names": { "common": "Niger" }, "flag": { "emoji": "🇳🇪" } },
{ "names": { "common": "Nigeria" }, "flag": { "emoji": "🇳🇬" } },
{ "names": { "common": "Algeria" }, "flag": { "emoji": "🇩🇿" } }
],
"meta": { "total": 4, "count": 4, "limit": 25, "offset": 0, "more": false }
}
}
Field reference
Every country record carries the following fields. Static fields are sourced from ISO, UN, and Wikidata and reviewed weekly (see Data sources for the full list of registries we cross-check against). Dynamic fields (e.g. population, leaders, government_type) are recomputed on a 4-hour cadence (by reconciling values across multiple authoritative upstream sources).
Names
names.common
string
Part of aggregate
names.official
string
Part of aggregate
names.alternates
string[]
Part of aggregate
names.native
object
Part of aggregate
names.translationsobjectcapitalsobject[]demonymsobjectCodes
codes.alpha_2
string
Part of aggregate
codes.alpha_3
string
Part of aggregate
codes.ccn3
string
Part of aggregate
codes.fips
string
Part of aggregate
codes.gec
string
Part of aggregate
codes.cioc
string
Part of aggregate
codes.fifa
string
Part of aggregate
Flag
flag.emoji
string
no read
no search
flag.unicode
string
no read
no search
flag.html_entity
string
no read
no search
flag.url_png
string
no read
no search
flag.url_svg
string
no read
no search
flag.description
string
no read
flag.colors.dominant
string
no read
no search
flag.colors.prominent
string
no read
no search
flag.colors.palette
object[]
no read
no search
{hex, proportion} maps — each entry pairs a hex color with
its proportion, the share (0–1) of the flag that color covers
(response example).
flag.colors.swatches
object
no read
no search
vibrant, muted,
dark_vibrant, dark_muted,
light_vibrant, light_muted), each a hex
string. All six roles are always present; a role the flag has no matching color for is null
(response example).
Geography & people
regionstringsubregionstringcontinentsstring[]landlockedbooleanbordersstring[]area.kilometers
number
no read
no search
area.miles
number
no read
no search
coordinates.lat
number
no read
no search
coordinates.lng
number
no read
no search
timezonesstring[]population
number
dynamic
no read
no search
economy.gini_coefficient
object
no read
no search
languagesarrayCurrencies & calling codes
currenciesobjectcalling_codesstring[]tldsstring[]Cars
cars.driving_sidestringcars.signsstring[]Postal codes
postal_code.format
string
no read
no search
# for digits and letters as literals (e.g.
A#A #A# for Canada). Suitable as an input-field placeholder.
Empty when no source data is available or the country does not use postal codes
(response example).
postal_code.regex
string
no read
no search
Calendar & dates
date.start_of_weekstringmonday, sunday,
saturday)
(response example /
read example).
date.academic_year_start.monthintegerdate.academic_year_start.dayintegerdate.fiscal_year_start.government.monthintegerdate.fiscal_year_start.government.dayintegerdate.fiscal_year_start.corporate.monthintegerdate.fiscal_year_start.corporate.dayintegerdate.fiscal_year_start.corporate.basisstringmandated (legally fixed),
default (a country-recommended date most corporations follow
but may opt out of), or convention (no rule; the value is
the most common practice)
(response example /
read example).
date.fiscal_year_start.personal.monthintegerdate.fiscal_year_start.personal.dayintegerNumber formatting
number_format.decimal_separatorstring. in the United States and United Kingdom,
, across most of continental Europe)
(response example /
read example).
number_format.thousands_separatorstring, in the United States,
. in Germany, a non-breaking space in France,
' in Switzerland). Empty when the country does not use a
thousands separator
(response example /
read example).
Units
units.measurement_systemstringmetric almost everywhere,
imperial in the United States, Liberia,
and Myanmar)
(response example /
read example).
units.temperature_scalestringCelsius in most of the world,
Fahrenheit in the United States and a
handful of other territories)
(response example /
read example).
Classification & memberships
classification.sovereignbooleanclassification.un_memberbooleanclassification.un_observerbooleanclassification.disputedbooleanclassification.dependencybooleanclassification.dependency_typestringcrown_dependency,
overseas_territory,
special_administrative_region, …)
(response example /
read example).
classification.iso_statusstringparent.alpha_2stringparent.alpha_3stringmemberships.unbooleanmemberships.eubooleanmemberships.eurozonebooleanmemberships.schengenbooleanmemberships.natobooleanmemberships.commonwealthbooleanmemberships.oecdbooleanmemberships.g7booleanmemberships.g20booleanmemberships.bricsbooleanmemberships.opecbooleanmemberships.african_unionbooleanmemberships.aseanbooleanmemberships.arab_leaguebooleanGovernment & links
government_type
string
dynamic
no read
leaders
object[]
dynamic
premium
no read
data.leaders
(response example).
links.official
string
no read
no search
links.wikipedia
string
no read
no search
links.open_street_maps
string
no read
no search
links.google_maps
string
no read
no search
Premium Data
Fields tagged premium are available on Personal plans and above.
In broader country responses, the field value is replaced in place. For example, leaders data will show the following (for free plans):
{
"leaders": [
{
"message": "data.leaders is only available on paid plans. Go to https://restcountries.com/plans to make updates to your account."
}
]
}
Direct searches, filters, and lookups against the premium field return an error response. For example, attempt to search or filter by leaders will show the following (for free plans):
{
"errors": [
{
"message": "The leaders field is available only on paid plans and cannot be used as a search, filter, or lookup field on your current plan. Request broader country data to receive the fallback value. Go to https://restcountries.com/plans to make updates to your account."
}
]
}
Sort order
Multi-object responses are returned in a stable order, sorted alphabetically (ascending) by
names.common. The same query returns the same objects in the same
positions across requests, which means
limit and
offset can be used to page through a
result set without races or duplicates.
Custom sort parameters aren't currently supported. If you have a use case that needs a different order (most populous first, by region then name, etc.), let us know.
Pagination
Every endpoint that returns objects accepts limit and
offset (default 0). limit defaults to
25 and can range from 1 to 100 on the free plan, or up to 500 on a paid plan. The response
carries a data.meta block describing the slice, so you can iterate
without tracking position client-side:
{
"data": {
"objects": [ ... ],
"meta": {
"total": 249,
"count": 25,
"limit": 25,
"offset": 0,
"more": true,
"request_id": "<request-id>"
}
}
}
total is the total matched count after filters;
count is the size of the returned slice;
more is shorthand for "there are records past this page".
Out-of-bounds offsets return an empty objects array (status
200), not a 404. A limit above your
plan's ceiling (100 on the free plan, 500 on a paid plan) or below 1 returns an error.
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": "Limit must be an integer between 1 and 100."
}
]
}
| Status | Meaning |
|---|---|
200 | OK. |
400 | Malformed request: empty
q, out-of-range limit, or another query-parameter validation
failure. |
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), or when a Free-plan account searches or
filters by a premium field such as leaders. Frozen accounts clear on the
monthly billing anniversary or when you upgrade. |
404 | Path doesn't match a known endpoint, or the property/aggregate isn't readable / searchable. |
405 | HTTP method not allowed. Countries
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 Countries API data changes: a
country's fields updating after a reconciliation cycle (country.updated,
country.field.changed), or a country joining or leaving the
dataset (country.added, country.removed).
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.
Free flag CDN
REST Countries also provides a free global CDN for country flag images. It serves
254 flags in PNG, JPG, GIF, and SVG formats, with raster widths from
w160 through w2560.
Each flag has 45 raster URL variations plus one SVG, which means 11,684 image URLs
are available for free across the CDN. No account is needed, no API key is required,
and the CDN can be used entirely anonymously.
That keeps teams from having to generate, resize, convert, store, and invalidate thousands of flag image variations themselves.
<img src="https://flags.restcountries.com/v5/w320/ca.png" alt="Canada flag">
Flag requests are delivered from Cloudflare edge nodes with cache rules for stable image URLs, with CloudFront as the fallback source and S3 behind that as the durable origin. The flag CDN is cookie free and GDPR compliant, and it is designed for direct browser use with no tracking.