API Documentation - Endpoints & Examples

Select Cities

Get cities by state/province, optimized for select dropdowns

The /v1/select/cities endpoint returns a lightweight list of cities filtered by state/province, with only id and name fields, optimized for dropdown menus.

Endpoint

GET https://api.countrydataapi.com/v1/select/cities

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 State ID (from /select/states endpoint)
lang string No Language code for city names (default: en)

Supported Languages

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

Request Example

# Get cities for California
curl "https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=66c7a6cae4bda21f4ab11d1a&lang=en"

JavaScript (Fetch)

const API_KEY = 'your-api-key';
const STATE_ID = '66c7a6cae4bda21f4ab11d1a'; // California

async function loadCities(stateId) {
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=${API_KEY}&state=${stateId}&lang=en`
  );
  const data = await response.json();
  return data;
}

const cities = await loadCities(STATE_ID);

JavaScript (Axios)

import axios from 'axios';

const response = await axios.get(
  'https://api.countrydataapi.com/v1/select/cities',
  {
    params: {
      apikey: 'your-api-key',
      state: '66c7a6cae4bda21f4ab11d1a',
      lang: 'en',
    },
  }
);

Python

import requests

response = requests.get(
    'https://api.countrydataapi.com/v1/select/cities',
    params={
        'apikey': 'your-api-key',
        'state': '66c7a6cae4bda21f4ab11d1a',
        'lang': 'en'
    }
)
data = response.json()

PHP

<?php
$apiKey = 'your-api-key';
$stateId = '66c7a6cae4bda21f4ab11d1a';
$url = "https://api.countrydataapi.com/v1/select/cities?apikey={$apiKey}&state={$stateId}&lang=en";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
?>

Angular

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

interface City {
  id: string;
  name: string;
}

interface ApiResponse {
  success: boolean;
  data: City[];
}

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

  constructor(private http: HttpClient) {}

  getCitiesByState(stateId: string, lang: string = 'en'): Observable<City[]> {
    const params = new HttpParams()
      .set('apikey', this.API_KEY)
      .set('state', stateId)
      .set('lang', lang);

    return this.http.get<ApiResponse>(
      `${this.BASE_URL}/select/cities`,
      { params }
    ).pipe(
      map(response => response.data)
    );
  }
}

// city-select.component.ts
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { CityService } from './city.service';

@Component({
  selector: 'app-city-select',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div *ngIf="loading">Loading cities...</div>
    <div *ngIf="error" class="error">{{ error }}</div>
    <select
      *ngIf="!loading && !error"
      [(ngModel)]="selectedCity"
      (ngModelChange)="onCityChange($event)"
      [disabled]="!stateId"
    >
      <option value="">Select a city...</option>
      <option *ngFor="let city of cities" [value]="city.id">
        {{ city.name }}
      </option>
    </select>
  `
})
export class CitySelectComponent implements OnChanges {
  @Input() stateId: string = '';
  @Output() cityChange = new EventEmitter<string>();

  cities: any[] = [];
  selectedCity = '';
  loading = false;
  error: string | null = null;

  constructor(private cityService: CityService) {}

  ngOnChanges(changes: SimpleChanges) {
    if (changes['stateId'] && this.stateId) {
      this.loadCities();
    } else {
      this.cities = [];
      this.selectedCity = '';
    }
  }

  loadCities() {
    this.loading = true;
    this.error = null;

    this.cityService.getCitiesByState(this.stateId, 'en').subscribe({
      next: (data) => {
        this.cities = data;
        this.loading = false;
      },
      error: (err) => {
        this.error = 'Failed to load cities';
        this.loading = false;
      }
    });
  }

  onCityChange(cityId: string) {
    this.cityChange.emit(cityId);
  }
}

// Complete cascading form example
// address-form.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CountrySelectComponent } from './country-select.component';
import { StateSelectComponent } from './state-select.component';
import { CitySelectComponent } from './city-select.component';

@Component({
  selector: 'app-address-form',
  standalone: true,
  imports: [CommonModule, CountrySelectComponent, StateSelectComponent, CitySelectComponent],
  template: `
    <form>
      <div class="form-group">
        <label>Country</label>
        <app-country-select (countryChange)="onCountryChange($event)"></app-country-select>
      </div>

      <div class="form-group">
        <label>State</label>
        <app-state-select
          [countryId]="selectedCountry"
          (stateChange)="onStateChange($event)"
        ></app-state-select>
      </div>

      <div class="form-group">
        <label>City</label>
        <app-city-select
          [stateId]="selectedState"
          (cityChange)="onCityChange($event)"
        ></app-city-select>
      </div>
    </form>
  `
})
export class AddressFormComponent {
  selectedCountry = '';
  selectedState = '';
  selectedCity = '';

  onCountryChange(countryId: string) {
    this.selectedCountry = countryId;
    this.selectedState = '';
    this.selectedCity = '';
  }

  onStateChange(stateId: string) {
    this.selectedState = stateId;
    this.selectedCity = '';
  }

  onCityChange(cityId: string) {
    this.selectedCity = cityId;
    console.log('Selected:', {
      country: this.selectedCountry,
      state: this.selectedState,
      city: this.selectedCity
    });
  }
}

Response Format

Success Response (California)

{
  "success": true,
  "data": [
    {
      "id": "66c7a6d1e4bda21f4ab5c825",
      "name": "Adelanto"
    },
    {
      "id": "66c7a6d1e4bda21f4ab5c826",
      "name": "Agoura Hills"
    },
    {
      "id": "66c7a6d1e4bda21f4ab5c827",
      "name": "Alameda"
    },
    {
      "id": "66c7a6d1e4bda21f4ab5c828",
      "name": "Albany"
    },
    {
      "id": "66c7a6d1e4bda21f4ab5c829",
      "name": "Alhambra"
    }
    // ... hundreds of cities
  ]
}

Response Fields

Field Type Description
success boolean Indicates if the request was successful
data array Array of city objects
data[].id string Unique city identifier (MongoDB ObjectId)
data[].name string City name in the requested language

Error Response

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

Empty Result

If a state has no cities in the database:

{
  "success": true,
  "data": []
}

Token Usage

This endpoint consumes 1 token per request.

Important: Some states have thousands of cities. Consider implementing pagination or search functionality for better UX.

Complete Cascading Dropdown

HTML + JavaScript

<select id="country" onchange="loadStates(this.value)">
  <option value="">Select country...</option>
</select>

<select id="state" onchange="loadCities(this.value)" disabled>
  <option value="">Select state...</option>
</select>

<select id="city" disabled>
  <option value="">Select city...</option>
</select>

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

// Load countries on page load
async function loadCountries() {
  const response = await fetch(`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=en`);
  const { data } = await response.json();

  const select = document.getElementById('country');
  data.forEach(country => {
    select.add(new Option(country.name, country.id));
  });
}

async function loadStates(countryId) {
  const stateSelect = document.getElementById('state');
  const citySelect = document.getElementById('city');

  // Reset
  stateSelect.innerHTML = '<option value="">Select state...</option>';
  citySelect.innerHTML = '<option value="">Select city...</option>';
  stateSelect.disabled = true;
  citySelect.disabled = true;

  if (!countryId) return;

  stateSelect.innerHTML = '<option value="">Loading...</option>';

  const response = await fetch(
    `${BASE_URL}/select/states?apikey=${API_KEY}&country=${countryId}&lang=en`
  );
  const { data } = await response.json();

  stateSelect.innerHTML = '<option value="">Select state...</option>';
  data.forEach(state => {
    stateSelect.add(new Option(state.name, state.id));
  });
  stateSelect.disabled = false;
}

async function loadCities(stateId) {
  const citySelect = document.getElementById('city');

  // Reset
  citySelect.innerHTML = '<option value="">Select city...</option>';
  citySelect.disabled = true;

  if (!stateId) return;

  citySelect.innerHTML = '<option value="">Loading...</option>';

  const response = await fetch(
    `${BASE_URL}/select/cities?apikey=${API_KEY}&state=${stateId}&lang=en`
  );
  const { data } = await response.json();

  citySelect.innerHTML = '<option value="">Select city...</option>';
  data.forEach(city => {
    citySelect.add(new Option(city.name, city.id));
  });
  citySelect.disabled = false;
}

// Load countries when page loads
loadCountries();
</script>

React Component

import { useState, useEffect } from 'react';

function CitySelect({ stateId, onChange }) {
  const [cities, setCities] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!stateId) {
      setCities([]);
      return;
    }

    setLoading(true);
    fetch(
      `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${stateId}&lang=en`
    )
      .then(res => res.json())
      .then(({ data }) => {
        setCities(data || []);
        setLoading(false);
      })
      .catch(err => {
        console.error(err);
        setLoading(false);
      });
  }, [stateId]);

  return (
    <select
      onChange={e => onChange(e.target.value)}
      disabled={!stateId || loading}
    >
      <option value="">
        {loading ? 'Loading cities...' : 'Select city...'}
      </option>
      {cities.map(city => (
        <option key={city.id} value={city.id}>
          {city.name}
        </option>
      ))}
    </select>
  );
}

export default CitySelect;

Vue Component

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

const props = defineProps(['stateId']);
const emit = defineEmits(['change']);

const cities = ref([]);
const loading = ref(false);

watch(() => props.stateId, async (stateId) => {
  if (!stateId) {
    cities.value = [];
    return;
  }

  loading.value = true;
  try {
    const response = await fetch(
      `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${stateId}&lang=en`
    );
    const { data } = await response.json();
    cities.value = data || [];
  } catch (err) {
    console.error(err);
  } finally {
    loading.value = false;
  }
});
</script>

<template>
  <select
    @change="emit('change', $event.target.value)"
    :disabled="!stateId || loading"
  >
    <option value="">
      {{ loading ? 'Loading cities...' : 'Select city...' }}
    </option>
    <option v-for="city in cities" :key="city.id" :value="city.id">
      {{ city.name }}
    </option>
  </select>
</template>

State-Specific Examples

California (1000+ cities)

const CA_ID = '66c7a6cae4bda21f4ab11d1a';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${CA_ID}`
);
// Returns: Los Angeles, San Francisco, San Diego, Sacramento, etc.

Texas (900+ cities)

const TX_ID = '66c7a6cbe4bda21f4ab11f55';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${TX_ID}`
);
// Returns: Houston, Dallas, Austin, San Antonio, etc.

New York (500+ cities)

const NY_ID = '66c7a6cae4bda21f4ab11e8f';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${NY_ID}`
);
// Returns: New York City, Buffalo, Rochester, Albany, etc.

Search/Filter Implementation

For states with many cities, implement client-side search:

function filterCities(cities, searchTerm) {
  if (!searchTerm) return cities;

  return cities.filter(city =>
    city.name.toLowerCase().includes(searchTerm.toLowerCase())
  );
}

// Usage
const allCities = await loadCities(stateId);
const filteredCities = filterCities(allCities, 'Los');
// Returns: Los Angeles, Los Gatos, Los Altos, etc.

Searchable Dropdown Example

<input
  type="text"
  id="city-search"
  placeholder="Search cities..."
  oninput="filterCityDropdown(this.value)"
>
<select id="city">
  <option value="">Select city...</option>
</select>

<script>
let allCities = [];

async function loadCities(stateId) {
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${stateId}`
  );
  const { data } = await response.json();
  allCities = data;
  renderCities(allCities);
}

function filterCityDropdown(searchTerm) {
  const filtered = allCities.filter(city =>
    city.name.toLowerCase().includes(searchTerm.toLowerCase())
  );
  renderCities(filtered);
}

function renderCities(cities) {
  const select = document.getElementById('city');
  select.innerHTML = '<option value="">Select city...</option>';
  cities.forEach(city => {
    select.add(new Option(city.name, city.id));
  });
}
</script>

Autocomplete Implementation

For better UX, implement autocomplete:

<input
  type="text"
  id="city-autocomplete"
  placeholder="Type to search cities..."
  list="cities-datalist"
>
<datalist id="cities-datalist">
  <!-- Populated dynamically -->
</datalist>

<script>
async function loadCitiesAutocomplete(stateId) {
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${stateId}`
  );
  const { data } = await response.json();

  const datalist = document.getElementById('cities-datalist');
  datalist.innerHTML = '';
  data.forEach(city => {
    const option = document.createElement('option');
    option.value = city.name;
    option.dataset.id = city.id;
    datalist.appendChild(option);
  });
}
</script>

Caching Strategy

Cache cities by state to minimize API calls:

const cityCache = new Map();

async function getCities(stateId) {
  // Check cache first
  if (cityCache.has(stateId)) {
    return cityCache.get(stateId);
  }

  // Fetch from API
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=your-api-key&state=${stateId}&lang=en`
  );
  const { data } = await response.json();

  // Save to cache (limit cache size to prevent memory issues)
  if (cityCache.size > 50) {
    const firstKey = cityCache.keys().next().value;
    cityCache.delete(firstKey);
  }
  cityCache.set(stateId, data);

  return data;
}

Performance Optimization

Lazy Loading

Only load cities when state dropdown is focused:

stateSelect.addEventListener('focus', () => {
  if (!citiesLoaded) {
    loadCities(stateSelect.value);
    citiesLoaded = true;
  }
});

Virtual Scrolling

For very large lists, use virtual scrolling libraries:

import { FixedSizeList } from 'react-window';

function VirtualCitySelect({ cities }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {cities[index].name}
    </div>
  );

  return (
    <FixedSizeList
      height={300}
      itemCount={cities.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

Error Handling

Always handle errors gracefully:

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

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

    const result = await response.json();

    if (!result.success) {
      throw new Error(result.error?.message || 'API error');
    }

    return result.data;
  } catch (err) {
    console.error('Failed to load cities:', err);
    alert('Failed to load cities. Please try again.');
    return [];
  }
}

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.

Common Errors

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 ID in request
INVALID_STATE_ID State ID format is invalid Use valid MongoDB ObjectId from /select/states
QUOTA_EXCEEDED Daily token limit reached Upgrade plan or wait for reset

See the Error Codes documentation for all possible errors.

Related Endpoints

Complete Integration Guides

Need Help?


Pro Tip: For states with 500+ cities, consider implementing a searchable autocomplete dropdown instead of a traditional select element. This significantly improves user experience and performance.