Skip to main content

ISO 3166-2 Subdivisions API — States, Provinces & Regions

REAL DATA

ISO 3166-2 states, provinces and regions: 5,046 subdivision codes for 200 countries, with English names, parent codes and an auditable per-country index.

Authoritative reference data or standards computation

Base URL
/api/country-subdivisions
Capabilities
4 routes
Last updated
July 30, 2026
Data sources · 3

GET /api/country-subdivisions

Returns matching subdivisions ordered by ISO 3166-2 code, which is also country-major. Filters compose; a zero-match filter returns an empty list plus a warning naming what was filtered. At most 100 records come back per request — use 'offset' to page.

Live requestRuns against the public API
No key required
GET/api/country-subdivisions?country=US

Request parameters

Only subdivisions of this country. Takes an ISO 3166-1 alpha-2 or alpha-3 code, case-insensitively (us, US, usa and USA all resolve to US); the canonical alpha-2 is echoed in meta.params.

Case- and diacritic-insensitive contains match on the subdivision name (zurich finds Zürich). It does not match codes — use /lookup for a code.

Only the direct children of this subdivision. Takes a CURRENT ISO 3166-2 code, accepting the same spellings as /lookup (GB-ENG, gb-eng, gb_eng, gbeng); the canonical form is echoed in meta.params. A withdrawn or unknown code is a 400, never a silent empty list.

Only subdivisions at this nesting level: 1 = contained directly by the country, 2 = nested inside another subdivision. Omitted returns both.

Skip this many matches before returning records. A response returns at most 100 subdivisions, so this is how you page through a larger match set.

Advanced response options6 options

Limit the number of returned records (1–100). Defaults to 100.

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/country-subdivisions?country=US"
const res = await fetch("https://randomapi.dev/api/country-subdivisions?country=US");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/country-subdivisions?country=US").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/country-subdivisions?country=US"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 5
country string

Only subdivisions of this country. Takes an ISO 3166-1 alpha-2 or alpha-3 code, case-insensitively (us, US, usa and USA all resolve to US); the canonical alpha-2 is echoed in meta.params.

example: country=US
search string

Case- and diacritic-insensitive contains match on the subdivision name (zurich finds Zürich). It does not match codes — use /lookup for a code.

allowed: 2 – 60
example: search=cal
parent string

Only the direct children of this subdivision. Takes a CURRENT ISO 3166-2 code, accepting the same spellings as /lookup (GB-ENG, gb-eng, gb_eng, gbeng); the canonical form is echoed in meta.params. A withdrawn or unknown code is a 400, never a silent empty list.

allowed: 3 – 12
example: parent=GB-ENG
level int

Only subdivisions at this nesting level: 1 = contained directly by the country, 2 = nested inside another subdivision. Omitted returns both.

allowed: 1 – 2
example: level=1
offset int

Skip this many matches before returning records. A response returns at most 100 subdivisions, so this is how you page through a larger match set.

default: 0
allowed: 0 – 5045
example: offset=100
Universal parameters Shared response and formatting options 6
count int

Limit the number of returned records (1–100). Defaults to 100.

default: 100
allowed: 1 – 100
example: count=3
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=code,name
exclude list

Return all fields except these (comma-separated).

example: exclude=aliasOf
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 8
code string (ISO 3166-2)

The ISO 3166-2 code in canonical uppercase-with-hyphen form: the alpha-2 country code plus 1–3 characters. Reproduced from CLDR 48's `subdivisionContainment`; see `countryCode` for what containment does and does not assert.

example: US-CA

name string

English name from CLDR, with CLDR's disambiguation mark stripped (see `cldrName`). CLDR's spelling is its own English form and is sometimes transliterated with diacritics the official English spelling omits (`IN-TS` is `Telangāna`, `JP-01` is `Hokkaidō`) — `search` folds diacritics so plain ASCII still finds them.

example: California

cldrName string

The exact string CLDR publishes for this code. Differs from `name` only for the 25 rows where CLDR appends a disambiguation mark (`²`, `³` or ` †`), e.g. `FR-IDF` → `"Île-de-France²"`. For the 19 `aliasOf` rows CLDR publishes no subdivision string at all, so this is its `<territory>` name for the alias target instead — `US-PR` is `"Puerto Rico"` from `<territory type="PR">`, and you will not find `uspr` in `common/subdivisions/en.xml`.

example: California

countryCode string (ISO 3166-1 alpha-2)

ISO 3166-1 alpha-2 of the country that CLDR 48's `subdivisionContainment` places this subdivision under — reproduced from CLDR / ISO 3166-2, never asserted by this API. Containment is a code-registry relationship, not a claim about status: `CN-HK`, `CN-MO` and `CN-TW` are listed under `CN` because ISO 3166-2:CN and CLDR list them there, while `HK`, `MO` and `TW` are simultaneously their own ISO 3166-1 rows in `/api/countries` — and `/api/country-borders` carries a 250th row, `XK`, that `/api/countries` excludes as user-assigned. Each surface reproduces its own cited source; see the neutrality statement above.

example: US

country string

English country name, joined from this project's ISO 3166-1 countries dataset.

example: United States

parentCode string (ISO 3166-2) | null nullable

The ISO 3166-2 code of the parent subdivision when CLDR nests this one, else null. Nesting is exactly two levels deep — never more.

example: GB-ENG

level integer

1 for a subdivision contained directly by the country, 2 for one contained by another subdivision. Only these two values occur (3,590 level-1 / 1,456 level-2).

example: 1

aliasOf string (ISO 3166-1 alpha-2) | null nullable

Non-null for the 19 codes CLDR declares an `overlong` alias of an ISO 3166-1 region and still lists in the containment (`US-PR` → `"PR"`, `NL-AW` → `"AW"`, `FI-01` → `"AX"`). For these, `name` is CLDR's English **territory** name, because CLDR publishes no separate subdivision name for them. null for the other 5,027.

example: null

Documented examples

Build-generated requests and complete responses
6
All 57 US states, territories and DC
GET /api/country-subdivisions?country=US
{
  "data": [
    {
      "code": "US-AK",
      "name": "Alaska",
      "cldrName": "Alaska",
      "countryCode": "US",
      "country": "United States",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "US-AL",
      "name": "Alabama",
      "cldrName": "Alabama",
      "countryCode": "US",
      "country": "United States",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "US-AR",
      "name": "Arkansas",
      "cldrName": "Arkansas",
      "countryCode": "US",
      "country": "United States",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "US-AS",
      "name": "American Samoa",
      "cldrName": "American Samoa",
      "countryCode": "US",
      "country": "United States",
      "parentCode": null,
      "level": 1,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
The four UK nations
GET /api/country-subdivisions?country=GB&level=1
{
  "data": [
    {
      "code": "GB-ENG",
      "name": "England",
      "cldrName": "England",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "GB-NIR",
      "name": "Northern Ireland",
      "cldrName": "Northern Ireland",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "GB-SCT",
      "name": "Scotland",
      "cldrName": "Scotland",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    },
    {
      "code": "GB-WLS",
      "name": "Wales",
      "cldrName": "Wales",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": null,
      "level": 1,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
English counties and unitary authorities
GET /api/country-subdivisions?parent=GB-ENG
{
  "data": [
    {
      "code": "GB-BAS",
      "name": "Bath and North East Somerset",
      "cldrName": "Bath and North East Somerset",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-BBD",
      "name": "Blackburn with Darwen",
      "cldrName": "Blackburn with Darwen",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-BCP",
      "name": "Bournemouth, Christchurch and Poole",
      "cldrName": "Bournemouth, Christchurch and Poole",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-BDF",
      "name": "Bedford",
      "cldrName": "Bedford",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
    …
    ]
  }
}
Second page of English principal areas
GET /api/country-subdivisions?parent=GB-ENG&offset=100
{
  "data": [
    {
      "code": "GB-RDB",
      "name": "Redbridge",
      "cldrName": "Redbridge",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-RDG",
      "name": "Reading",
      "cldrName": "Reading",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-RIC",
      "name": "Richmond upon Thames",
      "cldrName": "Richmond upon Thames",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
      "aliasOf": null
    },
    {
      "code": "GB-ROT",
      "name": "Rotherham",
      "cldrName": "Rotherham",
      "countryCode": "GB",
      "country": "United Kingdom",
      "parentCode": "GB-ENG",
      "level": 2,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Find Zürich without typing the umlaut
GET /api/country-subdivisions?search=zurich
{
  "data": [
    {
      "code": "CH-ZH",
      "name": "Zürich",
      "cldrName": "Zürich",
      "countryCode": "CH",
      "country": "Switzerland",
      "parentCode": null,
      "level": 1,
      "aliasOf": null
    }
  ],
  "meta": {
    "endpoint": "country-subdivisions",
    "count": 1,
    "params": {
      "search": "zurich",
      "offset": 0
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Japan's 47 prefectures as CSV
GET /api/country-subdivisions?country=JP&format=csv
code,name,cldrName,countryCode,country,parentCode,level,aliasOf
JP-01,Hokkaidō,Hokkaidō,JP,Japan,,1,
JP-02,Aomori,Aomori,JP,Japan,,1,
JP-03,Iwate,Iwate,JP,Japan,,1,
JP-04,Miyagi,Miyagi,JP,Japan,,1,
JP-05,Akita,Akita,JP,Japan,,1,
JP-06,Yamagata,Yamagata,JP,Japan,,1,
JP-07,Fukushima,Fukushima,JP,Japan,,1,
JP-08,Ibaraki,Ibaraki,JP,Japan,,1,
JP-09,Tochigi,Tochigi,JP,Japan,,1,
JP-10,Gunma,Gunma,JP,Japan,,1,
JP-11,Saitama,Saitama,JP,Japan,,1,
JP-12,Chiba,Chiba,JP,Japan,,1,
JP-13,Tokyo,Tokyo,JP,Japan,,1,
JP-14,Kanagawa,Kanagawa,JP,Japan,,1,
JP-15,Niigata,Niigata,JP,Japan,,1,
JP-16,Toyama,Toyama,JP,Japan,,1,
JP-17,Ishikawa,Ishikawa,JP,Japan,,1,
JP-18,Fukui,Fukui,JP,Japan,,1,
JP-19,Yamanashi,Yamanashi,JP,Japan,,1,
JP-20,Nagano,Nagano,JP,Japan,,1,
JP-21,Gifu,Gifu,JP,Japan,,1,
JP-22,Shizuoka,Shizuoka,JP,Japan,,1,
JP-23,Aichi,Aichi,JP,Japan,,1,
JP-24,Mie,Mie,JP,Japan,,1,
JP-25,Shiga,Shiga,JP,Japan,,1,
JP-26,Kyōto,Kyōto,JP,Japan,,1,
JP-27,Ōsaka,Ōsaka,JP,Japan,,1,
JP-28,Hyōgo,Hyōgo,JP,Japan,,1,
JP-29,Nara,Nara,JP,Japan,,1,
JP-30,Wakayama,Wakayama,JP,Japan,,1,
JP-31,Tottori,Tottori,JP,Japan,,1,
JP-32,Shimane,Shimane,JP,Japan,,1,
JP-33,Okayama,Okayama,JP,Japan,,1,
JP-34,Hiroshima,Hiroshima,JP,Japan,,1,
JP-35,Yamaguchi,Yamaguchi,JP,Japan,,1,
JP-36,Tokushima,Tokushima,JP,Japan,,1,
JP-37,Kagawa,Kagawa,JP,Japan,,1,
JP-38,Ehime,Ehime,JP,Japan,,1,
JP-39,Kōchi,Kōchi,JP,Japan,,1,
    …
JP-46,Kagoshima,Kagoshima,JP,Japan,,1,
JP-47,Okinawa,Okinawa,JP,Japan,,1,

GET /api/country-subdivisions/lookup

Resolves a single current ISO 3166-2 code and adds its direct children. A syntactically valid code that is not current is an explicit 404 that says WHY — the country has no subdivisions, the code is deprecated (with CLDR's replacement where one is recorded), or CLDR records it as an overlong alias of an ISO 3166-1 code. Never a substitute record.

Live requestRuns against the public API
No key required
GET/api/country-subdivisions/lookup?code=US-CA

Request parameters

An ISO 3166-2 code. Case-insensitive, and the separator may be a single `-`, `_` or plain space, or omitted entirely — US-CA, us-ca, us_ca, 'US CA' and usca all resolve to US-CA. Anything looser is rejected rather than quietly resolved: US--CA, U-S-A, US.CA and any other whitespace in the middle (a tab, a newline, a non-breaking space) all get a 400 that names the value. Surrounding whitespace is trimmed. The generous length bound is deliberate: it lets a wrong-shaped value get an error that names the value and the rule, instead of a bare length complaint.

Advanced response options4 options

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/country-subdivisions/lookup?code=US-CA"
const res = await fetch("https://randomapi.dev/api/country-subdivisions/lookup?code=US-CA");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/country-subdivisions/lookup?code=US-CA").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/country-subdivisions/lookup?code=US-CA"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 1
code string required

An ISO 3166-2 code. Case-insensitive, and the separator may be a single `-`, `_` or plain space, or omitted entirely — US-CA, us-ca, us_ca, 'US CA' and usca all resolve to US-CA. Anything looser is rejected rather than quietly resolved: US--CA, U-S-A, US.CA and any other whitespace in the middle (a tab, a newline, a non-breaking space) all get a 400 that names the value. Surrounding whitespace is trimmed. The generous length bound is deliberate: it lets a wrong-shaped value get an error that names the value and the rule, instead of a bare length complaint.

allowed: 3 – 12
example: code=US-CA
Universal parameters Shared response and formatting options 4
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=code,name
exclude list

Return all fields except these (comma-separated).

example: exclude=children
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 10
code string (ISO 3166-2)

The ISO 3166-2 code in canonical uppercase-with-hyphen form: the alpha-2 country code plus 1–3 characters. Reproduced from CLDR 48's `subdivisionContainment`; see `countryCode` for what containment does and does not assert.

example: US-CA

name string

English name from CLDR, with CLDR's disambiguation mark stripped (see `cldrName`). CLDR's spelling is its own English form and is sometimes transliterated with diacritics the official English spelling omits (`IN-TS` is `Telangāna`, `JP-01` is `Hokkaidō`) — `search` folds diacritics so plain ASCII still finds them.

example: California

cldrName string

The exact string CLDR publishes for this code. Differs from `name` only for the 25 rows where CLDR appends a disambiguation mark (`²`, `³` or ` †`), e.g. `FR-IDF` → `"Île-de-France²"`. For the 19 `aliasOf` rows CLDR publishes no subdivision string at all, so this is its `<territory>` name for the alias target instead — `US-PR` is `"Puerto Rico"` from `<territory type="PR">`, and you will not find `uspr` in `common/subdivisions/en.xml`.

example: California

countryCode string (ISO 3166-1 alpha-2)

ISO 3166-1 alpha-2 of the country that CLDR 48's `subdivisionContainment` places this subdivision under — reproduced from CLDR / ISO 3166-2, never asserted by this API. Containment is a code-registry relationship, not a claim about status: `CN-HK`, `CN-MO` and `CN-TW` are listed under `CN` because ISO 3166-2:CN and CLDR list them there, while `HK`, `MO` and `TW` are simultaneously their own ISO 3166-1 rows in `/api/countries` — and `/api/country-borders` carries a 250th row, `XK`, that `/api/countries` excludes as user-assigned. Each surface reproduces its own cited source; see the neutrality statement above.

example: US

country string

English country name, joined from this project's ISO 3166-1 countries dataset.

example: United States

parentCode string (ISO 3166-2) | null nullable

The ISO 3166-2 code of the parent subdivision when CLDR nests this one, else null. Nesting is exactly two levels deep — never more.

example: GB-ENG

level integer

1 for a subdivision contained directly by the country, 2 for one contained by another subdivision. Only these two values occur (3,590 level-1 / 1,456 level-2).

example: 1

aliasOf string (ISO 3166-1 alpha-2) | null nullable

Non-null for the 19 codes CLDR declares an `overlong` alias of an ISO 3166-1 region and still lists in the containment (`US-PR` → `"PR"`, `NL-AW` → `"AW"`, `FI-01` → `"AX"`). For these, `name` is CLDR's English **territory** name, because CLDR publishes no separate subdivision name for them. null for the other 5,027.

example: null

childCount integer

Direct children CLDR nests under this code (0 for a leaf). The maximum in this release is 152 (`GB-ENG`).

example: 152

children string[]

Those children's ISO 3166-2 codes, ascending. Empty for a leaf.

example: ["GB-BAS","GB-BBD","GB-BCP"]

Documented examples

Build-generated requests and complete responses
4
California
GET /api/country-subdivisions/lookup?code=US-CA
{
  "data": {
    "code": "US-CA",
    "name": "California",
    "cldrName": "California",
    "countryCode": "US",
    "country": "United States",
    "parentCode": null,
    "level": 1,
    "aliasOf": null,
    "childCount": 0,
    "children": []
  },
  "meta": {
    "endpoint": "country-subdivisions",
    "route": "/lookup",
    "params": {
      "code": "US-CA"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
England, with its 152 child areas
GET /api/country-subdivisions/lookup?code=GB-ENG
{
  "data": {
    "code": "GB-ENG",
    "name": "England",
    "cldrName": "England",
    "countryCode": "GB",
    "country": "United Kingdom",
    "parentCode": null,
    "level": 1,
    "aliasOf": null,
    "childCount": 152,
    "children": [
      "GB-BAS",
      "GB-BBD",
      "GB-BCP",
      "GB-BDF",
      "GB-BDG",
      "GB-BEN",
      "GB-BEX",
      "GB-BIR",
      "GB-BKM",
      "GB-BNE",
      "GB-BNH",
      "GB-BNS",
      "GB-BOL",
      "GB-BPL",
      "GB-BRC",
      "GB-BRD",
      "GB-BRY",
      "GB-BST",
      "GB-BUR",
      "GB-CAM",
      "GB-CBF",
      "GB-CHE",
      "GB-CHW",
      "GB-CLD",
      "GB-CMA",
      "GB-CMD",
      "GB-CON",
      "GB-COV",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Puerto Rico — a subdivision that is also a country code
GET /api/country-subdivisions/lookup?code=us-pr
{
  "data": {
    "code": "US-PR",
    "name": "Puerto Rico",
    "cldrName": "Puerto Rico",
    "countryCode": "US",
    "country": "United States",
    "parentCode": null,
    "level": 1,
    "aliasOf": "PR",
    "childCount": 0,
    "children": []
  },
  "meta": {
    "endpoint": "country-subdivisions",
    "route": "/lookup",
    "params": {
      "code": "US-PR"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Just the name and parent
GET /api/country-subdivisions/lookup?code=GB-BKM&fields=code,name,parentCode
{
  "data": {
    "code": "GB-BKM",
    "name": "Buckinghamshire",
    "parentCode": "GB-ENG"
  },
  "meta": {
    "endpoint": "country-subdivisions",
    "route": "/lookup",
    "params": {
      "code": "GB-BKM"
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/country-subdivisions/countries

One row for every one of the 249 officially assigned ISO 3166-1 countries, ordered by alpha-2 code, so a zero on the main route is provable rather than suspicious. 249 rows means three pages at 100 per response — use 'offset'.

Live requestRuns against the public API
No key required
GET/api/country-subdivisions/countries

Request parameters

Filter by what CLDR says about the country: subdivisions (CLDR lists at least one), none (CLDR states it has none), unstated (CLDR says neither). any keeps every row.

true keeps only countries with at least one shipped subdivision, false keeps only those with exactly zero.

Case- and diacritic-insensitive contains match on the English country name.

Skip this many matches before returning records. A response returns at most 100 countries, so this is how you page through a larger match set.

Advanced response options6 options

Limit the number of returned records (1–100). Defaults to 100.

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/country-subdivisions/countries"
const res = await fetch("https://randomapi.dev/api/country-subdivisions/countries");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/country-subdivisions/countries").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/country-subdivisions/countries"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
sourceStatement enum

Filter by what CLDR says about the country: subdivisions (CLDR lists at least one), none (CLDR states it has none), unstated (CLDR says neither). any keeps every row.

default: any
allowed: any | subdivisions | none | unstated
example: sourceStatement=none
hasSubdivisions boolean

true keeps only countries with at least one shipped subdivision, false keeps only those with exactly zero.

example: hasSubdivisions=true
search string

Case- and diacritic-insensitive contains match on the English country name.

allowed: 2 – 60
example: search=united
offset int

Skip this many matches before returning records. A response returns at most 100 countries, so this is how you page through a larger match set.

default: 0
allowed: 0 – 248
example: offset=100
Universal parameters Shared response and formatting options 6
count int

Limit the number of returned records (1–100). Defaults to 100.

default: 100
allowed: 1 – 100
example: count=3
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=countryCode,country
exclude list

Return all fields except these (comma-separated).

example: exclude=aliasedCodeCount
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 8
countryCode string (ISO 3166-1 alpha-2)

ISO 3166-1 alpha-2 code of an officially assigned country or territory.

example: US

country string

English country name, joined from this project's ISO 3166-1 countries dataset.

example: United States

subdivisionCount integer

Codes this API serves for the country. Sums to 5,046 across all 249 rows.

example: 57

level1Count integer

Of those, how many CLDR contains directly under the country.

example: 57

level2Count integer

Of those, how many CLDR nests inside another subdivision.

example: 0

sourceStatement string

What CLDR says about this country: `subdivisions` (CLDR lists at least one — 200 countries), `none` (CLDR's own file carries an explicit "NO SUBDIVISIONS" statement — 48 countries), `unstated` (neither; only GS). Note that `HK` and `MO` are `none` here *and* are served as `CN-HK`/`CN-MO` on the main route — see the `countryCode` field.

example: subdivisions

deprecatedCodeCount integer

Codes CLDR marks deprecated for this country, so a legacy dataset holding dead codes is explicable rather than mysterious. Browse them at /api/country-subdivisions/deprecated.

example: 0

aliasedCodeCount integer

Of `subdivisionCount`, how many CLDR declares `overlong` aliases of an ISO 3166-1 region code (19 in total, across 5 countries).

example: 6

Documented examples

Build-generated requests and complete responses
5
The first page of the country index
GET /api/country-subdivisions/countries
{
  "data": [
    {
      "countryCode": "AD",
      "country": "Andorra",
      "subdivisionCount": 7,
      "level1Count": 7,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AE",
      "country": "United Arab Emirates",
      "subdivisionCount": 7,
      "level1Count": 7,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AF",
      "country": "Afghanistan",
      "subdivisionCount": 34,
      "level1Count": 34,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AG",
      "country": "Antigua and Barbuda",
      "subdivisionCount": 8,
      "level1Count": 8,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
    …
    ]
  }
}
Countries CLDR states have no subdivisions
GET /api/country-subdivisions/countries?sourceStatement=none
{
  "data": [
    {
      "countryCode": "AI",
      "country": "Anguilla",
      "subdivisionCount": 0,
      "level1Count": 0,
      "level2Count": 0,
      "sourceStatement": "none",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AQ",
      "country": "Antarctica",
      "subdivisionCount": 0,
      "level1Count": 0,
      "level2Count": 0,
      "sourceStatement": "none",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AS",
      "country": "American Samoa",
      "subdivisionCount": 0,
      "level1Count": 0,
      "level2Count": 0,
      "sourceStatement": "none",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "AW",
      "country": "Aruba",
      "subdivisionCount": 0,
      "level1Count": 0,
      "level2Count": 0,
      "sourceStatement": "none",
      "deprecatedCodeCount": 0,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
The one country CLDR says nothing about
GET /api/country-subdivisions/countries?sourceStatement=unstated
{
  "data": [
    {
      "countryCode": "GS",
      "country": "South Georgia and the South Sandwich Islands",
      "subdivisionCount": 0,
      "level1Count": 0,
      "level2Count": 0,
      "sourceStatement": "unstated",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    }
  ],
  "meta": {
    "endpoint": "country-subdivisions",
    "route": "/countries",
    "count": 1,
    "params": {
      "sourceStatement": "unstated",
      "offset": 0
    },
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Per-country withdrawn-code counts
GET /api/country-subdivisions/countries?count=100&fields=countryCode,country,deprecatedCodeCount
{
  "data": [
    {
      "countryCode": "AD",
      "country": "Andorra",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AE",
      "country": "United Arab Emirates",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AF",
      "country": "Afghanistan",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AG",
      "country": "Antigua and Barbuda",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AI",
      "country": "Anguilla",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AL",
      "country": "Albania",
      "deprecatedCodeCount": 36
    },
    {
      "countryCode": "AM",
      "country": "Armenia",
      "deprecatedCodeCount": 0
    },
    {
      "countryCode": "AO",
      "country": "Angola",
    …
    ]
  }
}
Every country whose name contains 'united'
GET /api/country-subdivisions/countries?search=united
{
  "data": [
    {
      "countryCode": "AE",
      "country": "United Arab Emirates",
      "subdivisionCount": 7,
      "level1Count": 7,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "GB",
      "country": "United Kingdom",
      "subdivisionCount": 221,
      "level1Count": 4,
      "level2Count": 217,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 31,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "UM",
      "country": "United States Minor Outlying Islands",
      "subdivisionCount": 9,
      "level1Count": 9,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
      "aliasedCodeCount": 0
    },
    {
      "countryCode": "US",
      "country": "United States",
      "subdivisionCount": 57,
      "level1Count": 57,
      "level2Count": 0,
      "sourceStatement": "subdivisions",
      "deprecatedCodeCount": 0,
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

GET /api/country-subdivisions/deprecated

The migration route: the 599 codes CLDR's own subdivisionAlias table marks reason="deprecated", ordered by code. 120 of them carry a replacement CLDR actually records; the rest use its "unknown" form and come back with an empty replacedBy rather than a guess.

Live requestRuns against the public API
No key required
GET/api/country-subdivisions/deprecated?hasReplacement=true

Request parameters

Only deprecated codes of this country. Takes an ISO 3166-1 alpha-2 or alpha-3 code, case-insensitively (us, US, usa and USA all resolve to US); the canonical alpha-2 is echoed in meta.params.

Case- and diacritic-insensitive contains match on the deprecated code's name. The 227 rows CLDR deprecates without ever publishing a name can never match it.

true keeps only the rows where CLDR records a replacement code, false keeps only the rows where it does not.

Skip this many matches before returning records. A response returns at most 100 codes, so this is how you page through a larger match set.

Advanced response options6 options

Limit the number of returned records (1–100). Defaults to 100.

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

Return all fields except these (comma-separated).

Response format: json envelope, ndjson (one record per line) or csv.

Pretty-print the JSON response.

Drop the envelope: return the raw array/object without data/meta wrapper.

Response

Example parameters are ready. Send the request to inspect the live response.

Route reference

Code samples Ready-to-copy requests in 4 languages
Choose a code sample language
curl "https://randomapi.dev/api/country-subdivisions/deprecated?hasReplacement=true"
const res = await fetch("https://randomapi.dev/api/country-subdivisions/deprecated?hasReplacement=true");
const { data, meta } = await res.json();
import requests

data = requests.get("https://randomapi.dev/api/country-subdivisions/deprecated?hasReplacement=true").json()["data"]
$json = json_decode(file_get_contents(
  "https://randomapi.dev/api/country-subdivisions/deprecated?hasReplacement=true"
), true);
$data = $json["data"];
Parameters Route-specific request inputs 4
country string

Only deprecated codes of this country. Takes an ISO 3166-1 alpha-2 or alpha-3 code, case-insensitively (us, US, usa and USA all resolve to US); the canonical alpha-2 is echoed in meta.params.

example: country=LV
search string

Case- and diacritic-insensitive contains match on the deprecated code's name. The 227 rows CLDR deprecates without ever publishing a name can never match it.

allowed: 2 – 60
example: search=carbonia
hasReplacement boolean

true keeps only the rows where CLDR records a replacement code, false keeps only the rows where it does not.

example: hasReplacement=true
offset int

Skip this many matches before returning records. A response returns at most 100 codes, so this is how you page through a larger match set.

default: 0
allowed: 0 – 598
example: offset=100
Universal parameters Shared response and formatting options 6
count int

Limit the number of returned records (1–100). Defaults to 100.

default: 100
allowed: 1 – 100
example: count=3
fields list

Return only these fields (comma-separated). Mutually exclusive with 'exclude'.

example: fields=code,name
exclude list

Return all fields except these (comma-separated).

example: exclude=replacementKnown
format enum

Response format: json envelope, ndjson (one record per line) or csv.

default: json
allowed: json | ndjson | csv
example: format=csv
pretty boolean

Pretty-print the JSON response.

default: false
example: pretty=true
unwrap boolean

Drop the envelope: return the raw array/object without data/meta wrapper.

default: false
example: unwrap=true
Response schema Fields returned in each record 7
code string (ISO 3166-2)

The deprecated ISO 3166-2 code, in canonical uppercase-with-hyphen form.

example: IT-CI

name string | null nullable

CLDR's English name, mark-stripped. null for the 227 codes CLDR deprecates without ever publishing a name.

example: Carbonia-Iglesias

cldrName string | null nullable

The verbatim CLDR string; null on the same rows as `name`.

example: Carbonia-Iglesias

countryCode string (ISO 3166-1 alpha-2)

ISO 3166-1 alpha-2 of the country the deprecated code belonged to.

example: IT

country string

English country name, joined from this project's ISO 3166-1 countries dataset.

example: Italy

replacedBy string[]

CLDR's recorded replacement codes, canonicalized. Empty when CLDR records none (it writes an `xx?` "unknown" form). Otherwise 1–9 entries — `NZ-N` split into 9. Three classes of CLDR's replacement values do not resolve to a current subdivision on this endpoint, and all three are reproduced rather than repaired: 3 point at an ISO 3166-1 country code instead of a subdivision (`FR-GUA`→`GP`, `FR-LRE`→`RE`, `FR-MAY`→`YT`); 7 point at another code that is itself deprecated (`CZ-10A`→`CZ-110` … `CZ-10F`→`CZ-115`, and `FR-H`→`FR-COR`); and 2 name a code CLDR 48 itself lists neither as current nor as deprecated, so `/lookup` answers it with a 404 (`CN-15`→`CN-MN`, where CLDR's Inner Mongolia is `CN-NM`, and `CZ-611`→`CZ-663`). This API reproduces CLDR exactly and does **not** follow chains or correct dead targets; the parity script pins all three counts, so a release that changes them fails instead of quietly falsifying this description.

example: []

replacementKnown boolean

Whether `replacedBy` is non-empty — i.e. whether CLDR records a replacement at all.

example: false

Documented examples

Build-generated requests and complete responses
4
Every deprecated code with a known replacement
GET /api/country-subdivisions/deprecated?hasReplacement=true
{
  "data": [
    {
      "code": "CN-11",
      "name": null,
      "cldrName": null,
      "countryCode": "CN",
      "country": "China",
      "replacedBy": [
        "CN-BJ"
      ],
      "replacementKnown": true
    },
    {
      "code": "CN-12",
      "name": null,
      "cldrName": null,
      "countryCode": "CN",
      "country": "China",
      "replacedBy": [
        "CN-TJ"
      ],
      "replacementKnown": true
    },
    {
      "code": "CN-13",
      "name": null,
      "cldrName": null,
      "countryCode": "CN",
      "country": "China",
      "replacedBy": [
        "CN-HE"
      ],
      "replacementKnown": true
    },
    {
      "code": "CN-14",
      "name": null,
      "cldrName": null,
      "countryCode": "CN",
    …
    ]
  }
}
Latvia's abolished municipalities
GET /api/country-subdivisions/deprecated?country=LV
{
  "data": [
    {
      "code": "LV-001",
      "name": "Aglona",
      "cldrName": "Aglona",
      "countryCode": "LV",
      "country": "Latvia",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "LV-003",
      "name": "Aizpute",
      "cldrName": "Aizpute",
      "countryCode": "LV",
      "country": "Latvia",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "LV-004",
      "name": "Aknīste",
      "cldrName": "Aknīste",
      "countryCode": "LV",
      "country": "Latvia",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "LV-005",
      "name": "Aloja",
      "cldrName": "Aloja",
      "countryCode": "LV",
      "country": "Latvia",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "LV-006",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
Italy's abolished Sardinian provinces
GET /api/country-subdivisions/deprecated?country=IT
{
  "data": [
    {
      "code": "IT-AO",
      "name": "Aosta",
      "cldrName": "Aosta",
      "countryCode": "IT",
      "country": "Italy",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "IT-CI",
      "name": "Carbonia-Iglesias",
      "cldrName": "Carbonia-Iglesias",
      "countryCode": "IT",
      "country": "Italy",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "IT-OG",
      "name": "Ogliastra",
      "cldrName": "Ogliastra",
      "countryCode": "IT",
      "country": "Italy",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "IT-OT",
      "name": "Olbia-Tempio",
      "cldrName": "Olbia-Tempio",
      "countryCode": "IT",
      "country": "Italy",
      "replacedBy": [],
      "replacementKnown": false
    },
    {
      "code": "IT-SD",
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}
The multi-code New Zealand split
GET /api/country-subdivisions/deprecated?country=NZ&hasReplacement=true
{
  "data": [
    {
      "code": "NZ-N",
      "name": null,
      "cldrName": null,
      "countryCode": "NZ",
      "country": "New Zealand",
      "replacedBy": [
        "NZ-AUK",
        "NZ-BOP",
        "NZ-GIS",
        "NZ-HKB",
        "NZ-MWT",
        "NZ-NTL",
        "NZ-TKI",
        "NZ-WGN",
        "NZ-WKO"
      ],
      "replacementKnown": true
    },
    {
      "code": "NZ-S",
      "name": null,
      "cldrName": null,
      "countryCode": "NZ",
      "country": "New Zealand",
      "replacedBy": [
        "NZ-CAN",
        "NZ-MBH",
        "NZ-NSN",
        "NZ-OTA",
        "NZ-STL",
        "NZ-TAS",
        "NZ-WTC"
      ],
      "replacementKnown": true
    }
  ],
  "meta": {
    …
    "generatedAt": "2026-07-30T15:14:08.000Z"
  }
}

About this API

Coverage & behavior

The ISO 3166-2 subdivision code list — 5,046 current codes across 200 countries — reproduced from Unicode CLDR, pinned to release release-48-2 (cldrVersion 48) and verified by SHA-256 against 5 upstream files.

Codes and names are reproduced exactly as published by the source cited on this page; randomapi.dev takes no position on the sovereignty or territorial status of any country, territory or subdivision, and the presence, absence or nesting of any entry implies none.

Three surfaces, three sources — and they do not line up. CLDR's containment lists CN-HK, CN-MO and CN-TW under CN, so that is where this endpoint serves them. /api/countries reproduces ISO 3166-1, where HK, MO and TW are each their own top-level row. /api/country-borders reproduces a third source, so it carries an XK row that /api/countries excludes as not officially assigned. CLDR itself asserts in both directions at once: its alias table maps the legacy CN-71, CN-91 and CN-92 up to TW, HK and MO while still listing CN-TW, CN-HK and CN-MO inside CN. Each page reproduces the source it cites; none of them adjudicates, and the sentence above is the same on all three.

What "current" means here, and why it is a sourced claim. CLDR's files disagree with each other, and the disagreement is itself published. common/subdivisions/en.xml names 5,399 codes, but that list is historical: it keeps names for codes ISO has withdrawn. common/supplemental/subdivisions.xml (subdivisionContainment) lists the 5,046 codes of the current structure — that is what this endpoint serves. Every single one of the 372 codes that has a name but is not in the containment is independently declared reason="deprecated" in CLDR's subdivisionAlias table, with zero exceptions. So "deprecated" is CLDR's own statement, never our inference — and the committed parity script fails loudly if that ever stops being true.

The four routes.

  • /api/country-subdivisions lists subdivisions, filtered by country (alpha-2 or alpha-3), parent, level (1 or 2) and search. Ordered by code, which is also country-major.
  • /api/country-subdivisions/lookup?code= resolves one code. US-CA, us-ca, us_ca, US CA and usca all resolve to the same record, which also carries childCount and children.
  • /api/country-subdivisions/countries is the honesty route: one row for every one of the 249 officially assigned ISO 3166-1 countries, with its subdivision count and what CLDR says about it. An empty result on the main route is therefore provable rather than suspicious.
  • /api/country-subdivisions/deprecated lists the 599 codes CLDR marks deprecated, with the replacement CLDR records for 120 of them. This is the route to reach for when a stored code stopped resolving.

Paging. A single response returns at most 100 records, and five countries exceed that (GB 221, SI 212, UG 139, IT 126, FR 124). Use offset to continue; a warning tells you how many matched in total and which offset starts the next full page. If you lower count, advance offset by your own count rather than by 100, or you will skip rows.

Zero is a real answer. 48 of the 249 ISO countries carry CLDR's own explicit "NO SUBDIVISIONS" statement (Vatican City, Bermuda, Guam, Åland Islands, Hong Kong, Macao…), and exactly 1 — GS — has neither subdivisions nor that statement, so it is reported as unstated rather than assumed to be zero. ?country=VA returns an empty list plus a warning, never a 400 and never another country's rows.

Names. name is CLDR's English form with its disambiguation mark stripped; cldrName is the verbatim string, so nothing is hidden (FR-IDF is Île-de-France / Île-de-France²). 25 rows differ this way. search is a case- and diacritic-insensitive contains match on name, so zurich finds CH-ZH (Zürich), telangana finds IN-TS (Telangāna) and hokkaido finds JP-01 (Hokkaidō). It deliberately does not match codes — use /lookup for a code.

Aliases, and the 8 codes that are neither current nor deprecated. CLDR declares 27 codes overlong aliases of an ISO 3166-1 region code. 19 of them are still inside the containment and are served with aliasOf set and CLDR's own <territory> name as their display name (US-PR → Puerto Rico, FR-CP → Clipperton Island, SH-TA → Tristan da Cunha). The other 8 — CN-71, CN-91, CN-92, FR-GF, FR-GP, FR-MQ, FR-RE, FR-YT — CLDR leaves out of the containment and does not mark deprecated, so they appear on no list route. /lookup answers them with a 404 that states CLDR's alias target instead of guessing a "did you mean". That is also why ?country=FR has no Guadeloupe or Réunion row while ?country=US does have Puerto Rico: asymmetric upstream behaviour, reproduced rather than patched.

What this endpoint deliberately does not have.

  • No subdivision type. CLDR publishes no state / province / prefecture / region label, and deriving one from a source that only covers a handful of countries would be invention. If you need to know whether a country's addresses even use a state field, and what it is called locally, that is address-formats' job.
  • No coordinates, centroids or bounding boxes. GeoNames' admin1CodesASCII.txt was evaluated and rejected: GeoNames' own readme documents that column as a FIPS code "subject to change to iso code", so joining on code equality can silently attach the wrong point rather than merely failing to match. Subdivision-level geography belongs to cities.
  • No population, area, capital, timezone or flag per subdivision — none of it is in CLDR.
  • English only. CLDR publishes localized subdivision names; bundling them would multiply this dataset for no v2 benefit, so there is no ?locale=.
  • Not a scrape of ISO's database. ISO's Online Browsing Platform is not redistributable in bulk. This is what CLDR publishes, which tracks ISO 3166-2 with a newsletter lag — hence the pinned, visible release.
  • No chain-following, and no repairing dead targets. replacedBy is CLDR verbatim. FR-HFR-COR, and CLDR marks FR-COR deprecated too (Corsica's current code is FR-20R, "Corse"). CLDR also records 2 replacements naming a code it itself lists neither as current nor as deprecated — CN-15CN-MN (CLDR's Inner Mongolia is CN-NM) and CZ-611CZ-663 — so following those two lands on a 404. We report what CLDR records and let /deprecated?country=FR show the rest.

Everything here is a pure function of its resolved parameters: identical query, byte-identical data, for as long as the pinned release is the pinned release. There is no ?seed=, no clock read and no request-time network.

Use it for

  • Populate a state/province/region picker from the same ISO 3166-2 codes your database stores
  • Validate a user-submitted subdivision code and reject it with a specific reason instead of a generic error
  • Migrate a legacy dataset off withdrawn subdivision codes using CLDR's recorded replacements
  • Prove to an auditor that a country genuinely has no subdivisions rather than a gap in your data
  • Build two-level administrative hierarchies (UK nations → counties, France regions → departments) for test fixtures

Frequently asked questions

How do I get the list of states or provinces for one country?

Call /api/country-subdivisions?country=US — it returns all 57 US codes (50 states, DC and 6 outlying areas). Alpha-3 works too (country=usa). A response caps at 100 records, so a big country such as GB (221 rows) needs offset to page.

Why does this API return no subdivisions for my country?

Because CLDR says there are none. 48 of the 249 ISO 3166-1 countries carry CLDR's own explicit "NO SUBDIVISIONS" statement, and /api/country-subdivisions/countries shows sourceStatement: "none" for each of them — so an empty list is provably not a bug. Exactly 1 country (GS) has neither subdivisions nor that statement, and is reported as unstated rather than assumed to be zero.

My stored code IT-CI or MX-DIF returns 404 — is the code wrong?

It is withdrawn, not wrong. Both are deprecated in CLDR 48, so /lookup returns a 404 that names the code, gives its old English name and reports CLDR's replacement when CLDR records one. /api/country-subdivisions/deprecated lists all 599 deprecated codes, and 120 of them carry a recorded replacement.

Is this the complete ISO 3166-2 list?

It is every code in CLDR 48's subdivisionContainment — 5,046 codes — which is what tracks the current ISO 3166-2 structure. It is not scraped from ISO's paywalled Online Browsing Platform, it carries no subdivision type (state vs province vs prefecture, which CLDR does not publish), and CLDR follows ISO's newsletters with a lag. The pinned release and the SHA-256 digests are published on this page.

Why is Taiwan both a country and a CN subdivision here?

Because both cited sources say so, and this API reproduces each of them rather than adjudicating. /api/countries reproduces ISO 3166-1, where TW is its own entry (as are HK and MO); this endpoint reproduces ISO 3166-2:CN via CLDR's containment, which lists CN-TW, CN-HK and CN-MO under CN. CLDR itself does both at once — its alias table maps the legacy CN-71 up to TW while still listing CN-TW inside CN. See also /api/country-borders, which reproduces a third source and therefore carries an XK row /api/countries excludes.

Standards & references