npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@countrystatecity/postalcodes

v2.0.0

Published

Official postal/ZIP code database with locality search, existence-based validation, and lazy loading

Downloads

1,015

Readme

@countrystatecity/postalcodes

npm CI

Official postal/ZIP code database with locality search, existence-based validation, and lazy loading.

Environment: 🖥️ Server-side only (Node.js, Next.js API routes, Express, etc.)

Upgrading to 2.0.0

Data now comes from upstream v4.0-export.50. The v3.2 → v4.0 transition removed 4,486 city ids (duplicates, US counties moved to a separate counties dataset, and non-places), reassigned 7,527 cities to different states, and added 27 states while retiring 6. Stored ids and per-state results can change; US counties are no longer returned as cities. Later export corrections are included in this snapshot.

Before upgrading stored city/state ids, use removed_ids_v3.2_to_v4.0.csv attached to the upstream v4.0 release. It identifies merged ids and their survivors, counties moved to new ids, and retired states and their successors. Match the record, outcome, and new_table columns before using new_id; a county id is not a city id. Refresh stored state associations against this snapshot.

Postal-code records retain their own postal ids, but state_code and per-state file membership changed, including retired/replaced state codes in Italy and new assignments elsewhere. Re-query the manifest and refresh stored state codes before using per-state lookups. city_id remains deliberately omitted from this package even though upstream now supplies links on many records; use locality_name for this package's locality search.

✨ Features

  • 📮 844,000+ postal codes across ~125 countries
  • 🔎 Locality search: find postal codes by place name
  • ✅ Existence-based validation: checks the real dataset, not a guessed format
  • 🔄 Lazy loading: per-country, per-state files loaded on demand
  • 📝 TypeScript: full type definitions included

Why existence-based validation, not regex?

Some postal code packages validate format only (e.g. "5 digits"), which accepts codes that look right but don't exist. This package instead checks whether a code is actually present in the dataset for that country, which catches typos in otherwise-plausible codes. The tradeoff: a genuinely new/valid code not yet in the upstream database won't validate until the data is refreshed.

Coverage note

Not every country has postal codes — only ~125 of 250 do, matching what upstream's postal-code dataset actually publishes. Use getSupportedCountryCodes() / isCountrySupported() to check before assuming a country is covered.

📦 Installation

npm install @countrystatecity/postalcodes
# or
yarn add @countrystatecity/postalcodes
# or
pnpm add @countrystatecity/postalcodes

⚠️ Server-Side Only: This package requires Node.js file system access and cannot be used in browser environments.

⚠️ Serverless deployments (Vercel/Lambda): this package's data directory is ~120MB uncompressed (postal code coverage is far larger than the countries package's data). If your platform's bundler traces and includes the whole dist/data/ tree rather than only the country subfolders actually read at runtime, this can approach serverless function size limits. Add serverExternalPackages: ['@countrystatecity/postalcodes'] to next.config.js on Next.js/Vercel to prevent webpack from bundling it at all — see @countrystatecity/countries' Vercel guide for the general pattern.

🚀 Quick Start

import {
  getSupportedCountryCodes,
  validatePostalCode,
  lookupPostalCode,
  searchPostalCodesByLocalityInCountry,
} from '@countrystatecity/postalcodes';

// Which countries have postal code data? (~125 of 250)
const supported = await getSupportedCountryCodes();

// Existence check — does this code actually exist for this country?
const isValid = await validatePostalCode('AD', 'AD100'); // true

// Look up a code — always returns an array, since codes can repeat
// across different localities within a country.
const matches = await lookupPostalCode('AD', 'AD100');
// [{ id: 1, code: 'AD100', country_code: 'AD', state_code: '02', locality_name: 'Canillo', ... }]

// Search by place name — this package deliberately omits upstream
// city_id links, so locality_name is the search field here.
const places = await searchPostalCodesByLocalityInCountry('AD', 'Canillo');

Local package or live API?

This package ships a free, offline-capable snapshot of the data — that's still the right choice when the app must work offline, a periodic snapshot is enough, or you just need simple lookups.

This package's postal code data isn't part of the live CountryStateCity API yet. But if your app also needs countries, states, cities, currencies, phone codes, timezones, or fuzzy search, @countrystatecity/sdk covers those live, with freshness, server-side search, and configurable rate limits beyond a bundled snapshot.

Start free with 3,000 requests per month. Compare plans or get an API key.

npm install @countrystatecity/sdk

See the full migration guide for what's covered today.

📖 API Reference

getManifest()

Lightweight list of every country with postal code data, plus per-country counts and state codes.

  • Returns: Promise<IPostalCodeManifestEntry[]>

getPostalCodesOfState(countryCode, stateCode)

All postal codes for a specific country + state.

  • Returns: Promise<IPostalCode[]>

getUnassignedPostalCodesOfCountry(countryCode)

Postal codes with no state subdivision in the upstream data (small territories, or a country's non-state-linked subset).

  • Returns: Promise<IPostalCode[]>

getAllPostalCodesOfCountry(countryCode)

All postal codes for an entire country (every state file + the unassigned bucket). Can be large — Portugal alone is ~197K records.

  • Returns: Promise<IPostalCode[]>

validatePostalCode(countryCode, code, stateCode?)

Existence check — does this code exist in the dataset? Not a format/regex check.

  • Returns: Promise<boolean>

lookupPostalCode(countryCode, code, stateCode?)

All records matching an exact code. Always an array — codes are not guaranteed unique.

  • Returns: Promise<IPostalCode[]>

searchPostalCodesByLocality(countryCode, stateCode, searchTerm) / searchPostalCodesByLocalityInCountry(countryCode, searchTerm)

Case-insensitive substring search on locality name.

  • Returns: Promise<IPostalCode[]>

getSupportedCountryCodes() / isCountrySupported(countryCode)

Which countries have postal code data.

  • Returns: Promise<string[]> / Promise<boolean>

🗂️ Data shape

interface IPostalCode {
  id: number;
  code: string;
  country_code: string;
  state_code: string | null; // null when this record has no state subdivision
  locality_name: string | null;
  type: string;
  latitude: string | null;
  longitude: string | null;
}

city_id is intentionally omitted from this package projection; upstream v4.0 now links many postal codes to cities. Use upstream exports if you need those links.

📊 Data Source

Data from countries-states-cities-database. Report data issues (wrong codes, missing localities) there — this package consumes that data, fixes happen upstream first.

📄 License

ODbL-1.0