Skip to content
Back to skills

Rest Countries

ASecurity

`https://restcountries.com` — open JSON API for country data. **Never use the browser.** All data is reachable via `http_get`. No auth required, no API key.

  • 9 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 27, 2026
toolsgoapiperformance

Works with

  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add yangchuansheng/browser-harness-rust --skill rest-countries --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rest Countries?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rest Countries
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yangchuansheng-rest-countries/badge)](https://www.skillsdirectory.com/skills/yangchuansheng-rest-countries)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# REST Countries — Scraping & Data Extraction

`https://restcountries.com` — open JSON API for country data. **Never use the browser.** All data is reachable via `http_get`. No auth required, no API key.

## Do this first

**Fetch all 250 countries in one call with a field filter — almost always the fastest approach.**

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/all?fields=name,cca2,capital,population,area,region")
countries = json.loads(data)
# countries is a list of 250 dicts — confirmed 2026-04-18

for c in countries:
    name       = c["name"]["common"]          # "Germany"
    official   = c["name"]["official"]        # "Federal Republic of Germany"
    code       = c["cca2"]                    # "DE"
    capital    = c["capital"][0] if c.get("capital") else None   # list — may be empty
    population = c["population"]              # 83491249
    area       = c["area"]                    # 357114.0 (km²)
    region     = c["region"]                  # "Europe"
    print(code, name, population)
# Confirmed output (first result varies — API returns unsorted):
# CI Ivory Coast 31719275
```

Use the `?fields=` query param to limit response size — essential when fetching all 250.

## Common workflows

### Lookup a single country by code (ISO 3166-1 alpha-2 or alpha-3)

```text
import json
from helpers import http_get

# Single code — returns a list (one element)
data = http_get("https://restcountries.com/v3.1/alpha/DE")
country = json.loads(data)[0]

# But: /alpha/CODE?fields=... returns a plain dict, not a list — watch for this
data2 = http_get("https://restcountries.com/v3.1/alpha/DE?fields=name,cca2,currencies,languages,flags")
country2 = json.loads(data2)          # dict, NOT list

name       = country2["name"]["common"]                             # "Germany"
currencies = country2["currencies"]                                 # {"EUR": {"name": "euro", "symbol": "€"}}
currency_codes = list(currencies.keys())                            # ["EUR"]
currency_name  = currencies["EUR"]["name"]                          # "euro"
languages  = country2["languages"]                                  # {"deu": "German"}
lang_names = list(languages.values())                               # ["German"]
flag_png   = country2["flags"]["png"]                               # "https://flagcdn.com/w320/de.png"
flag_svg   = country2["flags"]["svg"]                               # "https://flagcdn.com/de.svg"
flag_alt   = country2["flags"]["alt"]                               # description text

print(name, currency_codes, lang_names)
# Confirmed: Germany ['EUR'] ['German']
```

### Batch lookup — multiple codes in one call

Use `/alpha?codes=` for fetching a known list of countries — always returns a list.

```text
import json
from helpers import http_get

codes = ["US", "GB", "FR", "DE", "JP", "CN", "IN", "BR", "AU", "CA"]
data = http_get(f"https://restcountries.com/v3.1/alpha?codes={','.join(codes)}&fields=name,cca2,population")
countries = json.loads(data)
# Returns list, order NOT guaranteed to match input order
for c in countries:
    print(c["cca2"], c["name"]["common"], c["population"])
# Confirmed: 10 results, returned in arbitrary order
```

### Search by name

```text
import json
from helpers import http_get

# Partial match (default) — may return multiple results
data = http_get("https://restcountries.com/v3.1/name/united")
results = json.loads(data)
# Returns 7 countries: United States, UK, UAE, Tanzania, Mexico, ...

# Exact match — use fullText=true with the full common or official name
data2 = http_get("https://restcountries.com/v3.1/name/united%20kingdom?fullText=true")
results2 = json.loads(data2)
# Returns exactly 1 result: United Kingdom
print(results2[0]["name"]["common"])  # United Kingdom
```

### Filter by region

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/region/europe?fields=name,cca2,population")
countries = json.loads(data)
# 53 European countries — confirmed

# Sort by population
ranked = sorted(countries, key=lambda x: x["population"], reverse=True)
for c in ranked[:5]:
    print(c["cca2"], c["name"]["common"], f"{c['population']:,}")
# Confirmed top 5: RU Russia, DE Germany, FR France, GB United Kingdom, IT Italy
```

Valid region values: `africa`, `americas`, `asia`, `europe`, `oceania`, `antarctic`

### Filter by subregion

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/subregion/Western%20Europe?fields=name,cca2")
countries = json.loads(data)
print([c["cca2"] for c in countries])
# Confirmed: ['FR', 'NL', 'MC', 'DE', 'BE', 'LI', 'CH', 'LU']
```

### Filter by language

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/lang/arabic")
countries = json.loads(data)
print(f"Arabic-speaking countries: {len(countries)}")
# Confirmed: 25 countries

# Language param is the language name (English), not the ISO 639-3 code
# Works: arabic, french, spanish, english, portuguese, german, russian, chinese
```

### Filter by currency

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/currency/EUR")
countries = json.loads(data)
print(f"EUR countries: {len(countries)}")  # Confirmed: 36
names = [c["name"]["common"] for c in countries]
print(names[:5])

# Use ISO 4217 currency code (uppercase)
```

### Filter by capital city

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/capital/berlin?fields=name,cca2,capital")
result = json.loads(data)
print(result[0]["name"]["common"], result[0]["capital"])
# Confirmed: Germany ['Berlin']
# Capital param is case-insensitive
```

### Full country detail — all fields

```text
import json
from helpers import http_get

data = http_get("https://restcountries.com/v3.1/alpha/US")
c = json.loads(data)[0]

# Available top-level keys (confirmed for US/DE):
# name, tld, cca2, ccn3, cca3, cioc, independent, status, unMember,
# currencies, idd, capital, altSpellings, region, subregion, languages,
# translations, latlng, landlocked, borders, area, demonyms, flag (emoji),
# maps, population, gini, fifa, car, timezones, continents, flags,
# coatOfArms, startOfWeek, capitalInfo, postalCode

print(c["idd"])          # {"root": "+1", "suffixes": ["201", "202", ...]}
print(c["car"]["side"])  # "right" or "left"
print(c["gini"])         # {"2018": 41.4}  — year keyed, may be absent
print(c["timezones"])    # list of UTC offset strings
print(c["borders"])      # list of cca3 codes for bordering countries
print(c["latlng"])       # [lat, lng] of geographic center
```

## URL reference

| Endpoint | Pattern | Notes |
|---|---|---|
| All countries | `/v3.1/all` | Always add `?fields=` |
| By code | `/v3.1/alpha/{code}` | cca2 or cca3; single code → list (no fields) or dict (with fields) |
| By codes | `/v3.1/alpha?codes=DE,FR,JP` | Always returns list |
| By name | `/v3.1/name/{name}` | Partial; add `?fullText=true` for exact match |
| By region | `/v3.1/region/{region}` | africa, americas, asia, europe, oceania, antarctic |
| By subregion | `/v3.1/subregion/{subregion}` | URL-encode spaces as `%20` |
| By language | `/v3.1/lang/{language}` | English language name |
| By currency | `/v3.1/currency/{code}` | ISO 4217 (EUR, USD, GBP) |
| By capital | `/v3.1/capital/{city}` | Case-insensitive |

All endpoints accept `?fields=field1,field2,...` to limit response payload.

## Gotchas

- **`name` is a nested object, not a string.** Use `c["name"]["common"]` for the familiar English name, `c["name"]["official"]` for the full official name. `nativeName` is a dict keyed by ISO 639-3 language code.

- **`/alpha/CODE` return type depends on whether `?fields=` is present.** Without `?fields=`, returns a list (one element). With `?fields=...`, returns a plain dict. Use `json.loads(data)[0]` for the no-fields case, `json.loads(data)` for the fields case. Using `/alpha?codes=CODE` always returns a list regardless.

- **`currencies` is a dict keyed by ISO 4217 code, not a list.** `c["currencies"]["EUR"]["name"]` → `"euro"`, `c["currencies"]["EUR"]["symbol"]` → `"€"`. A country can have multiple currencies — iterate `currencies.items()`.

- **`languages` is a dict keyed by ISO 639-3 code.** `c["languages"]["deu"]` → `"German"`. Use `list(c["languages"].values())` for a simple list of language names.

- **`capital` is a list and may be empty.** Some territories (Antarctica, Bouvet Island, Macau, Heard Island) have no capital — `c.get("capital")` returns `[]`, not `None`. Guard with `c["capital"][0] if c.get("capital") else None`. South Africa has 3 capitals.

- **`gini` is a dict keyed by year string, may be absent entirely.** `c["gini"]` → `{"2016": 31.9}`. Many small countries or territories have no gini data — always check `c.get("gini")`.

- **`borders` uses cca3 codes, not cca2.** `c["borders"]` → `["AUT", "BEL", ...]`. Cross-reference with another `/alpha?codes=` call to resolve to names.

- **`translations` covers ~45 languages.** Each entry: `c["translations"]["deu"]` → `{"official": "Bundesrepublik Deutschland", "common": "Deutschland"}`. Useful for multilingual apps.

- **No rate limit headers, no documented rate limit.** In practice the API handles rapid sequential calls fine. For bulk crawling hundreds of per-country requests, add a short sleep (`time.sleep(0.5)`) between calls to be polite.

- **404 returns JSON, not HTML.** `{"message": "Not Found", "status": 404}`. Wrap calls in try/except and check for this pattern when handling user-supplied country names or codes.

- **`?fields=` is the key performance lever.** The full all-countries payload without field filtering is ~1.5 MB. With `?fields=name,cca2,population` it drops to ~50 KB. Always filter when you don't need all fields.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.