Sélectionner des Villes

Obtenir des villes par état/province, optimisé pour les menus déroulants

Le point de terminaison /v1/select/cities retourne une liste légère de villes filtrées par état/province, avec uniquement les champs id et name, optimisée pour les menus déroulants.

Point de Terminaison

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

Authentification

Incluez votre clé API comme paramètre de requête :

?apikey=votre-cle-api

Votre clé API est comme un mot de passe, gardez-la en sécurité. Obtenez votre clé depuis le tableau de bord du compte.

Paramètres de Requête

Paramètre Type Obligatoire Description
apikey string Oui Votre clé d'authentification API
state string Oui ID de l'état (depuis le point de terminaison /select/states)
lang string Non Code de langue pour les noms de villes (par défaut : fr)

Langues Supportées

  • en - Anglais
  • es - Espagnol
  • pt - Portugais
  • fr - Français (par défaut)
  • de - Allemand
  • it - Italien

Exemple de Requête

# Obtenir les villes de l'Île-de-France
curl "https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=66c7a6c9e4bda21f4ab0f9e1&lang=fr"

JavaScript (Fetch)

const API_KEY = 'votre-cle-api';
const STATE_ID = '66c7a6c9e4bda21f4ab0f9e1'; // Île-de-France

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

const villes = await chargerVilles(STATE_ID);

JavaScript (Axios)

import axios from 'axios';

const response = await axios.get(
  'https://api.countrydataapi.com/v1/select/cities',
  {
    params: {
      apikey: 'votre-cle-api',
      state: '66c7a6c9e4bda21f4ab0f9e1',
      lang: 'fr',
    },
  }
);

Python

import requests

response = requests.get(
    'https://api.countrydataapi.com/v1/select/cities',
    params={
        'apikey': 'votre-cle-api',
        'state': '66c7a6c9e4bda21f4ab0f9e1',
        'lang': 'fr'
    }
)
data = response.json()

PHP

<?php
$apiKey = 'votre-cle-api';
$stateId = '66c7a6c9e4bda21f4ab0f9e1';
$url = "https://api.countrydataapi.com/v1/select/cities?apikey={$apiKey}&state={$stateId}&lang=fr";

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

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

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

  constructor(private http: HttpClient) {}

  getVillesParRegion(regionId: string, lang: string = 'fr'): Observable<Ville[]> {
    const params = new HttpParams()
      .set('apikey', this.API_KEY)
      .set('state', regionId)
      .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 { VilleService } from './city.service';

@Component({
  selector: 'app-city-select',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div *ngIf="loading">Chargement des villes...</div>
    <div *ngIf="error" class="error">{{ error }}</div>
    <select
      *ngIf="!loading && !error"
      [(ngModel)]="selectedCity"
      (ngModelChange)="onCityChange($event)"
      [disabled]="!stateId"
    >
      <option value="">Sélectionnez une ville...</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 villeService: VilleService) {}

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

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

    this.villeService.getVillesParRegion(this.stateId, 'fr').subscribe({
      next: (data) => {
        this.cities = data;
        this.loading = false;
      },
      error: (err) => {
        this.error = 'Échec du chargement des villes';
        this.loading = false;
      }
    });
  }

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

// Exemple complet de formulaire en cascade
// 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>Pays</label>
        <app-country-select (countryChange)="onCountryChange($event)"></app-country-select>
      </div>

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

      <div class="form-group">
        <label>Ville</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('Sélectionné :', {
      pays: this.selectedCountry,
      region: this.selectedState,
      ville: this.selectedCity
    });
  }
}

Format de Réponse

Réponse de Succès (Île-de-France)

{
  "success": true,
  "data": [
    {
      "id": "66c7a6d1e4bda21f4ab23456",
      "name": "Paris"
    },
    {
      "id": "66c7a6d1e4bda21f4ab23457",
      "name": "Boulogne-Billancourt"
    },
    {
      "id": "66c7a6d1e4bda21f4ab23458",
      "name": "Saint-Denis"
    },
    {
      "id": "66c7a6d1e4bda21f4ab23459",
      "name": "Argenteuil"
    },
    {
      "id": "66c7a6d1e4bda21f4ab23460",
      "name": "Montreuil"
    }
    // ... centaines de villes
  ]
}

Champs de la Réponse

Champ Type Description
success boolean Indique si la requête a réussi
data array Tableau d'objets ville
data[].id string Identifiant unique de la ville (MongoDB ObjectId)
data[].name string Nom de la ville dans la langue demandée

Réponse d'Erreur

{
  "success": false,
  "error": {
    "code": "MISSING_PARAMETER",
    "message": "Le paramètre obligatoire 'state' est manquant"
  }
}

Résultat Vide

Si une région n'a pas de villes dans la base de données :

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

Utilisation des Tokens

Ce point de terminaison consomme 1 token par requête.

Important : Certaines régions ont des milliers de villes. Envisagez d'implémenter la pagination ou une fonctionnalité de recherche pour une meilleure UX.

Menu Déroulant en Cascade Complet

HTML + JavaScript

<select id="country" onchange="chargerRegions(this.value)">
  <option value="">Sélectionnez un pays...</option>
</select>

<select id="state" onchange="chargerVilles(this.value)" disabled>
  <option value="">Sélectionnez une région...</option>
</select>

<select id="city" disabled>
  <option value="">Sélectionnez une ville...</option>
</select>

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

// Charger les pays au chargement de la page
async function chargerPays() {
  const response = await fetch(`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=fr`);
  const { data } = await response.json();

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

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

  // Réinitialiser
  stateSelect.innerHTML = '<option value="">Sélectionnez une région...</option>';
  citySelect.innerHTML = '<option value="">Sélectionnez une ville...</option>';
  stateSelect.disabled = true;
  citySelect.disabled = true;

  if (!paysId) return;

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

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

  stateSelect.innerHTML = '<option value="">Sélectionnez une région...</option>';
  data.forEach(state => {
    stateSelect.add(new Option(state.name, state.id));
  });
  stateSelect.disabled = false;
}

async function chargerVilles(regionId) {
  const citySelect = document.getElementById('city');

  // Réinitialiser
  citySelect.innerHTML = '<option value="">Sélectionnez une ville...</option>';
  citySelect.disabled = true;

  if (!regionId) return;

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

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

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

// Charger les pays au chargement de la page
chargerPays();
</script>

Composant 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=votre-cle-api&state=${stateId}&lang=fr`
    )
      .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 ? 'Chargement des villes...' : 'Sélectionnez une ville...'}
      </option>
      {cities.map(city => (
        <option key={city.id} value={city.id}>
          {city.name}
        </option>
      ))}
    </select>
  );
}

export default CitySelect;

Composant 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=votre-cle-api&state=${stateId}&lang=fr`
    );
    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 ? 'Chargement des villes...' : 'Sélectionnez une ville...' }}
    </option>
    <option v-for="city in cities" :key="city.id" :value="city.id">
      {{ city.name }}
    </option>
  </select>
</template>

Exemples par Région

Île-de-France (1200+ villes)

const IDF_ID = '66c7a6c9e4bda21f4ab0f9e1';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${IDF_ID}`
);
// Retourne : Paris, Boulogne-Billancourt, Saint-Denis, Argenteuil, etc.

Provence-Alpes-Côte d'Azur (900+ villes)

const PACA_ID = '66c7a6c9e4bda21f4ab0f9e2';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${PACA_ID}`
);
// Retourne : Marseille, Nice, Toulon, Aix-en-Provence, etc.

Auvergne-Rhône-Alpes (4000+ villes)

const ARA_ID = '66c7a6c9e4bda21f4ab0f9e3';
const response = await fetch(
  `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${ARA_ID}`
);
// Retourne : Lyon, Grenoble, Saint-Étienne, Villeurbanne, etc.

Implémentation de Recherche/Filtre

Pour les régions avec beaucoup de villes, implémentez une recherche côté client :

function filtrerVilles(villes, termeRecherche) {
  if (!termeRecherche) return villes;

  return villes.filter(ville =>
    ville.name.toLowerCase().includes(termeRecherche.toLowerCase())
  );
}

// Utilisation
const toutesVilles = await chargerVilles(regionId);
const villesFiltrees = filtrerVilles(toutesVilles, 'Saint');
// Retourne : Saint-Denis, Saint-Étienne, Saint-Nazaire, etc.

Exemple de Menu Déroulant avec Recherche

<input
  type="text"
  id="city-search"
  placeholder="Rechercher des villes..."
  oninput="filtrerMenuVilles(this.value)"
>
<select id="city">
  <option value="">Sélectionnez une ville...</option>
</select>

<script>
let toutesVilles = [];

async function chargerVilles(regionId) {
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${regionId}`
  );
  const { data } = await response.json();
  toutesVilles = data;
  afficherVilles(toutesVilles);
}

function filtrerMenuVilles(termeRecherche) {
  const filtrées = toutesVilles.filter(ville =>
    ville.name.toLowerCase().includes(termeRecherche.toLowerCase())
  );
  afficherVilles(filtrées);
}

function afficherVilles(villes) {
  const select = document.getElementById('city');
  select.innerHTML = '<option value="">Sélectionnez une ville...</option>';
  villes.forEach(ville => {
    select.add(new Option(ville.name, ville.id));
  });
}
</script>

Implémentation de l'Autocomplétion

Pour une meilleure UX, implémentez l'autocomplétion :

<input
  type="text"
  id="city-autocomplete"
  placeholder="Tapez pour rechercher des villes..."
  list="cities-datalist"
>
<datalist id="cities-datalist">
  <!-- Peuplé dynamiquement -->
</datalist>

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

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

Stratégie de Cache

Mettez en cache les villes par région pour minimiser les appels API :

const cacheVilles = new Map();

async function getVilles(regionId) {
  // Vérifier le cache d'abord
  if (cacheVilles.has(regionId)) {
    return cacheVilles.get(regionId);
  }

  // Récupérer depuis l'API
  const response = await fetch(
    `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${regionId}&lang=fr`
  );
  const { data } = await response.json();

  // Sauvegarder dans le cache (limiter la taille pour éviter les problèmes de mémoire)
  if (cacheVilles.size > 50) {
    const premiereCle = cacheVilles.keys().next().value;
    cacheVilles.delete(premiereCle);
  }
  cacheVilles.set(regionId, data);

  return data;
}

Optimisation des Performances

Chargement Paresseux

Charger les villes uniquement lorsque le menu déroulant de la région est en focus :

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

Défilement Virtuel

Pour les très grandes listes, utilisez des bibliothèques de défilement virtuel :

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

Gestion des Erreurs

Gérez toujours les erreurs de manière élégante :

async function chargerVilles(regionId) {
  try {
    const response = await fetch(
      `https://api.countrydataapi.com/v1/select/cities?apikey=votre-cle-api&state=${regionId}`
    );

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

    const result = await response.json();

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

    return result.data;
  } catch (err) {
    console.error('Échec du chargement des villes :', err);
    alert('Échec du chargement des villes. Veuillez réessayer.');
    return [];
  }
}

Limites de Taux

  • Forfait gratuit : 100 requêtes/jour
  • Forfait basique : 1 000 requêtes/jour
  • Forfait pro : 10 000 requêtes/jour
  • Forfait entreprise : Illimité

Consultez notre page de tarification pour plus de details.

Erreurs Courantes

Code d'Erreur Description Solution
INVALID_API_KEY Clé API invalide Vérifiez votre clé dans le tableau de bord du compte
MISSING_PARAMETER Paramètre state manquant Incluez l'ID de la région dans la requête
INVALID_STATE_ID Format de l'ID de région invalide Utilisez un ObjectId MongoDB valide de /select/states
QUOTA_EXCEEDED Limite quotidienne de tokens atteinte Mettez à niveau votre forfait ou attendez la réinitialisation

Voir la documentation des Codes d'Erreur pour toutes les erreurs possibles.

Points de Terminaison Associés

Guides d'Intégration Complets

Besoin d'Aide ?


Astuce Pro : Pour les régions avec 500+ villes, envisagez d'implémenter un menu déroulant avec autocomplétion recherchable plutôt qu'un élément select traditionnel. Cela améliore considérablement l'expérience utilisateur et les performances.