API Documentation - Endpoints & Examples

Search Postal Codes by Country

Country Data API provides detailed information about +200 different countries

Overview

The /v1/zipcodes/country endpoint returns all postal codes for a specific country, organized by state/province. This is perfect for applications that need complete postal code coverage for a specific country.

Endpoint

GET https://api.countrydataapi.com/v1/zipcodes/country

Authentication

Include your API key as a query parameter:

?apikey=your-api-key

Your API key is like a password, keep it secure. Get your key from the account dashboard.

Query Parameters

Parameter Type Required Description
apikey string Yes Your API authentication key
country string Yes Country ID, ISO code (e.g., "US", "DE"), or name
lang string No Language code for response (default: en)
limitToken number No Maximum tokens to spend on this request (default: 1000)

Supported Languages

  • en - English (default)
  • es - Spanish
  • pt - Portuguese
  • fr - French
  • de - German
  • it - Italian

Token Usage

Each returned postal code will cost 1 token.

Note: Large countries like the United States or Germany have thousands of postal codes. Use limitToken to control costs.

Response Format

Success Response

[
  {
    "id": "8dd25479-067a-43b0-ac4a-8e7faf2bcaf",
    "country_name": "United States",
    "state_names": [
      {
        "id": "8dd25479-067a-43b0-ac4a-8e7faf2bcad",
        "name": "California",
        "postal_code": [
          "90001",
          "90002",
          "90003",
          "90004"
        ]
      },
      {
        "id": "9ae35480-178b-54c1-bd5b-9f8gbf3cdeh",
        "name": "Texas",
        "postal_code": [
          "73301",
          "73344",
          "75001"
        ]
      }
    ]
  }
]

Response Fields

Field Type Description
id string Unique country identifier
country_name string Country name in the requested language
state_names array Array of state objects containing postal codes
state_names[].id string Unique state identifier
state_names[].name string State/province name
state_names[].postal_code array Array of postal codes for this state

Error Response

{
  "success": false,
  "error": {
    "code": "MISSING_PARAMETER",
    "message": "Required parameter 'country' is missing"
  }
}

See the Error Codes documentation for all possible error codes.

Code Examples

cURL

# Search by country name
curl -X GET "https://api.countrydataapi.com/v1/zipcodes/country?apikey=your-api-key&country=Germany&lang=en"

# Search by ISO code
curl -X GET "https://api.countrydataapi.com/v1/zipcodes/country?apikey=your-api-key&country=US&lang=en&limitToken=500"

# Search by country ID
curl -X GET "https://api.countrydataapi.com/v1/zipcodes/country?apikey=your-api-key&country=66c7a6c9e4bda21f4ab10fd5&lang=en"

JavaScript (Fetch)

const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.countrydataapi.com/v1';

async function getZipcodesByCountry(country, limitToken = 1000) {
  try {
    const params = new URLSearchParams({
      apikey: API_KEY,
      country: country,
      lang: 'en',
      limitToken: limitToken.toString()
    });

    const response = await fetch(`${BASE_URL}/zipcodes/country?${params}`);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error fetching zipcodes by country:', error);
    throw error;
  }
}

// Usage examples
// By country name
const germanyZipcodes = await getZipcodesByCountry('Germany');
console.log(germanyZipcodes);

// By ISO code
const usZipcodes = await getZipcodesByCountry('US', 500);
console.log(usZipcodes);

// By country ID
const mexicoZipcodes = await getZipcodesByCountry('66c7a6c9e4bda21f4ab10fa8');
console.log(mexicoZipcodes);

JavaScript (Axios)

import axios from 'axios';

const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.countrydataapi.com/v1';

async function getZipcodesByCountry(country, limitToken = 1000) {
  try {
    const response = await axios.get(`${BASE_URL}/zipcodes/country`, {
      params: {
        apikey: API_KEY,
        country: country,
        lang: 'en',
        limitToken: limitToken
      }
    });

    return response.data;
  } catch (error) {
    if (error.response) {
      console.error('API Error:', error.response.data);
      throw new Error(error.response.data.error?.message || 'API Error');
    } else {
      console.error('Network Error:', error.message);
      throw error;
    }
  }
}

// Usage
const data = await getZipcodesByCountry('France', 500);
console.log(`Found postal codes for ${data[0]?.state_names?.length || 0} states`);

React

import { useState, useEffect } from 'react';

const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.countrydataapi.com/v1';

function useCountryZipcodes(country, limitToken = 1000) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!country) {
      setData(null);
      return;
    }

    async function fetchZipcodes() {
      try {
        setLoading(true);
        setError(null);

        const params = new URLSearchParams({
          apikey: API_KEY,
          country: country,
          lang: 'en',
          limitToken: limitToken.toString()
        });

        const response = await fetch(`${BASE_URL}/zipcodes/country?${params}`);

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const result = await response.json();
        setData(result[0] || null);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchZipcodes();
  }, [country, limitToken]);

  return { data, loading, error };
}

// Component Example
function CountryZipcodeExplorer() {
  const [selectedCountry, setSelectedCountry] = useState('');
  const { data, loading, error } = useCountryZipcodes(selectedCountry, 500);

  const countries = [
    { code: 'US', name: 'United States' },
    { code: 'DE', name: 'Germany' },
    { code: 'FR', name: 'France' },
    { code: 'ES', name: 'Spain' },
    { code: 'MX', name: 'Mexico' }
  ];

  const totalZipcodes = data?.state_names?.reduce(
    (sum, state) => sum + state.postal_code.length,
    0
  ) || 0;

  return (
    <div>
      <h2>Explore Postal Codes by Country</h2>

      <select
        value={selectedCountry}
        onChange={(e) => setSelectedCountry(e.target.value)}
      >
        <option value="">Select a country...</option>
        {countries.map(c => (
          <option key={c.code} value={c.code}>{c.name}</option>
        ))}
      </select>

      {loading && <p>Loading postal codes...</p>}
      {error && <p style={{ color: 'red' }}>Error: {error}</p>}

      {data && (
        <div>
          <h3>{data.country_name}</h3>
          <p>Total states: {data.state_names.length}</p>
          <p>Total postal codes loaded: {totalZipcodes}</p>

          <div style={{ maxHeight: '400px', overflow: 'auto' }}>
            {data.state_names.map(state => (
              <details key={state.id}>
                <summary>
                  {state.name} ({state.postal_code.length} codes)
                </summary>
                <p style={{ fontSize: '0.9em', color: '#666' }}>
                  {state.postal_code.slice(0, 20).join(', ')}
                  {state.postal_code.length > 20 && '...'}
                </p>
              </details>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

export default CountryZipcodeExplorer;

Vue 3

<script setup>
import { ref, watch, computed } from 'vue';

const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.countrydataapi.com/v1';

const selectedCountry = ref('');
const countryData = ref(null);
const loading = ref(false);
const error = ref(null);

const countries = [
  { code: 'US', name: 'United States' },
  { code: 'DE', name: 'Germany' },
  { code: 'FR', name: 'France' },
  { code: 'ES', name: 'Spain' },
  { code: 'MX', name: 'Mexico' }
];

const totalZipcodes = computed(() => {
  if (!countryData.value?.state_names) return 0;
  return countryData.value.state_names.reduce(
    (sum, state) => sum + state.postal_code.length,
    0
  );
});

watch(selectedCountry, async (newCountry) => {
  if (!newCountry) {
    countryData.value = null;
    return;
  }

  try {
    loading.value = true;
    error.value = null;

    const params = new URLSearchParams({
      apikey: API_KEY,
      country: newCountry,
      lang: 'en',
      limitToken: '500'
    });

    const response = await fetch(`${BASE_URL}/zipcodes/country?${params}`);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    countryData.value = data[0] || null;
  } catch (err) {
    error.value = err.message;
  } finally {
    loading.value = false;
  }
});
</script>

<template>
  <div>
    <h2>Explore Postal Codes by Country</h2>

    <select v-model="selectedCountry">
      <option value="">Select a country...</option>
      <option v-for="c in countries" :key="c.code" :value="c.code">
        {{ c.name }}
      </option>
    </select>

    <div v-if="loading">Loading postal codes...</div>
    <div v-else-if="error" style="color: red;">Error: {{ error }}</div>

    <div v-else-if="countryData">
      <h3>{{ countryData.country_name }}</h3>
      <p>Total states: {{ countryData.state_names.length }}</p>
      <p>Total postal codes loaded: {{ totalZipcodes }}</p>

      <div style="max-height: 400px; overflow: auto;">
        <details v-for="state in countryData.state_names" :key="state.id">
          <summary>
            {{ state.name }} ({{ state.postal_code.length }} codes)
          </summary>
          <p style="font-size: 0.9em; color: #666;">
            {{ state.postal_code.slice(0, 20).join(', ') }}
            <span v-if="state.postal_code.length > 20">...</span>
          </p>
        </details>
      </div>
    </div>
  </div>
</template>

Angular

// zipcode.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';

interface State {
  id: string;
  name: string;
  postal_code: string[];
}

interface CountryZipcode {
  id: string;
  country_name: string;
  state_names: State[];
}

@Injectable({
  providedIn: 'root'
})
export class ZipcodeService {
  private readonly API_KEY = 'your-api-key';
  private readonly BASE_URL = 'https://api.countrydataapi.com/v1';

  constructor(private http: HttpClient) {}

  getZipcodesByCountry(
    country: string,
    limitToken: number = 1000
  ): Observable<CountryZipcode[]> {
    const params = new HttpParams()
      .set('apikey', this.API_KEY)
      .set('country', country)
      .set('lang', 'en')
      .set('limitToken', limitToken.toString());

    return this.http.get<CountryZipcode[]>(
      `${this.BASE_URL}/zipcodes/country`,
      { params }
    );
  }
}

// country-zipcode-explorer.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ZipcodeService } from './zipcode.service';

interface Country {
  code: string;
  name: string;
}

@Component({
  selector: 'app-country-zipcode-explorer',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div>
      <h2>Explore Postal Codes by Country</h2>

      <select [(ngModel)]="selectedCountry" (ngModelChange)="onCountryChange()">
        <option value="">Select a country...</option>
        <option *ngFor="let c of countries" [value]="c.code">
          {{ c.name }}
        </option>
      </select>

      <div *ngIf="loading">Loading postal codes...</div>
      <div *ngIf="error" style="color: red;">Error: {{ error }}</div>

      <div *ngIf="countryData && !loading">
        <h3>{{ countryData.country_name }}</h3>
        <p>Total states: {{ countryData.state_names.length }}</p>
        <p>Total postal codes loaded: {{ getTotalZipcodes() }}</p>

        <div style="max-height: 400px; overflow: auto;">
          <details *ngFor="let state of countryData.state_names">
            <summary>
              {{ state.name }} ({{ state.postal_code.length }} codes)
            </summary>
            <p style="font-size: 0.9em; color: #666;">
              {{ state.postal_code.slice(0, 20).join(', ') }}
              <span *ngIf="state.postal_code.length > 20">...</span>
            </p>
          </details>
        </div>
      </div>
    </div>
  `
})
export class CountryZipcodeExplorerComponent {
  countries: Country[] = [
    { code: 'US', name: 'United States' },
    { code: 'DE', name: 'Germany' },
    { code: 'FR', name: 'France' },
    { code: 'ES', name: 'Spain' },
    { code: 'MX', name: 'Mexico' }
  ];

  selectedCountry = '';
  countryData: any = null;
  loading = false;
  error: string | null = null;

  constructor(private zipcodeService: ZipcodeService) {}

  onCountryChange() {
    if (!this.selectedCountry) {
      this.countryData = null;
      return;
    }

    this.loading = true;
    this.error = null;

    this.zipcodeService.getZipcodesByCountry(this.selectedCountry, 500).subscribe({
      next: (data) => {
        this.countryData = data[0] || null;
        this.loading = false;
      },
      error: (err) => {
        this.error = err.message;
        this.loading = false;
      }
    });
  }

  getTotalZipcodes(): number {
    if (!this.countryData?.state_names) return 0;
    return this.countryData.state_names.reduce(
      (sum: number, state: any) => sum + state.postal_code.length,
      0
    );
  }
}

PHP

<?php
$apiKey = 'your-api-key';
$country = 'Germany'; // Can be country name, ISO code, or ID
$limitToken = 500;

$url = "https://api.countrydataapi.com/v1/zipcodes/country?" . http_build_query([
    'apikey' => $apiKey,
    'country' => $country,
    'lang' => 'en',
    'limitToken' => $limitToken
]);

// Using cURL
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Accept: application/json'
    ]
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);

if ($curlError) {
    die("cURL Error: {$curlError}");
}

if ($httpCode === 200) {
    $data = json_decode($response, true);

    if (!empty($data)) {
        $countryData = $data[0];
        echo "Country: " . $countryData['country_name'] . "\n";
        echo "Total States: " . count($countryData['state_names']) . "\n";

        $totalZipcodes = 0;
        foreach ($countryData['state_names'] as $state) {
            $totalZipcodes += count($state['postal_code']);
            echo "\n  State: " . $state['name'] . "\n";
            echo "  Postal Codes: " . count($state['postal_code']) . "\n";
            echo "  Sample: " . implode(', ', array_slice($state['postal_code'], 0, 5)) . "...\n";
        }
        echo "\nTotal Postal Codes: {$totalZipcodes}\n";
    } else {
        echo "No postal codes found for country: {$country}\n";
    }
} else {
    echo "Error: HTTP {$httpCode}\n";
    $error = json_decode($response, true);
    echo "Message: " . ($error['error']['message'] ?? 'Unknown error') . "\n";
}

// Reusable function
function getZipcodesByCountry($apiKey, $country, $lang = 'en', $limitToken = 1000) {
    $url = "https://api.countrydataapi.com/v1/zipcodes/country?" . http_build_query([
        'apikey' => $apiKey,
        'country' => $country,
        'lang' => $lang,
        'limitToken' => $limitToken
    ]);

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode !== 200) {
        throw new Exception("API Error: HTTP {$httpCode}");
    }

    return json_decode($response, true);
}

// Usage
try {
    // By ISO code
    $usData = getZipcodesByCountry($apiKey, 'US', 'en', 100);
    print_r($usData);

    // By country name in Spanish
    $spainData = getZipcodesByCountry($apiKey, 'Spain', 'es', 200);
    print_r($spainData);
} catch (Exception $e) {
    echo $e->getMessage();
}
?>

Python

import requests
from typing import List, Dict, Any, Optional

API_KEY = 'your-api-key'
BASE_URL = 'https://api.countrydataapi.com/v1'

def get_zipcodes_by_country(
    country: str,
    lang: str = 'en',
    limit_token: int = 1000
) -> List[Dict[str, Any]]:
    """
    Fetch postal codes for a specific country.

    Args:
        country: Country name, ISO code, or ID
        lang: Language code for response
        limit_token: Maximum tokens to spend on this request

    Returns:
        List containing country data with states and postal codes
    """
    try:
        response = requests.get(
            f'{BASE_URL}/zipcodes/country',
            params={
                'apikey': API_KEY,
                'country': country,
                'lang': lang,
                'limitToken': limit_token
            },
            timeout=30
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        print(f'HTTP Error: {e.response.status_code}')
        print(f'Response: {e.response.text}')
        raise
    except requests.exceptions.RequestException as e:
        print(f'Request Error: {e}')
        raise

def count_total_zipcodes(country_data: Dict[str, Any]) -> int:
    """Count total postal codes in country data"""
    return sum(
        len(state['postal_code'])
        for state in country_data.get('state_names', [])
    )

# Usage examples
if __name__ == '__main__':
    # Search by country name
    germany_data = get_zipcodes_by_country('Germany', limit_token=500)
    if germany_data:
        country = germany_data[0]
        print(f"Country: {country['country_name']}")
        print(f"Total states: {len(country['state_names'])}")
        print(f"Total postal codes: {count_total_zipcodes(country)}")

        for state in country['state_names'][:3]:
            print(f"\n  State: {state['name']}")
            print(f"  Codes: {', '.join(state['postal_code'][:5])}...")

    # Search by ISO code
    print("\n--- France (by ISO code) ---")
    france_data = get_zipcodes_by_country('FR', limit_token=300)
    if france_data:
        print(f"Total postal codes: {count_total_zipcodes(france_data[0])}")

    # Search in Spanish
    print("\n--- Mexico (in Spanish) ---")
    mexico_data = get_zipcodes_by_country('Mexico', lang='es', limit_token=200)
    if mexico_data:
        for state in mexico_data[0]['state_names'][:3]:
            print(f"  Estado: {state['name']}")

# Async version using aiohttp
import aiohttp
import asyncio

async def get_zipcodes_by_country_async(
    country: str,
    lang: str = 'en',
    limit_token: int = 1000
) -> List[Dict[str, Any]]:
    """Async version using aiohttp"""
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f'{BASE_URL}/zipcodes/country',
            params={
                'apikey': API_KEY,
                'country': country,
                'lang': lang,
                'limitToken': limit_token
            }
        ) as response:
            if response.status != 200:
                text = await response.text()
                raise Exception(f'API Error: {response.status} - {text}')
            return await response.json()

# Fetch multiple countries in parallel
async def get_multiple_countries(countries: List[str]) -> Dict[str, Any]:
    """Fetch zipcodes for multiple countries in parallel"""
    tasks = [
        get_zipcodes_by_country_async(country, limit_token=100)
        for country in countries
    ]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return dict(zip(countries, results))

# Run async
# data = asyncio.run(get_multiple_countries(['US', 'DE', 'FR']))

Error Handling

Always implement proper error handling:

async function getZipcodesWithErrorHandling(country) {
  try {
    const response = await fetch(
      `https://api.countrydataapi.com/v1/zipcodes/country?apikey=your-api-key&country=${encodeURIComponent(country)}`
    );

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));

      switch (response.status) {
        case 400:
          throw new Error(errorData.error?.message || 'Invalid request parameters');
        case 401:
          throw new Error('Invalid API key');
        case 402:
          throw new Error('Insufficient tokens. Please upgrade your plan.');
        case 404:
          throw new Error(`Country "${country}" not found`);
        case 429:
          throw new Error('Rate limit exceeded. Please try again later.');
        default:
          throw new Error(`HTTP error! status: ${response.status}`);
      }
    }

    const data = await response.json();

    if (!Array.isArray(data) || data.length === 0) {
      console.warn(`No postal codes found for country: ${country}`);
      return null;
    }

    return data[0];
  } catch (error) {
    console.error('Failed to fetch country zipcodes:', error.message);
    throw error;
  }
}

Common Error Codes

Error Code Description Solution
INVALID_API_KEY API key is invalid Check your API key in account dashboard
MISSING_PARAMETER country parameter missing Include country name, ISO code, or ID
COUNTRY_NOT_FOUND Country does not exist Verify country name or use valid ISO code
QUOTA_EXCEEDED Daily token limit reached Upgrade plan or wait for reset

Use Cases

1. Populate Address Form by Country

async function populateAddressForm(countryCode) {
  const data = await getZipcodesByCountry(countryCode);

  if (!data) return;

  const stateSelect = document.getElementById('state');
  stateSelect.innerHTML = '<option value="">Select state...</option>';

  data.state_names.forEach(state => {
    const option = new Option(state.name, state.id);
    option.dataset.zipcodes = JSON.stringify(state.postal_code);
    stateSelect.add(option);
  });
}

2. Validate Postal Code for Country

async function validatePostalCode(countryCode, postalCode) {
  const data = await getZipcodesByCountry(countryCode);

  if (!data) {
    return { valid: false, message: 'Country not found' };
  }

  for (const state of data.state_names) {
    if (state.postal_code.includes(postalCode)) {
      return {
        valid: true,
        state: state.name,
        message: `Valid postal code for ${state.name}`
      };
    }
  }

  return { valid: false, message: 'Invalid postal code for this country' };
}

// Usage
const result = await validatePostalCode('DE', '10115');
console.log(result); // { valid: true, state: 'Berlin', message: '...' }

Performance Tips

  1. Use limitToken: Control data size for countries with many postal codes
  2. Cache by country: Store postal codes per country (they rarely change)
  3. Lazy load states: Fetch all, but display progressively
  4. Use ISO codes: Faster lookup than full country names

Rate Limits

  • Free tier: 100 requests/day
  • Basic tier: 1,000 requests/day
  • Pro tier: 10,000 requests/day
  • Enterprise tier: Unlimited

Check our pricing page for more details.

Related Endpoints

Need Help?