Documentação

Selecionar Cidades

Obter cidades por estado/provincia, otimizado para selects dropdown

O endpoint /v1/select/cities retorna uma lista leve de cidades filtradas por estado/provincia, com apenas campos id e name, otimizado para menus dropdown.

Endpoint

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

Autenticacao

Inclua sua chave de API como parametro de consulta:

?apikey=sua-chave-api

Sua chave de API e como uma senha, mantenha-a segura. Obtenha sua chave no painel da conta.

Parametros de Consulta

Parametro Tipo Obrigatorio Descricao
apikey string Sim Sua chave de autenticacao da API
state string Sim ID do estado (do endpoint /select/states)
lang string Nao Codigo do idioma para nomes das cidades (padrao: pt)

Idiomas Suportados

  • en - Ingles
  • es - Espanhol
  • pt - Portugues (padrao)
  • fr - Frances
  • de - Alemao
  • it - Italiano

Exemplo de Requisicao

# Obter cidades de Sao Paulo
curl "https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=66c7a6c9e4bda21f4ab0f8d2&lang=pt"

JavaScript (Fetch)

const API_KEY = 'sua-chave-api';
const STATE_ID = '66c7a6c9e4bda21f4ab0f8d2'; // Sao Paulo

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

const cidades = await carregarCidades(STATE_ID);

JavaScript (Axios)

import axios from 'axios';

const response = await axios.get(
  'https://api.countrydataapi.com/v1/select/cities',
  {
    params: {
      apikey: 'sua-chave-api',
      state: '66c7a6c9e4bda21f4ab0f8d2',
      lang: 'pt',
    },
  }
);

Python

import requests

response = requests.get(
    'https://api.countrydataapi.com/v1/select/cities',
    params={
        'apikey': 'sua-chave-api',
        'state': '66c7a6c9e4bda21f4ab0f8d2',
        'lang': 'pt'
    }
)
data = response.json()

PHP

<?php
$apiKey = 'sua-chave-api';
$stateId = '66c7a6c9e4bda21f4ab0f8d2';
$url = "https://api.countrydataapi.com/v1/select/cities?apikey={$apiKey}&state={$stateId}&lang=pt";

$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 Cidade {
  id: string;
  name: string;
}

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

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

  constructor(private http: HttpClient) {}

  getCidadesPorEstado(estadoId: string, lang: string = 'pt'): Observable<Cidade[]> {
    const params = new HttpParams()
      .set('apikey', this.API_KEY)
      .set('state', estadoId)
      .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 { CidadeService } from './city.service';

@Component({
  selector: 'app-city-select',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div *ngIf="loading">Carregando cidades...</div>
    <div *ngIf="error" class="error">{{ error }}</div>
    <select
      *ngIf="!loading && !error"
      [(ngModel)]="selectedCity"
      (ngModelChange)="onCityChange($event)"
      [disabled]="!stateId"
    >
      <option value="">Selecione uma cidade...</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 cidadeService: CidadeService) {}

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

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

    this.cidadeService.getCidadesPorEstado(this.stateId, 'pt').subscribe({
      next: (data) => {
        this.cities = data;
        this.loading = false;
      },
      error: (err) => {
        this.error = 'Falha ao carregar cidades';
        this.loading = false;
      }
    });
  }

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

// Exemplo completo de formulario em cascata
// 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>Pais</label>
        <app-country-select (countryChange)="onCountryChange($event)"></app-country-select>
      </div>

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

      <div class="form-group">
        <label>Cidade</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('Selecionado:', {
      pais: this.selectedCountry,
      estado: this.selectedState,
      cidade: this.selectedCity
    });
  }
}

Formato de Resposta

Resposta de Sucesso (Sao Paulo)

{
  "success": true,
  "data": [
    {
      "id": "66c7a6d1e4bda21f4ab12345",
      "name": "Sao Paulo"
    },
    {
      "id": "66c7a6d1e4bda21f4ab12346",
      "name": "Campinas"
    },
    {
      "id": "66c7a6d1e4bda21f4ab12347",
      "name": "Santos"
    },
    {
      "id": "66c7a6d1e4bda21f4ab12348",
      "name": "Guarulhos"
    },
    {
      "id": "66c7a6d1e4bda21f4ab12349",
      "name": "Ribeirao Preto"
    }
    // ... centenas de cidades
  ]
}

Campos da Resposta

Campo Tipo Descricao
success boolean Indica se a requisicao foi bem-sucedida
data array Array de objetos de cidade
data[].id string Identificador unico da cidade (MongoDB ObjectId)
data[].name string Nome da cidade no idioma solicitado

Resposta de Erro

{
  "success": false,
  "error": {
    "code": "MISSING_PARAMETER",
    "message": "O parametro obrigatorio 'state' esta faltando"
  }
}

Resultado Vazio

Se um estado nao tiver cidades no banco de dados:

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

Uso de Tokens

Este endpoint consome 1 token por requisicao.

Importante: Alguns estados tem milhares de cidades. Considere implementar paginacao ou funcionalidade de busca para melhor UX.

Dropdown em Cascata Completo

HTML + JavaScript

<select id="country" onchange="carregarEstados(this.value)">
  <option value="">Selecione o pais...</option>
</select>

<select id="state" onchange="carregarCidades(this.value)" disabled>
  <option value="">Selecione o estado...</option>
</select>

<select id="city" disabled>
  <option value="">Selecione a cidade...</option>
</select>

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

// Carregar paises ao carregar a pagina
async function carregarPaises() {
  const response = await fetch(`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`);
  const { data } = await response.json();

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

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

  // Resetar
  stateSelect.innerHTML = '<option value="">Selecione o estado...</option>';
  citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
  stateSelect.disabled = true;
  citySelect.disabled = true;

  if (!paisId) return;

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

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

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

async function carregarCidades(estadoId) {
  const citySelect = document.getElementById('city');

  // Resetar
  citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
  citySelect.disabled = true;

  if (!estadoId) return;

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

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

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

// Carregar paises quando a pagina carregar
carregarPaises();
</script>

Componente React

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=sua-chave-api&state=${stateId}&lang=pt`
    )
      .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 ? 'Carregando cidades...' : 'Selecione a cidade...'}
      </option>
      {cities.map(city => (
        <option key={city.id} value={city.id}>
          {city.name}
        </option>
      ))}
    </select>
  );
}

export default CitySelect;

Componente Vue

<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=sua-chave-api&state=${stateId}&lang=pt`
    );
    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 ? 'Carregando cidades...' : 'Selecione a cidade...' }}
    </option>
    <option v-for="city in cities" :key="city.id" :value="city.id">
      {{ city.name }}
    </option>
  </select>
</template>

Exemplos por Estado

Sao Paulo (600+ cidades)

const SP_ID = '66c7a6c9e4bda21f4ab0f8d2';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${SP_ID}`
);
// Retorna: Sao Paulo, Campinas, Santos, Guarulhos, etc.

Minas Gerais (800+ cidades)

const MG_ID = '66c7a6c9e4bda21f4ab0f8d3';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${MG_ID}`
);
// Retorna: Belo Horizonte, Uberlandia, Contagem, Juiz de Fora, etc.

Rio de Janeiro (90+ cidades)

const RJ_ID = '66c7a6c9e4bda21f4ab0f8d4';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${RJ_ID}`
);
// Retorna: Rio de Janeiro, Niteroi, Nova Iguacu, Duque de Caxias, etc.

Implementacao de Busca/Filtro

Para estados com muitas cidades, implemente busca do lado do cliente:

function filtrarCidades(cidades, termoBusca) {
  if (!termoBusca) return cidades;

  return cidades.filter(cidade =>
    cidade.name.toLowerCase().includes(termoBusca.toLowerCase())
  );
}

// Uso
const todasCidades = await carregarCidades(estadoId);
const cidadesFiltradas = filtrarCidades(todasCidades, 'Sao');
// Retorna: Sao Paulo, Sao Bernardo do Campo, Sao Jose dos Campos, etc.

Exemplo de Dropdown Pesquisavel

<input
  type="text"
  id="city-search"
  placeholder="Buscar cidades..."
  oninput="filtrarDropdownCidades(this.value)"
>
<select id="city">
  <option value="">Selecione a cidade...</option>
</select>

<script>
let todasCidades = [];

async function carregarCidades(estadoId) {
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${estadoId}`
  );
  const { data } = await response.json();
  todasCidades = data;
  renderizarCidades(todasCidades);
}

function filtrarDropdownCidades(termoBusca) {
  const filtradas = todasCidades.filter(cidade =>
    cidade.name.toLowerCase().includes(termoBusca.toLowerCase())
  );
  renderizarCidades(filtradas);
}

function renderizarCidades(cidades) {
  const select = document.getElementById('city');
  select.innerHTML = '<option value="">Selecione a cidade...</option>';
  cidades.forEach(cidade => {
    select.add(new Option(cidade.name, cidade.id));
  });
}
</script>

Implementacao de Autocomplete

Para melhor UX, implemente autocomplete:

<input
  type="text"
  id="city-autocomplete"
  placeholder="Digite para buscar cidades..."
  list="cities-datalist"
>
<datalist id="cities-datalist">
  <!-- Populado dinamicamente -->
</datalist>

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

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

Estrategia de Cache

Armazene cidades em cache por estado para minimizar chamadas de API:

const cachesCidades = new Map();

async function getCidades(estadoId) {
  // Verificar cache primeiro
  if (cachesCidades.has(estadoId)) {
    return cachesCidades.get(estadoId);
  }

  // Buscar da API
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${estadoId}&lang=pt`
  );
  const { data } = await response.json();

  // Salvar no cache (limitar tamanho do cache para evitar problemas de memoria)
  if (cachesCidades.size > 50) {
    const primeiraChave = cachesCidades.keys().next().value;
    cachesCidades.delete(primeiraChave);
  }
  cachesCidades.set(estadoId, data);

  return data;
}

Otimizacao de Performance

Lazy Loading

Carregar cidades apenas quando o dropdown de estado estiver em foco:

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

Virtual Scrolling

Para listas muito grandes, use bibliotecas de virtual scrolling:

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>
  );
}

Tratamento de Erros

Sempre trate erros de forma adequada:

async function carregarCidades(estadoId) {
  try {
    const response = await fetch(
      `https://api.countrydataapi.com/v1/select/cities?apikey=sua-chave-api&state=${estadoId}`
    );

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

    const result = await response.json();

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

    return result.data;
  } catch (err) {
    console.error('Falha ao carregar cidades:', err);
    alert('Falha ao carregar cidades. Por favor, tente novamente.');
    return [];
  }
}

Limites de Taxa

  • Plano gratuito: 100 requisicoes/dia
  • Plano basico: 1.000 requisicoes/dia
  • Plano pro: 10.000 requisicoes/dia
  • Plano enterprise: Ilimitado

Consulte nossa pagina de precos para mais detalhes.

Erros Comuns

Codigo de Erro Descricao Solucao
INVALID_API_KEY Chave de API invalida Verifique sua chave no painel da conta
MISSING_PARAMETER Parametro state faltando Inclua o ID do estado na requisicao
INVALID_STATE_ID Formato do ID do estado invalido Use um ObjectId MongoDB valido de /select/states
QUOTA_EXCEEDED Limite diario de tokens atingido Faca upgrade do plano ou aguarde o reset

Veja a documentacao de Codigos de Erro para todos os erros possiveis.

Endpoints Relacionados

Guias de Integracao Completos

Precisa de Ajuda?


Dica Pro: Para estados com 500+ cidades, considere implementar um dropdown com autocomplete pesquisavel em vez de um elemento select tradicional. Isso melhora significativamente a experiencia do usuario e a performance.