// Get country with all code formats
const response = await fetch("https://worlddataapi.com/v1/countries/US", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await response.json();
console.log(data);
// {
// "code": "US", // Alpha-2
// "code_alpha3": "USA", // Alpha-3
// "code_numeric": 840, // Numeric
// "name": "United States",
// ...
// }
import requests
response = requests.get(
"https://worlddataapi.com/v1/countries/US",
headers={"X-API-Key": "YOUR_API_KEY"}
)
data = response.json()
print(data)
# {
# "code": "US", # Alpha-2
# "code_alpha3": "USA", # Alpha-3
# "code_numeric": 840, # Numeric
# "name": "United States",
# ...
# }
curl -X GET "https://worlddataapi.com/v1/countries/US" \
-H "X-API-Key: YOUR_API_KEY"
ISO 3166 is the international standard for country codes. Understanding when to use alpha-2 (US), alpha-3 (USA), or numeric (840) codes prevents data mismatches and makes your application interoperable with external systems.
The Challenge#
Country codes seem straightforward until you encounter real-world complexity:
External APIs use different formats. Stripe uses alpha-2 (
US), SWIFT uses alpha-3 (USA), and payment systems often use numeric (840).The ISO code for the United Kingdom is
GB, notUK. This catches developers off guard.Region codes follow different patterns per country:
US-CAfor California, butJP-13for Tokyo.Territories like Puerto Rico (
PR) and Hong Kong (HK) have their own codes, separate from their parent countries.
This tutorial walks through the ISO 3166 standard, shows you when to use each format, and demonstrates how to convert between them using World Data API.
Prerequisites#
Before starting this tutorial, you need:
A World Data API key (free tier works for this tutorial)
Basic familiarity with REST APIs
A development environment with JavaScript, Python, or curl
The Three Code Formats#
ISO 3166-1 defines three representations for each country:
| Format | Example | Length | Use Cases |
|---|---|---|---|
| Alpha-2 | US | 2 chars | Most common. URLs, APIs, user-facing |
| Alpha-3 | USA | 3 chars | Better readability. Some financial systems |
| Numeric | 840 | 3 digits | Language-independent. Payment systems |
Alpha-2 Codes#
Two-letter codes like US, GB, JP. The most widely used format:
// Alpha-2 examples
const codes = {
US: "United States",
GB: "United Kingdom", // Not 'UK'
JP: "Japan",
DE: "Germany",
CN: "China",
};
Use alpha-2 for:
API parameters and responses
URL paths (
/api/countries/US/holidays)User-facing dropdowns (with full names displayed)
Domain names (
.us,.jp)Most databases and internal storage
Watch out for:
GBis the ISO code for the United Kingdom, notUKSome codes aren't intuitive:
CH= Switzerland,ES= Spain
Alpha-3 Codes#
Three-letter codes like USA, GBR, JPN. More readable but less common:
// Alpha-3 examples
const alpha3 = {
USA: "United States",
GBR: "United Kingdom",
JPN: "Japan",
DEU: "Germany",
CHN: "China",
};
Use alpha-3 for:
Financial systems (SWIFT, some banking APIs)
Olympic country codes (close alignment)
Systems where readability matters more than brevity
Interoperability with legacy systems using this format
Numeric Codes#
Three-digit codes like 840, 826, 392. Language-independent:
// Numeric examples
const numeric = {
840: "United States",
826: "United Kingdom",
392: "Japan",
276: "Germany",
156: "China",
};
Use numeric for:
International payment systems (ISO 4217 currency codes use the same pattern)
Systems that must work across different scripts/languages
EDI (Electronic Data Interchange)
Legacy mainframe systems
Benefit: Works in systems without alphabet support. 840 is unambiguous regardless of whether the system supports Latin characters.
Converting Between Formats#
The API accepts any format and returns all three:
// All of these return the same country
await fetch("/v1/countries/JP"); // Alpha-2
await fetch("/v1/countries/JPN"); // Alpha-3
await fetch("/v1/countries/392"); // Numeric
await fetch("/v1/countries/japan"); // Name (case-insensitive)
// Response always includes all formats
// {
// "code": "JP",
// "code_alpha3": "JPN",
// "code_numeric": 392,
// "name": "Japan",
// ...
// }
Building a Converter#
async function fetchCountryCodeMap() {
const countries = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`https://worlddataapi.com/v1/countries?page=${page}&per_page=100`,
{ headers: { "X-API-Key": API_KEY } },
);
const data = await response.json();
countries.push(...data.data);
hasMore = data.pagination.page < data.pagination.total_pages;
page++;
}
// Build lookup maps
const byAlpha2 = new Map();
const byAlpha3 = new Map();
const byNumeric = new Map();
const byName = new Map();
for (const country of countries) {
byAlpha2.set(country.code, country);
byAlpha3.set(country.code_alpha3, country);
byNumeric.set(country.code_numeric, country);
byName.set(country.name.toLowerCase(), country);
}
return {
byAlpha2,
byAlpha3,
byNumeric,
byName,
// Conversion helpers
toAlpha2: (input) => {
const country = resolveCountry(input);
return country?.code;
},
toAlpha3: (input) => {
const country = resolveCountry(input);
return country?.code_alpha3;
},
toNumeric: (input) => {
const country = resolveCountry(input);
return country?.code_numeric;
},
};
function resolveCountry(input) {
if (typeof input === "number") {
return byNumeric.get(input);
}
const str = input.toString();
return (
byAlpha2.get(str.toUpperCase()) ||
byAlpha3.get(str.toUpperCase()) ||
byNumeric.get(parseInt(str)) ||
byName.get(str.toLowerCase())
);
}
}
// Usage
const codes = await fetchCountryCodeMap();
codes.toAlpha2("JPN"); // "JP"
codes.toAlpha2(392); // "JP"
codes.toAlpha2("japan"); // "JP"
codes.toAlpha3("JP"); // "JPN"
codes.toNumeric("Japan"); // 392
ISO 3166-2: Region Codes#
ISO 3166-2 extends the standard to administrative subdivisions: states, provinces, territories, prefectures, cantons, etc.
// Get a region by ISO 3166-2 code
const response = await fetch("https://worlddataapi.com/v1/regions/US-CA", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await response.json();
console.log(data);
// {
// "code": "US-CA",
// "name": "California",
// "country": "US",
// "kind": "state",
// "timezone": "America/Los_Angeles"
// }
Region Code Format#
ISO 3166-2 codes follow the pattern: {country}-{subdivision}
const regionCodes = {
"US-CA": "California",
"US-NY": "New York",
"GB-ENG": "England",
"GB-SCT": "Scotland",
"DE-BY": "Bavaria",
"JP-13": "Tokyo",
"CA-ON": "Ontario",
"AU-NSW": "New South Wales",
};
The subdivision part varies by country:
United States: 2-letter state codes (
US-CA,US-TX)Japan: 2-digit prefecture codes (
JP-13,JP-27)Germany: 2-letter state codes (
DE-BY,DE-NW)United Kingdom: 3-letter codes (
GB-ENG,GB-SCT,GB-WLS)
Listing Regions#
// Get all regions for a country
async function getRegionsForCountry(country) {
const regions = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`https://worlddataapi.com/v1/regions?country=${country}&page=${page}&per_page=100`,
{ headers: { "X-API-Key": API_KEY } },
);
const data = await response.json();
regions.push(...data.data);
hasMore = data.pagination.page < data.pagination.total_pages;
page++;
}
return regions;
}
const japanPrefectures = await getRegionsForCountry("JP");
console.log(japanPrefectures.length); // 47
Subdivision Types#
Different countries use different terminology:
// The API preserves local administrative vocabulary
const subdivisionTypes = {
US: "state",
CA: "province",
AU: "state",
JP: "prefecture",
DE: "state",
CH: "canton",
FR: "region",
GB: "country", // England, Scotland, Wales, NI
AE: "emirate",
};
// Filter regions by type
function getStatesByType(regions, kind) {
return regions.filter((r) => r.kind === kind);
}
Validation#
function isValidAlpha2(code) {
return (
typeof code === "string" &&
code.length === 2 &&
/^[A-Z]{2}$/.test(code.toUpperCase())
);
}
function isValidAlpha3(code) {
return (
typeof code === "string" &&
code.length === 3 &&
/^[A-Z]{3}$/.test(code.toUpperCase())
);
}
function isValidNumeric(code) {
const num = typeof code === "string" ? parseInt(code) : code;
return Number.isInteger(num) && num >= 1 && num <= 999;
}
function isValidRegionCode(code) {
// Format: XX-YYY where XX is country, YYY is 1-3 alphanumeric
return typeof code === "string" && /^[A-Z]{2}-[A-Z0-9]{1,3}$/i.test(code);
}
// Validate against actual data
async function validateCountryCode(code, codeMap) {
const country =
codeMap.byAlpha2.get(code.toUpperCase()) ||
codeMap.byAlpha3.get(code.toUpperCase()) ||
codeMap.byNumeric.get(parseInt(code));
return country !== undefined;
}
Database Schema#
Store country references consistently:
-- Countries table
CREATE TABLE countries (
alpha2 CHAR(2) PRIMARY KEY, -- "US"
alpha3 CHAR(3) UNIQUE NOT NULL, -- "USA"
numeric SMALLINT UNIQUE NOT NULL, -- 840
name VARCHAR(100) NOT NULL
);
-- Use alpha-2 as foreign key (most compact)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
country CHAR(2) REFERENCES countries(alpha2),
region VARCHAR(6) -- ISO 3166-2: "US-CA"
);
-- Index for lookups by any format
CREATE INDEX idx_countries_alpha3 ON countries(alpha3);
CREATE INDEX idx_countries_numeric ON countries(numeric);
TypeScript Types#
// ISO 3166-1 Alpha-2 (249 countries)
type CountryAlpha2 =
| "AD"
| "AE"
| "AF"
| "AG"
| "AI"
| "AL"
| "AM"
| "AO"
| "AQ"
| "AR"
// ... all 249 codes
| "US"
| "UY"
| "UZ"
| "VA"
| "VC"
| "VE"
| "VG"
| "VI"
| "VN"
| "VU"
| "WF"
| "WS"
| "YE"
| "YT"
| "ZA"
| "ZM"
| "ZW";
// ISO 3166-2 Region Code
type RegionCode = `${CountryAlpha2}-${string}`;
// Country object
interface Country {
code: CountryAlpha2;
code_alpha3: string;
code_numeric: number;
name: string;
official_name: string;
continent: string;
currencies: string[];
languages: string[];
timezones: string[];
neighbors: CountryAlpha2[];
}
interface Region {
code: RegionCode;
name: string;
country: CountryAlpha2;
kind: string;
timezone: string;
}
Special Cases#
Territories and Dependencies#
ISO 3166-1 includes territories separately from their parent countries:
const territories = {
PR: "Puerto Rico", // US territory
GU: "Guam", // US territory
VI: "Virgin Islands, U.S.", // US territory
HK: "Hong Kong", // China SAR
MO: "Macao", // China SAR
GI: "Gibraltar", // UK territory
};
// These have their own codes despite not being sovereign states
Former Countries#
ISO 3166-3 tracks deleted codes:
const formerCodes = {
SU: "Soviet Union", // Deleted 1992
CS: "Czechoslovakia", // Deleted 1993
YU: "Yugoslavia", // Deleted 2003
AN: "Netherlands Antilles", // Deleted 2010
};
// Never reuse deleted codes for different countries
Reserved Codes#
Some codes are reserved and never assigned:
const reservedCodes = [
"UK", // Reserved for United Kingdom (use GB instead)
"EU", // Reserved for European Union
"AA",
"QM",
"QN", // User-assigned ranges
"XA",
"XZ", // User-assigned ranges
"ZZ", // Unknown territory
];
Building a Country/Region Picker#
function LocationPicker({ onSelect }) {
const [countries, setCountries] = useState([]);
const [regions, setRegions] = useState([]);
const [selectedCountry, setSelectedCountry] = useState("");
const [selectedRegion, setSelectedRegion] = useState("");
useEffect(() => {
// Load countries once
fetchAllCountries().then(setCountries);
}, []);
useEffect(() => {
if (!selectedCountry) {
setRegions([]);
return;
}
// Load regions when country changes
getRegionsForCountry(selectedCountry).then(setRegions);
}, [selectedCountry]);
const handleCountryChange = (code) => {
setSelectedCountry(code);
setSelectedRegion("");
onSelect({ country: code, region: null });
};
const handleRegionChange = (code) => {
setSelectedRegion(code);
onSelect({ country: selectedCountry, region: code });
};
return (
<div className="location-picker">
<select
value={selectedCountry}
onChange={(e) => handleCountryChange(e.target.value)}
>
<option value="">Select country...</option>
{countries.map((c) => (
<option key={c.code} value={c.code}>
{c.name} ({c.code})
</option>
))}
</select>
{selectedCountry && regions.length > 0 && (
<select
value={selectedRegion}
onChange={(e) => handleRegionChange(e.target.value)}
>
<option value="">
Select {regions[0]?.kind || "region"}...
</option>
{regions.map((r) => (
<option key={r.code} value={r.code}>
{r.name}
</option>
))}
</select>
)}
</div>
);
}
Caching Recommendations#
Country and region codes rarely change. Cache aggressively:
// Countries: Cache for 30 days
// Regions: Cache for 30 days
// Changes happen maybe once per year (new country, territory status change)
const COUNTRY_CACHE_TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
async function getCountries() {
const cached = localStorage.getItem("countries");
if (cached) {
const { data, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp < COUNTRY_CACHE_TTL) {
return data;
}
}
const countries = await fetchAllCountries();
localStorage.setItem(
"countries",
JSON.stringify({
data: countries,
timestamp: Date.now(),
}),
);
return countries;
}
Common Pitfalls#
Pitfall 1: Using UK Instead of GB#
// Incorrect
const country = "UK";
// Correct
const country = "GB"; // ISO 3166-1 alpha-2 for United Kingdom
The code UK is explicitly reserved in ISO 3166-1 to avoid confusion, but GB (Great Britain) is the official code. However, note that Northern Ireland is part of the UK but not Great Britain—the standard is imperfect.
Pitfall 2: Assuming 2-Letter Codes Are Universal#
// Incorrect - not all external systems use alpha-2
const stripeCountry = "JP";
const swiftCountry = "JP"; // SWIFT actually uses alpha-3: JPN
// Correct - convert based on system requirements
const swiftCountry = codes.toAlpha3("JP"); // "JPN"
Pitfall 3: Hardcoding Region Codes#
// Incorrect - region codes aren't always intuitive
const regions = {
california: "US-CAL", // Wrong! It's US-CA
tokyo: "JP-TK", // Wrong! It's JP-13
};
// Correct - fetch from API or use a verified lookup
const california = await fetch("/v1/regions/US-CA");
Pitfall 4: Mixing Standards#
// Incorrect - mixing ISO with other standards
const data = {
country: "USA", // Alpha-3
region: "California", // Name, not ISO code
currency: "USD", // ISO 4217 (different standard!)
};
// Correct - be consistent within your system
const data = {
country: "US", // Alpha-2
region: "US-CA", // ISO 3166-2
currency: "USD", // ISO 4217 (related but separate)
};
Pitfall 5: Forgetting Territories Have Separate Codes#
// Incorrect - assuming territories use parent country code
const userLocation = "US"; // User is in Puerto Rico
// Correct - territories have their own codes
const userLocation = "PR"; // Puerto Rico has its own ISO code
Summary#
ISO 3166 provides three code formats for countries: alpha-2 (most common, use for APIs and databases), alpha-3 (financial systems and SWIFT), and numeric (payment systems and legacy integrations). ISO 3166-2 extends this to regions with the pattern {country}-{subdivision}.
Key takeaways:
Use alpha-2 (
US) as your default—it's compact and widely supportedConvert to alpha-3 (
USA) or numeric (840) when integrating with systems that require themRemember
GBis the ISO code for the United Kingdom, notUKRegion code formats vary by country—fetch from the API rather than hardcoding
Cache country and region data aggressively (30-day TTL is reasonable)
Ready to integrate ISO country codes? Get your free API key and start building with World Data API. The free tier includes 60 requests per day—enough to build and test your country selector.
Related tutorials:
Building a Country Selector Dropdown — User-facing country picker
Implementing City Autocomplete Search — Searching within regions
Caching Strategies for Reference Data APIs — Cache TTL patterns