API Documentation - Endpoints & Examples

Search Postal Codes by State

Country Data API provides detailed information about +200 different countries

Overview

The /v1/zipcodes/state endpoint returns all postal codes for a specific state or province. This is ideal for applications that need to populate address forms or validate postal codes within a specific state.

Endpoint

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

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
state string Yes Name of the state or state ID being searched
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.

Response Format

Success Response

[
  {
    "id": "8dd25479-067a-43b0-ac4a-8e7faf2bcad",
    "name": "California",
    "postal_code": [
      "90001",
      "90002",
      "90003",
      "90004",
      "90005"
    ]
  }
]

Response Fields

Field Type Description
id string Unique state identifier
name string State/province name in the requested language
postal_code array Array of postal codes for this state

Error Response

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

See the Error Codes documentation for all possible error codes.

Code Examples

cURL

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

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

JavaScript (Fetch)

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

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

    const response = await fetch(`${BASE_URL}/zipcodes/state?${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 state:', error);
    throw error;
  }
}

// Usage
const californiaZipcodes = await getZipcodesByState('California');
console.log(californiaZipcodes);

// Or by state ID
const texasZipcodes = await getZipcodesByState('66c7a6cbe4bda21f4ab11f55');
console.log(texasZipcodes);

JavaScript (Axios)

import axios from 'axios';

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

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

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

// Usage
const zipcodes = await getZipcodesByState('California');
console.log(`Found ${zipcodes[0]?.postal_code?.length || 0} postal codes`);

React

import { useState, useEffect } from 'react';

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

function useStateZipcodes(stateName) {
  const [zipcodes, setZipcodes] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!stateName) {
      setZipcodes([]);
      return;
    }

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

        const params = new URLSearchParams({
          apikey: API_KEY,
          state: stateName,
          lang: 'en'
        });

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

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

        const data = await response.json();
        setZipcodes(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchZipcodes();
  }, [stateName]);

  return { zipcodes, loading, error };
}

// Component Example
function StateZipcodeSearch() {
  const [selectedState, setSelectedState] = useState('');
  const { zipcodes, loading, error } = useStateZipcodes(selectedState);

  const states = ['California', 'Texas', 'New York', 'Florida'];

  return (
    <div>
      <h2>Search Postal Codes by State</h2>

      <select
        value={selectedState}
        onChange={(e) => setSelectedState(e.target.value)}
      >
        <option value="">Select a state...</option>
        {states.map(state => (
          <option key={state} value={state}>{state}</option>
        ))}
      </select>

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

      {zipcodes.length > 0 && (
        <div>
          <h3>{zipcodes[0].name}</h3>
          <p>Total postal codes: {zipcodes[0].postal_code.length}</p>
          <div style={{ maxHeight: '200px', overflow: 'auto' }}>
            {zipcodes[0].postal_code.slice(0, 100).map(code => (
              <span key={code} style={{ margin: '2px', display: 'inline-block' }}>
                {code}
              </span>
            ))}
            {zipcodes[0].postal_code.length > 100 && (
              <span>...and {zipcodes[0].postal_code.length - 100} more</span>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

export default StateZipcodeSearch;

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 selectedState = ref('');
const zipcodes = ref([]);
const loading = ref(false);
const error = ref(null);

const states = ['California', 'Texas', 'New York', 'Florida'];

const postalCodes = computed(() => {
  return zipcodes.value[0]?.postal_code || [];
});

watch(selectedState, async (newState) => {
  if (!newState) {
    zipcodes.value = [];
    return;
  }

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

    const params = new URLSearchParams({
      apikey: API_KEY,
      state: newState,
      lang: 'en'
    });

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

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

    zipcodes.value = await response.json();
  } catch (err) {
    error.value = err.message;
  } finally {
    loading.value = false;
  }
});
</script>

<template>
  <div>
    <h2>Search Postal Codes by State</h2>

    <select v-model="selectedState">
      <option value="">Select a state...</option>
      <option v-for="state in states" :key="state" :value="state">
        {{ state }}
      </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="postalCodes.length > 0">
      <h3>{{ zipcodes[0].name }}</h3>
      <p>Total postal codes: {{ postalCodes.length }}</p>
      <div style="max-height: 200px; overflow: auto;">
        <span
          v-for="code in postalCodes.slice(0, 100)"
          :key="code"
          style="margin: 2px; display: inline-block;"
        >
          {{ code }}
        </span>
        <span v-if="postalCodes.length > 100">
          ...and {{ postalCodes.length - 100 }} more
        </span>
      </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 StateZipcode {
  id: string;
  name: string;
  postal_code: string[];
}

@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) {}

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

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

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

@Component({
  selector: 'app-state-zipcode-search',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div>
      <h2>Search Postal Codes by State</h2>

      <select [(ngModel)]="selectedState" (ngModelChange)="onStateChange()">
        <option value="">Select a state...</option>
        <option *ngFor="let state of states" [value]="state">
          {{ state }}
        </option>
      </select>

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

      <div *ngIf="zipcodes.length > 0 && !loading">
        <h3>{{ zipcodes[0].name }}</h3>
        <p>Total postal codes: {{ zipcodes[0].postal_code.length }}</p>
        <div style="max-height: 200px; overflow: auto;">
          <span
            *ngFor="let code of zipcodes[0].postal_code.slice(0, 100)"
            style="margin: 2px; display: inline-block;"
          >
            {{ code }}
          </span>
          <span *ngIf="zipcodes[0].postal_code.length > 100">
            ...and {{ zipcodes[0].postal_code.length - 100 }} more
          </span>
        </div>
      </div>
    </div>
  `
})
export class StateZipcodeSearchComponent {
  states = ['California', 'Texas', 'New York', 'Florida'];
  selectedState = '';
  zipcodes: any[] = [];
  loading = false;
  error: string | null = null;

  constructor(private zipcodeService: ZipcodeService) {}

  onStateChange() {
    if (!this.selectedState) {
      this.zipcodes = [];
      return;
    }

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

    this.zipcodeService.getZipcodesByState(this.selectedState).subscribe({
      next: (data) => {
        this.zipcodes = data;
        this.loading = false;
      },
      error: (err) => {
        this.error = err.message;
        this.loading = false;
      }
    });
  }
}

PHP

<?php
$apiKey = 'your-api-key';
$state = 'California'; // Can be state name or ID
$url = "https://api.countrydataapi.com/v1/zipcodes/state?" . http_build_query([
    'apikey' => $apiKey,
    'state' => $state,
    'lang' => 'en',
    'limitToken' => 1000
]);

// 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)) {
        $stateData = $data[0];
        echo "State: " . $stateData['name'] . "\n";
        echo "Total Postal Codes: " . count($stateData['postal_code']) . "\n";
        echo "Sample Codes: " . implode(', ', array_slice($stateData['postal_code'], 0, 10)) . "...\n";
    } else {
        echo "No postal codes found for state: {$state}\n";
    }
} else {
    echo "Error: HTTP {$httpCode}\n";
    $error = json_decode($response, true);
    echo "Message: " . ($error['error']['message'] ?? 'Unknown error') . "\n";
}

// Function wrapper for reusability
function getZipcodesByState($apiKey, $state, $lang = 'en', $limitToken = 1000) {
    $url = "https://api.countrydataapi.com/v1/zipcodes/state?" . http_build_query([
        'apikey' => $apiKey,
        'state' => $state,
        '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 {
    $zipcodes = getZipcodesByState($apiKey, 'Texas');
    print_r($zipcodes);
} 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_state(
    state: str,
    lang: str = 'en',
    limit_token: int = 1000
) -> List[Dict[str, Any]]:
    """
    Fetch postal codes for a specific state.

    Args:
        state: State name or ID
        lang: Language code for response
        limit_token: Maximum tokens to spend on this request

    Returns:
        List containing state data with postal codes
    """
    try:
        response = requests.get(
            f'{BASE_URL}/zipcodes/state',
            params={
                'apikey': API_KEY,
                'state': state,
                '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

# Usage examples
if __name__ == '__main__':
    # Search by state name
    california_data = get_zipcodes_by_state('California')
    if california_data:
        state = california_data[0]
        print(f"State: {state['name']}")
        print(f"Total postal codes: {len(state['postal_code'])}")
        print(f"Sample codes: {', '.join(state['postal_code'][:10])}...")

    # Search by state ID
    texas_data = get_zipcodes_by_state('66c7a6cbe4bda21f4ab11f55')
    if texas_data:
        print(f"\nTexas has {len(texas_data[0]['postal_code'])} postal codes")

# Async version using aiohttp
import aiohttp
import asyncio

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

# Run async
# data = asyncio.run(get_zipcodes_by_state_async('California'))

Error Handling

Always implement proper error handling:

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

    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(`State "${state}" 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 state: ${state}`);
      return [];
    }

    return data;
  } catch (error) {
    console.error('Failed to fetch state 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 state parameter missing Include state name or ID in request
STATE_NOT_FOUND State does not exist Verify state name or use valid state ID
QUOTA_EXCEEDED Daily token limit reached Upgrade plan or wait for reset

Use Cases

1. Address Form Validation

async function validateZipcode(state, zipcode) {
  const data = await getZipcodesByState(state);

  if (data.length === 0) {
    return { valid: false, message: 'State not found' };
  }

  const validZipcodes = data[0].postal_code;
  const isValid = validZipcodes.includes(zipcode);

  return {
    valid: isValid,
    message: isValid ? 'Valid postal code' : 'Invalid postal code for this state'
  };
}

// Usage
const result = await validateZipcode('California', '90210');
console.log(result); // { valid: true, message: 'Valid postal code' }

2. Autocomplete Dropdown

async function getZipcodeAutocomplete(state, query) {
  const data = await getZipcodesByState(state);

  if (data.length === 0) return [];

  return data[0].postal_code.filter(code =>
    code.startsWith(query)
  ).slice(0, 10);
}

// Usage
const suggestions = await getZipcodeAutocomplete('California', '902');
console.log(suggestions); // ['90201', '90202', '90210', ...]

Performance Tips

  1. Cache by state: Store postal codes per state to reduce API calls
  2. Lazy load: Only fetch when user selects a state
  3. Implement debounce: When using with search/autocomplete
  4. Use limitToken: Control data size for large states

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?