Most postcode APIs meter you: a free tier, a credit card wall, then per-request pricing. The data underneath them is largely public domain. That gap felt wrong, so we built postcode.xwira.tech — no signup, no tier, no meter.

This is how it works.

Architecture: GeoNames → loaders → PostgreSQL → Bun API → Traefik edge

The data: GeoNames, treated with respect

GeoNames publishes postal-code datasets for most of the world. The raw dumps are charmingly old-school: tab-separated files, one country per file, plus one giant allCountries.zip.

Our loaders do three jobs before anything reaches the database:

  1. Normalize — postcodes get uppercased and de-spaced per country convention; place names keep their original diacritics with an ASCII shadow column for search.
  2. Dedupe — GeoNames sometimes carries the same code+place pair from multiple sources; we keep the richest row.
  3. Bulk load — everything goes in through chunked COPY, not row-by-row INSERTs. Reloading a country is minutes, not hours.

The whole dataset — 8.6M places across 60+ countries — lives comfortably in a few GB of PostgreSQL.

The database: boring on purpose

PostgreSQL, run by the CloudNativePG operator on our Kubernetes cluster. No exotic search engine. Two decisions make it fast enough that we never miss one:

  • A composite index on (country, postcode) makes exact lookups effectively free.
  • Trigram indexes (pg_trgm) on place names give fuzzy search — typo-tolerant "Kuala Lumpr" still finds Kuala Lumpur — without running a separate search cluster.

A rule we keep re-learning: add infrastructure only when a query plan tells you to, not when a blog post does.

The API: a small Bun service

The API is a stateless Bun service — start time in milliseconds, tiny image, and one process handles far more traffic than a free community API ever sees. Endpoints follow the questions people actually ask:

  • GET /search?code=50450 — what place is this?
  • GET /search?q=petaling — what codes does this place have?
  • GET /nearby?lat=…&lng=… — what's around me?

Responses are flat JSON. No envelopes, no HATEOAS ceremony — the response should look like what you'd design in your head.

The edge

Traefik terminates TLS with certificates auto-issued by cert-manager and Let's Encrypt. Rate limiting lives here too — generous for humans, firm enough that one runaway script can't ruin the free lunch for everyone.

Every box in the diagram is a container on a self-hosted Kubernetes cluster, declared in version-controlled manifests. Databases are backed up nightly. The web UI at postcode.xwira.tech is a Next.js app that consumes the exact same public API you would — it has no privileged path.

Why free works

The honest math: public-domain data + one small always-on service + boring PostgreSQL ≈ pocket-money hosting costs. The expensive part of commercial postcode APIs was never the compute — it's the business wrapped around it. Remove the business, and free is just… the price.

Want the same lookup from your own code? It's one curl away — no key needed. Start at postcode.xwira.tech.