Tutorial

How to Add Zip Code Lookup to Your Application

February 15, 2025 7 min read CountryDataAPI Team

Learn how to implement zip code lookup and validation using CountryDataAPI's postal codes endpoint. JavaScript and Python examples included.

Zip code lookup is a powerful feature that improves form usability and data quality. When a user enters their zip code, your app can automatically fill in their city, state, and country. In this tutorial, we'll build a zip code lookup feature using CountryDataAPI's postal codes endpoint, with examples in both JavaScript and Python.

The CountryDataAPI Zip Code Endpoint

CountryDataAPI provides a dedicated endpoint for querying zip codes by country:

GET https://api.countrydataapi.com/v1/zipcode/by-country?country=US&zip=10001

Parameters:

  • country — ISO 3166-1 alpha-2 country code (e.g., US, DE, GB)
  • zip — The zip/postal code to look up

Sample response:

{
  "zip": "10001",
  "city": "New York",
  "state": "New York",
  "state_code": "NY",
  "country": "United States",
  "country_code": "US"
}

JavaScript Implementation

Basic Fetch Example

const lookupZipCode = async (countryCode, zipCode) => {
  const apiKey = process.env.COUNTRY_API_KEY;
  const url = new URL('https://api.countrydataapi.com/v1/zipcode/by-country');
  url.searchParams.set('country', countryCode);
  url.searchParams.set('zip', zipCode);

  const response = await fetch(url.toString(), {
    headers: { 'x-api-key': apiKey }
  });

  if (!response.ok) {
    if (response.status === 404) {
      throw new Error('Zip code not found');
    }
    throw new Error('API request failed');
  }

  return response.json();
};

// Usage
try {
  const data = await lookupZipCode('US', '90210');
  console.log(data.city);  // "Beverly Hills"
  console.log(data.state); // "California"
} catch (error) {
  console.error(error.message);
}

Auto-fill Address Form

Here's a complete example that auto-fills an address form when the user enters a zip code:

// address-form.js
document.getElementById('zipCode').addEventListener('blur', async function() {
  const zip = this.value.trim();
  const country = document.getElementById('country').value;

  if (!zip || !country) return;

  const statusEl = document.getElementById('zip-status');
  statusEl.textContent = 'Looking up...';

  try {
    const data = await lookupZipCode(country, zip);
    document.getElementById('city').value = data.city;
    document.getElementById('state').value = data.state;
    statusEl.textContent = '✓ Address filled';
    statusEl.className = 'success';
  } catch (error) {
    statusEl.textContent = '✗ Zip code not found';
    statusEl.className = 'error';
  }
});

React Hook for Zip Code Lookup

import { useState, useCallback } from 'react';

export const useZipLookup = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [result, setResult] = useState(null);

  const lookup = useCallback(async (country, zip) => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetch(
        `https://api.countrydataapi.com/v1/zipcode/by-country?country=${country}&zip=${zip}`,
        { headers: { 'x-api-key': process.env.REACT_APP_API_KEY } }
      );
      if (!res.ok) throw new Error('Not found');
      const data = await res.json();
      setResult(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  return { lookup, loading, error, result };
};

Python Implementation

Basic requests Example

import os
import requests

API_KEY = os.environ.get('COUNTRY_API_KEY')
BASE_URL = 'https://api.countrydataapi.com/v1'

def lookup_zip_code(country_code: str, zip_code: str) -> dict:
    """
    Look up location data for a zip code.

    Args:
        country_code: ISO 3166-1 alpha-2 code (e.g., 'US', 'DE')
        zip_code: The postal/zip code to look up

    Returns:
        dict with city, state, country data

    Raises:
        ValueError: If zip code is not found
        requests.HTTPError: On API errors
    """
    response = requests.get(
        f'{BASE_URL}/zipcode/by-country',
        params={'country': country_code, 'zip': zip_code},
        headers={'x-api-key': API_KEY},
        timeout=10
    )

    if response.status_code == 404:
        raise ValueError(f'Zip code {zip_code} not found in {country_code}')

    response.raise_for_status()
    return response.json()

# Usage
try:
    data = lookup_zip_code('DE', '10115')
    print(f"City: {data['city']}")    # Berlin
    print(f"State: {data['state']}")  # Berlin
except ValueError as e:
    print(f"Error: {e}")

Django Form Field with Validation

from django import forms
from django.core.exceptions import ValidationError
import requests

class AddressForm(forms.Form):
    country = forms.ChoiceField(choices=[])  # Populate from API
    zip_code = forms.CharField(max_length=20)
    city = forms.CharField(max_length=100, required=False)
    state = forms.CharField(max_length=100, required=False)

    def clean(self):
        cleaned_data = super().clean()
        country = cleaned_data.get('country')
        zip_code = cleaned_data.get('zip_code')

        if country and zip_code:
            try:
                data = lookup_zip_code(country, zip_code)
                cleaned_data['city'] = data.get('city', '')
                cleaned_data['state'] = data.get('state', '')
            except ValueError:
                raise ValidationError(
                    f'Invalid zip code {zip_code} for {country}'
                )
        return cleaned_data

Handling Edge Cases

Different countries use different postal code formats:

  • US: 5 digits (e.g., 10001) or ZIP+4 (e.g., 10001-1234)
  • UK: Alphanumeric (e.g., SW1A 1AA)
  • Canada: Alphanumeric (e.g., K1A 0A9)
  • Germany: 5 digits (e.g., 10115)

CountryDataAPI handles all these formats. Simply pass the raw code and the API normalizes it internally.

Rate Limiting and Caching

To avoid excessive API calls, implement client-side caching:

const zipCache = new Map();

const lookupWithCache = async (country, zip) => {
  const key = `${country}:${zip}`;
  if (zipCache.has(key)) return zipCache.get(key);
  const data = await lookupZipCode(country, zip);
  zipCache.set(key, data);
  return data;
};

Next Steps

Now that you have zip code lookup working, explore:

Related Guides