Library / SDK
openvenues/libpostal avatar
openvenues/libpostal

libpostal: parsing international street addresses in C

A C library for parsing/normalizing street addresses around the world. Powered by statistical NLP and open geo data.

4,888 stars471 forksCMIT

At a glance

What is it?
libpostal is an MIT-licensed C library that turns free-form address strings into structured components and normalized forms. It solves the parsing half of the address problem and deliberately leaves geocoding to you.
Who is it for?
Adopt libpostal if you ingest addresses in more than one country and need a parser that does not require you to hand-write per-country rules, and if you can absorb a large one-time data download plus disk and memory for the models.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 125 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

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

DEEP OPEN-SOURCE ANALYSIS

The problem libpostal exists to solve

Address strings are written for humans, not for indexes. The README puts it plainly: addresses are "packed with local conventions, abbreviations and context, making them difficult to index/query effectively with traditional full-text search engines." A German street suffix, a French type prefix, a Mexican colonia, and a US ZIP+4 do not decompose the same way, and a tokenizer that splits on whitespace and punctuation will produce different tokens for the same place depending on how the user typed it.

libpostal targets the applications the README lists: place search, transportation, on-demand and delivery services, check-ins, reviews. The common thread is that these systems need to compare two address strings and decide whether they refer to the same place, or need to index an address so a later query can find it. The README frames the output as "clean normalized forms suitable for machine comparison and full-text indexing."

This is a narrower scope than it first appears. The README states directly that libpostal "is not itself a full geocoder." It is positioned as a preprocessing step that makes a geocoding application "smarter, simpler, and more consistent internationally." If your requirement is latitude and longitude, libpostal is the stage before the stage you actually need.

Two operations: parse and expand

The library exposes two distinct operations. Parsing takes a free-form address string and splits it into labelled components, the parts a database would want as separate columns. Expansion takes an address string and produces normalized variants, so that abbreviations and spelled-out forms converge on a common representation for matching.

The mechanism behind both is statistical NLP trained on open geo data, which the README identifies as OpenStreetMap. The two linked blog posts are described as the research background for the project, and the release naming (Brooklyn 99, Walla Walla, Quito) suggests the model data is refreshed between major versions rather than continuously.

The README lists the language bindings that are "officially supported": Python, Ruby, Go, Java, PHP, and NodeJS, with the note that "it's easy to write bindings in other languages." That matters for adoption because the core is pure C. You are not expected to write C to use it, but the C library and its data are the thing being installed in every case.

What the material does not give is any accuracy figure, per-language coverage table, or statement about which of the roughly sixty flag emoji in the README represent first-class support versus incidental coverage. Treat the flags as a claim of international intent, not as a support matrix.

Getting it running and where the data comes from

The README is explicit that the build produces the library and then a separate step downloads the data. The sequence is the standard autotools one: run ./bootstrap.sh, then ./configure, then make, then sudo make install. The data step follows with a script in the repository, and the README points at ./data/download_languages.sh followed by ./data/download_osm_data.sh.

That split is the single most important operational fact about libpostal. The compiled library is small; the data is not. The download scripts pull language data and OpenStreetMap-derived data into a local directory, and the installed library reads from there at runtime. Any deployment plan that treats libpostal as a normal shared library and forgets the data directory will fail at first call, not at build time.

The README also links a GitHub Actions badge for the test workflow and an AppVeyor badge, which tells you CI covers Linux and Windows builds. It does not describe a Docker image, a package in any distribution's repository, or a prebuilt binary release. You are building from source and fetching data, and you should plan the data fetch as a provisioning step in whatever image or machine template you use.

For bindings, the README points to separate repositories (pypostal, ruby_postal, gopostal, jpostal, php-postal, node-postal). Their release cadence is not covered in the material, so version compatibility between a binding and the C library is something you have to check yourself.

Where libpostal is the wrong tool

The clearest boundary is the one the README draws itself: libpostal is not a geocoder. It will not return coordinates, and it will not tell you whether an address exists. If you feed it an invented street name in a real city, the parser has no way to reject it, because parsing is a labelling task, not a validation task.

The second boundary follows from the data source. The models come from OpenStreetMap, and coverage of OSM varies enormously by country and by settlement type. Dense urban areas in well-mapped countries are represented in far more detail than rural areas elsewhere. The README does not quantify this, and it would be wrong to guess at numbers, but the dependency is structural: where OSM is thin, the training signal is thin.

The third boundary is retraining. Nothing in the supplied material describes a supported path for training the models on your own labelled address corpus, or for correcting systematic errors in a specific market. If your addresses are highly specialized (an internal campus addressing scheme, for instance), a rule-based parser you control may be a better fit than a statistical model you cannot easily adjust.

Finally, the release history is thin. The most recent release listed is v1.1 from May 2018, with v1.0.0 before it in April 2017. The repository's last push is dated 2026, so work continues, but the absence of tagged releases for years means you should expect to track the master branch rather than pin to a release.

How it compares to a rules-based address parser

The obvious alternative for the parsing half of this problem is a hand-written parser: regular expressions and lookup tables per country, maintained by you. The difference in approach is where the knowledge lives. A rules-based parser encodes what you already know about address formats. libpostal encodes what can be inferred from a large corpus of open geographic data, which means it can handle formats nobody wrote a rule for, at the cost of being opaque when it gets something wrong.

That trade shows up in debugging. With a regex parser, a misparse points at a specific pattern you can edit. With a statistical parser, a misparse points at a model, and the material does not describe a per-input explanation or confidence output you could use to triage. You can compare the component labels against your expectations and build your own error sample, but there is no documented knob for a single bad case.

A second alternative is to skip parsing entirely and normalize at the string level: lowercase, strip punctuation, expand a fixed abbreviation list, and index the result. This is cheap and predictable. It fails exactly where libpostal is aimed, which is cross-language and cross-convention matching, because the same place written in two languages or two abbreviation styles will not collapse to the same normalized string.

The honest framing is that libpostal replaces per-country rules with a shared model, and you pay for that with a large data dependency and less direct control. If your address volume is single-country and small, the rules-based route is likely cheaper to own.

Licence, maintenance and upgrade cost

libpostal is MIT licensed. In practical terms that is permissive: you can use it in commercial and closed-source products, and you can modify it, provided the licence text and copyright notice travel with the distribution. The README's sponsorship section is separate from the licence and does not impose any obligation on users. This is a description of the licence identifier, not legal advice; confirm the obligations with your own counsel before shipping.

The maintenance picture has two parts. The C library and its bindings are separate repositories with separate release cadences, so an upgrade is not a single version bump. You are tracking the core, the binding, and the data together. The data download scripts mean that a fresh install, or a rebuild of a container image, re-fetches model data whose contents are not pinned by the library version in any way the material describes.

The upgrade cost is therefore dominated by the data, not the code. A new model may change how a string parses, which means any downstream deduplication or matching logic tuned against the previous output needs rechecking. The release naming convention (Quito, Brooklyn 99, Walla Walla) implies that model refreshes are bundled into named releases rather than shipped continuously, which at least gives you a stable target to test against when one does arrive.

Given that the latest listed release is v1.1 from 2018 while the repository shows recent activity, the practical choice is between pinning to that release and accepting it may lag the current models, or building from master and accepting that you are tracking an untagged state.

Who should adopt libpostal

Adopt it if you are building a system that must accept addresses from more than one country and you do not want to own a per-country rules engine. The officially supported bindings mean a Python or Go or Java service can call it without writing C, and the parse-then-normalize split maps cleanly onto a pipeline where one stage extracts components and a later stage indexes or matches them.

Do not adopt it as a substitute for geocoding, validation, or postal-code lookup. The README is unambiguous that it is not a geocoder, and the material describes no validation output at all. Do not adopt it if you need to retrain on proprietary address data, because no such path is documented here. And do not adopt it if a large, separately downloaded data directory is incompatible with your deployment model.

What to verify before you commit: run parse_address on a sample of your own real addresses in each language you care about and read the component labels yourself, rather than trusting the flag list in the README. Measure the size of the data directory after running the download scripts, since that number drives your image size and memory planning. Check whether the binding for your language has been updated against the current C library. And decide explicitly whether you are pinning to v1.1 or tracking master, because that decision determines what a future model change costs you.

Editorial conclusion

Adopt libpostal if you ingest addresses in more than one country and need a parser that does not require you to hand-write per-country rules, and if you can absorb a large one-time data download plus disk and memory for the models. Do not adopt it if you need a geocoder (the README states it is not one), if you need postal-code or administrative-boundary validation, or if you need a parser you can retrain on your own labelled addresses, because the material does not describe a supported retraining path. Before committing, verify three things in your own target language: the exact output of parse_address on a sample of your real strings, the disk footprint of the downloaded data directory, and whether the maintained binding for your language is still current.

Official sources

  1. Issues
  2. License: MIT
  3. openvenues/libpostal on GitHub
  4. README
  5. Releases
Community notes

Community notes