À propos de ce cours
Suivez une démarche complète pour utiliser une API publique avec l'exemple d'Open-Meteo : choix de l'API, lecture de la documentation, test de l'adresse, exploration du JSON (tableaux parallèles, unités), fonction fetch, page de météo de cinq villes tunisiennes en parallèle avec Promise.allSettled, sécurité des clés d'API avec un relais PHP, quotas, cache et attribution, autres API publiques. Avec des activités corrigées.
-
Objectifs
- Suivre une démarche complète pour utiliser une API publique : choisir, lire la documentation, tester, coder, afficher.
- Lire une documentation d'API et construire une requête avec ses paramètres.
- Explorer et exploiter une réponse JSON réelle (objets, tableaux parallèles, unités).
- Interroger plusieurs lieux en parallèle avec
Promise.allSettled. - Respecter les règles d'usage : clés d'API, quotas, cache, attribution.
-
Les API publiques
- Des milliers d'organismes mettent des données à disposition par des API web : météo, cartes, transports, taux de change, livres, données publiques des États (open data), etc. Les utiliser permet de créer des applications riches sans produire soi-même ces données.
- Elles diffèrent par leurs conditions d'accès:
Type d'accès Exemple Utilisable directement depuis une page ? Libre, sans clé Open-Meteo (météo), JSONPlaceholder (données de test) Oui, si CORS est autorisé Avec clé d'API gratuite De nombreux services météo, cartographiques ou financiers Non : la clé serait visible. Passer par son serveur Avec authentification de l'utilisateur (OAuth) Agenda, messagerie, réseaux sociaux Oui, après connexion de l'utilisateur Payante Données boursières, traduction, IA Non : toujours via son serveur - Pour ce cours, nous utilisons Open-Meteo: service européen de prévisions, gratuit pour un usage non commercial, sans inscription ni clé, qui autorise les appels depuis une page web (CORS). Ses données sont sous licence CC BY 4.0 : il faut citer la source.

-
Étape 1 : choisir l'API
Avant d'écrire une ligne de code, vérifiez :
- Les données: contient-elle ce dont j'ai besoin (température actuelle, prévisions heure par heure, sur combien de jours, pour la Tunisie) ?
- L'accès: clé obligatoire ? CORS autorisé ? HTTPS ?
- Les limites: nombre de requêtes par minute ou par jour (les dépasser donne une erreur
429). - Les conditions: usage commercial permis ? attribution obligatoire ?
- La fiabilité: documentation claire, service maintenu, versions de l'API.
Étape 2 : lire la documentation
- La documentation d'Open-Meteo (open-meteo.com/en/docs) propose un formulaire qui construit l'adresse pour vous : choisissez un lieu et des variables, et l'URL apparaît en bas de page. Les principaux paramètres :
| Paramètre | Exemple | Rôle |
|---|---|---|
latitude, longitude | 36.8065, 10.1815 | Le lieu (Tunis), obligatoires |
current | temperature_2m,weather_code | Variables de la météo actuelle |
hourly | temperature_2m,precipitation_probability | Variables heure par heure |
daily | temperature_2m_max,temperature_2m_min | Variables par jour |
timezone | auto | Heures exprimées dans le fuseau du lieu |
forecast_days | 7 | Nombre de jours de prévision |
temperature_unit | fahrenheit | Unité (Celsius par défaut) |
- Le temps est décrit par un code WMO (Organisation météorologique mondiale) : 0 = ciel dégagé, 3 = couvert, 61 = pluie faible, 95 = orage… La documentation en donne la liste complète, à traduire en texte et en icône.
- Une seconde API, Geocoding (
https://geocoding-api.open-meteo.com/v1/search?name=Sfax&count=1&language=fr), transforme un nom de ville en coordonnées : elle servira dans le TP 19.
Étape 3 : tester l'adresse
- Collez cette adresse dans votre navigateur (Firefox et Chrome affichent le JSON de façon lisible) :
https://api.open-meteo.com/v1/forecast?latitude=36.8065&longitude=10.1815¤t=temperature_2m,weather_code,wind_speed_10m&daily=temperature_2m_max,temperature_2m_min&timezone=auto&forecast_days=3
- Vous obtenez une réponse de cette forme (valeurs d'exemple) :
{
"latitude": 36.8125,
"longitude": 10.1875,
"timezone": "Africa/Tunis",
"current_units": { "temperature_2m": "°C", "wind_speed_10m": "km/h" },
"current": {
"time": "2026-11-12T21:45",
"temperature_2m": 17.6,
"weather_code": 3,
"wind_speed_10m": 18.4
},
"daily_units": { "temperature_2m_max": "°C", "temperature_2m_min": "°C" },
"daily": {
"time": ["2026-11-12", "2026-11-13", "2026-11-14"],
"temperature_2m_max": [21.2, 19.1, 18.4],
"temperature_2m_min": [13.1, 12.5, 11.9]
}
}
currentest un objet simple.daily(ethourly) contient des tableaux parallèles: l'indice1de chaque tableau concerne le même jour (le 13 novembre, max 19,1 °C, min 12,5 °C).- Les unités sont fournies à part (
current_units,daily_units) : affichez-les plutôt que de les écrire en dur. - Les coordonnées renvoyées diffèrent un peu de celles demandées : le modèle calcule sur une grille et renvoie le point le plus proche.
- Faites une erreur volontaire (
latitude=999) : l'API répond400avec un message d'explication en JSON. Prévoyez ce cas dans le code.
-
Étape 4 : coder la requête
async function meteoActuelle(latitude, longitude) {
const params = new URLSearchParams({
latitude,
longitude,
current: "temperature_2m,weather_code,wind_speed_10m",
timezone: "auto",
});
const reponse = await fetch(`https://api.open-meteo.com/v1/forecast?${params}`, {
signal: AbortSignal.timeout(8000),
});
if (!reponse.ok) {
throw new Error(`Service météo : erreur ${reponse.status}`);
}
const donnees = await reponse.json();
return donnees.current; // on ne garde que ce qui sert
}
const meteo = await meteoActuelle(36.8065, 10.1815);
console.log(`${meteo.temperature_2m} °C, vent ${meteo.wind_speed_10m} km/h`);
- Le code reprend tout ce que nous avons vu :
URLSearchParams, délai maximal, test dereponse.ok, lecture du JSON. La fonction renvoie seulement les données utiles, pour que le reste de l'application ne dépende pas de la structure exacte de l'API.
Étape 5 : exemple complet, la météo de cinq villes
La page affiche la météo actuelle de cinq villes tunisiennes. Les cinq requêtes partent en même temps; si l'une échoue, les autres s'affichent quand même grâce à Promise.allSettled.
index.html:
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<title>Météo des villes tunisiennes</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.9/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<script type="module" src="meteo.js"></script>
</head>
<body class="container py-4" style="max-width: 640px">
<h1 class="h3">🌍 Météo en Tunisie</h1>
<p class="text-body-secondary" id="etat">Chargement…</p>
<table class="table">
<thead>
<tr>
<th>Ville</th>
<th>Temps</th>
<th class="text-end">Température</th>
<th class="text-end">Vent</th>
</tr>
</thead>
<tbody id="tableau"></tbody>
</table>
<p class="small">
Données : <a href="https://open-meteo.com/" target="_blank" rel="noopener">Open-Meteo.com</a>
</p>
</body>
</html>
meteo.js:
const VILLES = [
{ nom: "Tunis", latitude: 36.8065, longitude: 10.1815 },
{ nom: "Sfax", latitude: 34.7406, longitude: 10.7603 },
{ nom: "Sousse", latitude: 35.8256, longitude: 10.6084 },
{ nom: "Gabès", latitude: 33.8815, longitude: 10.0982 },
{ nom: "Tozeur", latitude: 33.9197, longitude: 8.1335 },
];
const DESCRIPTIONS = {
0: "☀️ Dégagé",
1: "🌤️ Peu nuageux",
2: "⛅ Nuageux",
3: "☁️ Couvert",
61: "🌦️ Pluie",
};
function urlMeteo({ latitude, longitude }) {
const params = new URLSearchParams({
latitude,
longitude,
current: "temperature_2m,weather_code,wind_speed_10m",
timezone: "auto",
});
return `https://api.open-meteo.com/v1/forecast?${params}`;
}
async function meteoDe(ville) {
const reponse = await fetch(urlMeteo(ville), { signal: AbortSignal.timeout(8000) });
if (!reponse.ok) {
throw new Error(`HTTP ${reponse.status}`);
}
const donnees = await reponse.json();
return { ...ville, ...donnees.current };
}
function cellule(texte, classe = "") {
const td = document.createElement("td");
td.className = classe;
td.textContent = texte;
return td;
}
const debut = performance.now();
// 5 requêtes en parallèle ; allSettled attend la fin de TOUTES, même en cas d'échec
const resultats = await Promise.allSettled(VILLES.map(meteoDe));
const duree = Math.round(performance.now() - debut);
const lignes = resultats.map((resultat, i) => {
const tr = document.createElement("tr");
if (resultat.status === "fulfilled") {
const meteo = resultat.value;
tr.append(
cellule(meteo.nom, "fw-semibold"),
cellule(DESCRIPTIONS[meteo.weather_code] ?? `Code ${meteo.weather_code}`),
cellule(`${Math.round(meteo.temperature_2m)} °C`, "text-end"),
cellule(`${Math.round(meteo.wind_speed_10m)} km/h`, "text-end"),
);
} else {
tr.append(
cellule(VILLES[i].nom, "fw-semibold"),
cellule(`Indisponible (${resultat.reason.message})`),
);
}
return tr;
});
document.getElementById("tableau").replaceChildren(...lignes);
const reussies = resultats.filter((resultat) => resultat.status === "fulfilled").length;
document.getElementById("etat").textContent =
`${reussies}/${VILLES.length} villes chargées en ${duree} ms.`;
Promise.allSettledrenvoie, pour chaque promesse, un objet{ status: "fulfilled", value }ou{ status: "rejected", reason }: on affiche les données ou un message d'indisponibilité, ville par ville.performance.now()mesure la durée totale : environ celle de la requête la plus lente, et non la somme des cinq.- La source des données est citée en bas de page, comme l'exigent les conditions d'Open-Meteo.
Les clés d'API : jamais dans le JavaScript
- Beaucoup d'API exigent une clé, une chaîne secrète qui identifie votre compte (et vos quotas, voire votre facture). Tout ce qui est dans le JavaScript d'une page est public: n'importe qui peut lire la clé dans le code source ou l'onglet Réseau, et l'utiliser à votre place.
// ❌ À NE PAS FAIRE : la clé est visible par tous (code source, onglet Réseau)
fetch(`https://api.service-meteo.com/data?q=Tunis&appid=8f3a1c2e9b7d4f60a5e1c3b2d9f8e7a6`);
// ✅ Le navigateur appelle VOTRE serveur, qui ajoute la clé (gardée dans un fichier .env)
fetch("/api/meteo?ville=Tunis");
- La solution : un petit relais (proxy) sur votre serveur. Le navigateur appelle votre serveur, qui ajoute la clé (rangée dans une variable d'environnement, jamais dans Git) et renvoie la réponse. Exemple en PHP :
<?php
// api/meteo.php – petit relais (proxy) côté serveur : la clé ne quitte jamais le serveur
$ville = urlencode($_GET['ville'] ?? 'Tunis');
$cle = getenv('METEO_API_KEY');
$reponse = file_get_contents("https://api.service-meteo.com/data?q=$ville&appid=$cle&lang=fr");
header('Content-Type: application/json');
header('Cache-Control: max-age=600'); // le navigateur peut réutiliser la réponse 10 minutes
echo $reponse;
- Bonus : le serveur peut mettre les réponses en cache et limiter le nombre d'appels, ce qui protège aussi vos quotas.
- Si une clé a été publiée par erreur (dans un dépôt GitHub par exemple), elle doit être considérée comme compromise : révoquez-la et créez-en une nouvelle.
Respecter l'API : quotas, cache, attribution
- Quotas : chaque API limite le nombre de requêtes. Ne lancez pas une requête à chaque frappe au clavier : attendez une pause de saisie de 300 ms (technique du debounce, TP 19 et chapitre 23) et annulez les requêtes devenues inutiles.
- Erreur 429 : si vous la recevez, attendez avant de réessayer (l'en-tête
Retry-Afterindique parfois combien de secondes). - Cache : la météo change peu en quelques minutes ; gardez la dernière réponse en mémoire ou dans le
localStorage(chapitre 20) au lieu de rappeler l'API. - Attribution : citez la source avec un lien, comme demandé par la licence.
- Robustesse : une API externe peut être lente, en panne ou modifier son format. Prévoyez un message clair et un affichage dégradé, et ne laissez jamais une page entière dépendre d'un seul appel.
D'autres API publiques pour s'entraîner
| API | Données | Accès |
|---|---|---|
| Open-Meteo | Météo, qualité de l'air, géocodage, historique | Libre, sans clé |
| JSONPlaceholder | Faux utilisateurs, articles, tâches (pour les tests) | Libre, sans clé |
| Open Library | Livres, auteurs, couvertures | Libre, sans clé |
| Frankfurter | Taux de change publiés par la Banque centrale européenne | Libre, sans clé |
| Nager.Date | Jours fériés par pays (dont la Tunisie) | Libre, sans clé |
- Avant de les utiliser, appliquez la démarche : documentation, test de l'adresse, conditions d'usage. Les services gratuits évoluent : vérifiez qu'ils sont toujours actifs.
Activités
Activité 1 : explorer l'API
- Avec le formulaire de la documentation d'Open-Meteo, construisez l'adresse qui donne pour Sousse la température maximale et minimale et la somme des précipitations des 5 prochains jours. Testez-la dans le navigateur.
- Quelle est la température maximale prévue après-demain ? À quel indice des tableaux se trouve-t-elle ?
- Que renvoie l'API si vous oubliez le paramètre
longitude?
Activité 2 : les cinq villes
- Dans le dossier
api-meteode votre dépôtjs-ajax-fetch, reproduisez l'exemple complet. - Complétez la table
DESCRIPTIONSavec tous les codes WMO de la documentation. - Ajoutez une colonne « Demain » avec la température maximale du lendemain (paramètre
daily). - Ajoutez un bouton « Actualiser » et l'heure de la dernière mise à jour.
Activité 3 : une autre API
- Avec l'API Nager.Date (
https://date.nager.at/api/v3/PublicHolidays/2027/TN), affichez la liste des jours fériés en Tunisie pour 2027, avec la date au format « samedi 20 mars 2027 » (Intl.DateTimeFormat) et le nombre de jours restant avant chacun.
Activité 4 : questions
- Pourquoi ne faut-il jamais écrire une clé d'API dans le JavaScript d'une page ? Que faire à la place ?
- Dans la réponse d'Open-Meteo, comment retrouver la température minimale du 3e jour ?
- Quelle est la différence entre
Promise.alletPromise.allSettled? Laquelle convient pour la page des cinq villes, et pourquoi ? - Que signifie une réponse
429?
Corrigés. Activité 1 :
…/forecast?latitude=35.8256&longitude=10.6084&daily=temperature_2m_max,temperature_2m_min,precipitation_sum&timezone=auto&forecast_days=5; après-demain se trouve à l'indice 2 ; sans longitude, l'API répond par une erreur 400 accompagnée d'un message. Activité 2 : pour « Demain », ajoutez daily: "temperature_2m_max" et lisez donnees.daily.temperature_2m_max[1]. Activité 4 : elle serait visible par tout le monde et pourrait être utilisée à vos frais ; passer par un relais sur son serveur ; donnees.daily.temperature_2m_min[2]; Promise.all échoue dès qu'une requête échoue, Promise.allSettled attend tous les résultats ; allSettled, pour afficher les villes disponibles même si l'une est en erreur ; trop de requêtes, quota dépassé.À retenir
- Démarche : choisir l'API, lire la documentation, tester l'adresse, coder avec
fetch, afficher en gérant chargement et erreurs. - Explorez toujours le JSON avant de coder ; attention aux tableaux parallèles et aux unités.
Promise.allSettledpour plusieurs requêtes indépendantes qui peuvent échouer séparément.- Jamais de clé d'API dans le JavaScript ; respectez quotas, cache et attribution.
Sources : MDN Web Docs, WHATWG (XMLHttpRequest, Fetch), IETF, open-meteo.com.