Open-source project
mledoze/countries avatar
mledoze/countries

mledoze/countries: World Country Data in JSON, YAML, CSV and XML

World countries in JSON, YAML, CSV and XML. Any help is welcome!

6,263 stars1,282 forksPHPODbL-1.0

At a glance

What is it?
A dataset of ISO 3166-1 countries with names, codes, currencies, borders and translations, published as flat files and as npm and Packagist packages. Useful if you need country reference data offline; not a live source of political change.
Who is it for?
Adopt mledoze/countries when you need a country list you can read from disk or install from npm or Packagist, and you accept that the data is a snapshot rather than a feed. Do not adopt it as an authority on sovereignty or on current borders: the README itself warns that not every entity is an independent country, and the independent property is the only flag for that.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What mledoze/countries actually solves

Most applications eventually need a country list, and most teams build one badly. Someone pastes 200 rows into a seed file, the alpha-2 codes drift, and two years later nobody knows why Andorra has the wrong calling code. mledoze/countries is an attempt to keep that list in one place and publish it in several formats at once: JSON, CSV, XML and YAML, with the same underlying records. It targets developers who need a country list embedded in an application rather than fetched from a paid API, and who want the identifiers to follow ISO 3166-1 rather than someone's spreadsheet.

The README is explicit about a trap that catches people early. It states that not all entities in the project are independent countries, and points readers at the independent property to find out whether an entry is considered a sovereign state. That warning matters more than it looks. If you build a dropdown labelled "country" from the raw file, you inherit whatever political and administrative decisions the dataset makes, and you inherit them silently. The project gives you the flag to filter on; it does not filter for you.

The record shape: codes, currencies, borders, translations

Each country is one object with a fairly wide set of fields. The README lists name.common and name.official in English, a native map keyed by three-letter ISO 639-3 language code, tld, cca2, ccn3, cca3, cioc, independent, status, unMember, unRegionalGroup, currencies keyed by ISO 4217 code, idd with a root and a list of suffixes, capital, altSpellings, region, subregion, languages, translations, latlng, demonyms, landlocked, borders, area, flag and callingCodes.

Two design choices stand out. First, borders are stored as cca3 codes, so the Austria example lists CZE, DEU, HUN, ITA, LIE, SVK, SVN and CHE. That means you can build an adjacency graph without a second lookup, but it also means a border entry is only as good as the code it references; if an entry is removed from the dataset, the edge dangles. Second, the idd structure splits the calling code into a root and suffixes: Austria is root +4 with suffix 3, while the README explains that the Dominican Republic carries suffixes 809, 829 and 849. Assembling a dialable number from that is your job, not the dataset's.

Reading a record from the JSON file

The README does not give an install command, so the shortest path is the file the repository ships at its root: countries.json. The package.json files list includes countries.json, data/*, dist/*, index.cjs, index.d.ts and index.mjs, and the npm package name is world-countries. The README's Austria entry is the reference record for the field names used below.

A record begins with the nested name object and the code fields, exactly as the README shows for Austria:

json
{
	"name": {
		"common": "Austria",
		"official": "Republic of Austria"
	},
	"cca2": "AT",
	"ccn3": "040",
	"cca3": "AUT",
	"independent": true
}

That object is the shape you will be filtering on. The README's warning about sovereignty means a country picker should test the independent property rather than trusting the presence of a record. The same record also carries region, subregion, currencies, languages, translations, demonyms, borders and the flag emoji, all of which the README documents field by field.

If you prefer a module import over reading the file, package.json declares index.mjs as the module entry and index.cjs as the main entry, with index.d.ts as the types file. The README itself does not show an import statement, so treat the file layout in package.json as the source of truth for how the package is consumed.

PHP users get the same data through Packagist under the name mledoze/countries, and the repository also carries countries.php at the top level. The README does not spell out the PHP loading path beyond the package name, so check the package contents before wiring it into a framework.

Where the dataset will let you down

The data is a snapshot, not a feed. Nothing in the repository description promises that a country's status, currency or borders update on any schedule, and the release history shows the gap: 5.0.0 in September 2023, then 5.1.0 in February 2025. If your product depends on a change being reflected within days, this is the wrong source, and no amount of caching strategy fixes that.

The sovereignty question is the second failure mode. The README's warning is the only guidance on the subject, and it puts the decision on you. A form that lists every record as a country will disagree with what many of your users expect, and that disagreement will surface as a support ticket rather than a test failure.

Third, the formats are not equally rich. The CSV example in the README flattens the record into dotted column names such as name.common, idd.root and translations.deu.official, and the header shown is truncated mid-word at the flag column. Nested values like currencies, languages and demonyms do not survive that flattening intact. If you need the nested structure, CSV is the wrong output and JSON or YAML is the right one.

How it compares with a live country API

The obvious alternative is a hosted country API, which returns the same kind of fields over HTTP and updates without you redeploying. The difference is not accuracy, it is where the data lives. With mledoze/countries the records are files in your repository or your node_modules directory: no network call, no rate limit, no API key, and the same result in a build that runs offline. With a hosted API you get freshness and you get an availability dependency, plus a key to rotate.

A second alternative is generating your own list from an upstream standards body. That gives you control over exactly which entities appear and when, at the cost of owning the parsing, the translation fields and the border graph yourself. mledoze/countries is the middle position: someone else maintains the records, you maintain the filtering. The repository layout supports that reading, since the data folder also holds GeoJSON and TopoJSON outlines and SVG flags, which a plain API response would not give you in the same package.

Licence and upgrade cost

The project is licensed ODbL-1.0, the Open Database License, and the same identifier appears in package.json under licenses and on the Packagist badge in the README. ODbL is a database licence rather than a permissive software licence, and it carries attribution and share-alike conditions that differ from MIT or Apache-2.0. What that means for a specific product is a question for your own legal review; the practical point is that you should not assume this is a drop-in equivalent of a permissively licensed npm package.

Upgrade cost is low in the mechanical sense and non-trivial in the behavioural sense. The package ships the data as files, so a version bump is a dependency change, not a migration. But the jump from 4.1.1 to 5.0.0 happened in the same release window as 5.1.0 later did, and major versions exist for a reason. Pin the version, diff countries.json between the old and new release, and check that the fields your code reads are still present before you merge.

Editorial conclusion

Adopt mledoze/countries when you need a country list you can read from disk or install from npm or Packagist, and you accept that the data is a snapshot rather than a feed. Do not adopt it as an authority on sovereignty or on current borders: the README itself warns that not every entity is an independent country, and the independent property is the only flag for that. Before shipping, verify the licence obligations of ODbL-1.0 for your use, and check whether the country records you depend on still carry the fields your code reads.

Frequently asked questions

What is mledoze/countries?

It is a repository containing a list of world countries as defined by ISO Standard 3166-1, published in JSON, CSV, XML and YAML, with additional GeoJSON outlines and SVG flags in the data folder.

How do I use mledoze/countries in a JavaScript project?

The npm package is named world-countries; package.json sets main to index.cjs, module to index.mjs and types to index.d.ts, and the files list includes countries.json so the data ships with the package.

Does mledoze/countries only contain independent countries?

No. The README warns that not all entities in the project are independent countries, and says to refer to the independent property to know whether an entry is considered a sovereign state.

Which country codes does mledoze/countries provide?

Each record carries ISO 3166-1 alpha-2 as cca2, numeric as ccn3, alpha-3 as cca3, and the International Olympic Committee code as cioc, along with the country code top-level domain in tld.

What licence does mledoze/countries use?

The project is licensed ODbL-1.0, the Open Database License, as shown in the README badge and in package.json.

Official sources

  1. License: ODbL-1.0
  2. mledoze/countries on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mledoze-countries.svg)](https://hysenlabs.com/projects/mledoze-countries)