Documentation

Selectionner des Villes

Obtenir des villes par etat/province, optimise pour les menus deroulants

Le point de terminaison /v1/select/cities retourne une liste legere de villes filtrees par etat/province, avec uniquement les champs id et name, optimisee pour les menus deroulants.

Point de Terminaison

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

Authentification

Incluez votre cle API comme parametre de requete :

?apikey=votre-cle-api

Votre cle API est comme un mot de passe, gardez-la en securite. Obtenez votre cle depuis le tableau de bord du compte.

Parametres de Requete

Parametre Type Obligatoire Description
apikey string Oui Votre cle d'authentification API
state string Oui ID de l'etat (depuis le point de terminaison /select/states)
lang string Non Code de langue pour les noms de villes (par defaut : fr)

Langues Supportees

  • en - Anglais
  • es - Espagnol
  • pt - Portugais
  • fr - Francais (par defaut)
  • de - Allemand
  • it - Italien

Exemple de Requete

# Obtenir les villes de l'Ile-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'; // Ile-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="">Selectionnez 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 = 'Echec 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>Region</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('Selectionne :', {
      pays: this.selectedCountry,
      region: this.selectedState,
      ville: this.selectedCity
    });
  }
}

Format de Reponse

Reponse de Succes (Ile-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 Reponse

Champ Type Description
success boolean Indique si la requete a reussi
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 demandee

Reponse d'Erreur

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

Resultat Vide

Si une region n'a pas de villes dans la base de donnees :

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

Utilisation des Tokens

Ce point de terminaison consomme 1 token par requete.

Important : Certaines regions ont des milliers de villes. Envisagez d'implementer la pagination ou une fonctionnalite de recherche pour une meilleure UX.

Menu Deroulant en Cascade Complet

HTML + JavaScript

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

<select id="state" onchange="chargerVilles(this.value)" disabled>
  <option value="">Selectionnez une region...</option>
</select>

<select id="city" disabled>
  <option value="">Selectionnez 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');

  // Reinitialiser
  stateSelect.innerHTML = '<option value="">Selectionnez une region...</option>';
  citySelect.innerHTML = '<option value="">Selectionnez 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="">Selectionnez une region...</option>';
  data.forEach(state => {
    stateSelect.add(new Option(state.name, state.id));
  });
  stateSelect.disabled = false;
}

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

  // Reinitialiser
  citySelect.innerHTML = '<option value="">Selectionnez 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="">Selectionnez 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...' : 'Selectionnez 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...' : 'Selectionnez une ville...' }}
    </option>
    <option v-for="city in cities" :key="city.id" :value="city.id">
      {{ city.name }}
    </option>
  </select>
</template>

Exemples par Region

Ile-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-Cote 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-Rhone-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-Etienne, Villeurbanne, etc.

Implementation de Recherche/Filtre

Pour les regions avec beaucoup de villes, implementez une recherche cote 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-Etienne, Saint-Nazaire, etc.

Exemple de Menu Deroulant avec Recherche

<input
  type="text"
  id="city-search"
  placeholder="Rechercher des villes..."
  oninput="filtrerMenuVilles(this.value)"
>
<select id="city">
  <option value="">Selectionnez 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 filtrees = toutesVilles.filter(ville =>
    ville.name.toLowerCase().includes(termeRecherche.toLowerCase())
  );
  afficherVilles(filtrees);
}

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

Implementation de l'Autocompletion

Pour une meilleure UX, implementez l'autocompletion :

<input
  type="text"
  id="city-autocomplete"
  placeholder="Tapez pour rechercher des villes..."
  list="cities-datalist"
>
<datalist id="cities-datalist">
  <!-- Peuple 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>

Strategie de Cache

Mettez en cache les villes par region pour minimiser les appels API :

const cacheVilles = new Map();

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

  // Recuperer 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 eviter les problemes de memoire)
  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 deroulant de la region est en focus :

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

Defilement Virtuel

Pour les tres grandes listes, utilisez des bibliotheques de defilement 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

Gerez toujours les erreurs de maniere elegante :

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('Echec du chargement des villes :', err);
    alert('Echec du chargement des villes. Veuillez reessayer.');
    return [];
  }
}

Limites de Taux

  • Forfait gratuit : 100 requetes/jour
  • Forfait basique : 1 000 requetes/jour
  • Forfait pro : 10 000 requetes/jour
  • Forfait entreprise : Illimite

Consultez notre page de tarification pour plus de details.

Erreurs Courantes

Code d'Erreur Description Solution
INVALID_API_KEY Cle API invalide Verifiez votre cle dans le tableau de bord du compte
MISSING_PARAMETER Parametre state manquant Incluez l'ID de la region dans la requete
INVALID_STATE_ID Format de l'ID de region invalide Utilisez un ObjectId MongoDB valide de /select/states
QUOTA_EXCEEDED Limite quotidienne de tokens atteinte Mettez a niveau votre forfait ou attendez la reinitialisation

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

Points de Terminaison Associes

Guides d'Integration Complets

Besoin d'Aide ?


Astuce Pro : Pour les regions avec 500+ villes, envisagez d'implementer un menu deroulant avec autocompletion recherchable plutot qu'un element select traditionnel. Cela ameliore considerablement l'experience utilisateur et les performances.