Selecionar Cidades

Obter cidades por estado/província, otimizado para selects dropdown

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

Endpoint

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

Autenticação

Inclua sua chave de API como parâmetro de consulta:

?apikey=sua-chave-api

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

Parâmetros de Consulta

Parâmetro Tipo Obrigatório Descrição
apikey string Sim Sua chave de autenticação da API
state string Sim ID do estado (do endpoint /select/states)
lang string Não Código do idioma para nomes das cidades (padrão: pt)

Idiomas Suportados

  • en - Inglês
  • es - Espanhol
  • pt - Português (padrão)
  • fr - Francês
  • de - Alemão
  • it - Italiano

Exemplo de Requisição

# Obter cidades de São 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'; // São 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 formulário 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>País</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 (São Paulo)

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

Campos da Resposta

Campo Tipo Descrição
success boolean Indica se a requisição foi bem-sucedida
data array Array de objetos de cidade
data[].id string Identificador único da cidade (MongoDB ObjectId)
data[].name string Nome da cidade no idioma solicitado

Resposta de Erro

{
  "success": false,
  "error": {
    "code": "MISSING_PARAMETER",
    "message": "O parâmetro obrigatório 'state' está faltando"
  }
}

Resultado Vazio

Se um estado não tiver cidades no banco de dados:

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

Uso de Tokens

Este endpoint consome 1 token por requisição.

Importante: Alguns estados tem milhares de cidades. Considere implementar paginação ou funcionalidade de busca para melhor UX.

Dropdown em Cascata Completo

HTML + JavaScript

<select id="country" onchange="carregarEstados(this.value)">
  <option value="">Selecione o país...</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 países ao carregar a página
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 países quando a página 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

São 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: São 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, Uberlândia, 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, Niterói, Nova Iguaçu, Duque de Caxias, etc.

Implementação 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, 'São');
// Retorna: São Paulo, São Bernardo do Campo, São José dos Campos, etc.

Exemplo de Dropdown Pesquisável

<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>

Implementação 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>

Estratégia 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 memória)
  if (cachesCidades.size > 50) {
    const primeiraChave = cachesCidades.keys().next().value;
    cachesCidades.delete(primeiraChave);
  }
  cachesCidades.set(estadoId, data);

  return data;
}

Otimização 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 requisições/dia
  • Plano básico: 1.000 requisições/dia
  • Plano pro: 10.000 requisições/dia
  • Plano enterprise: Ilimitado

Consulte nossa página de preços para mais detalhes.

Erros Comuns

Código de Erro Descrição Solução
INVALID_API_KEY Chave de API inválida Verifique sua chave no painel da conta
MISSING_PARAMETER Parâmetro state faltando Inclua o ID do estado na requisição
INVALID_STATE_ID Formato do ID do estado inválido Use um ObjectId MongoDB válido de /select/states
QUOTA_EXCEEDED Limite diário de tokens atingido Faça upgrade do plano ou aguarde o reset

Veja a documentação de Códigos de Erro para todos os erros possíveis.

Endpoints Relacionados

Guias de Integração Completos

Precisa de Ajuda?


Dica Pro: Para estados com 500+ cidades, considere implementar um dropdown com autocomplete pesquisável em vez de um elemento select tradicional. Isso melhora significativamente a experiência do usuário e a performance.