Aller au contenu

Appeler l'API directement

Les paquets @unmap/* sont une commodité, pas une exigence. Tout ce qu'ils font est une requête GET vers https://api.unmap.dev avec votre clé jointe, et tout ce qu'ils renvoient est le JSON de la passerelle, tel quel. Si votre application n'est pas en JavaScript, si vous préférez ne pas ajouter une dépendance, ou si vous avez déjà un client HTTP en qui vous avez confiance, vous pouvez vous passer entièrement des paquets sans rien perdre : il n'existe aucun point de terminaison que le SDK atteint et que du HTTPS ordinaire ne peut pas atteindre.

Il existe une troisième voie entre les deux. Si vous voulez les paquets sans étape de compilation, unmap.dev sert le SDK complet en une seule balise de script ; voyez le Démarrage rapide.

Cette page contient tout ce que vous devez savoir pour le faire : (1) la forme du contrat, (2) le même géocodage et le même itinéraire en curl, fetch et Python, (3) l'affichage d'une carte avec un MapLibre GL JS standard et rien d'autre, et (4) ce que les paquets faisaient pour vous et qui devient votre tâche. Les paramètres et les corps de réponse de chaque point de terminaison ne sont pas répétés ici ; ils se trouvent sur l'API de cartes, Couches (y compris GET /identify et GET /data/query), l'API de géocodage, l'API d'itinéraires, l'API de transport en commun et l'API d'altitude, et ces pages décrivent déjà le contrat HTTP plutôt que le SDK. L'identification point-dans-polygone est GET /identify / unmap.identify(). La liste par bbox d'une couche interrogeable est GET /data/query / unmap.data.query().

Le contrat en un paragraphe

L'URL de base est https://api.unmap.dev, sans préfixe de version. Presque chaque point de terminaison est un GET avec des paramètres de requête et sans corps. Les exceptions sont POST /byod et DELETE /byod/{id} (une superposition privée; voir Apportez vos propres données) et POST /geocode/batch (jusqu'à 100 recherches dans un corps JSON; voir Géocodage par lots). La clé voyage comme Authorization: Bearer <clé>, X-API-Key: <clé> ou ?key=<clé> ; MapLibre récupère lui-même les tuiles, les glyphes et les sprites et ne peut envoyer que la dernière forme (voir Authentification). Les réponses de géocodage, d'itinéraires et de styles sont en application/json ; les tuiles sont des octets de tuiles vectorielles, les glyphes et les sprites sont dans les formats binaires et image que MapLibre attend. Un échec est un corps JSON avec un message error et, pour les échecs d'authentification et de forfait, un code, sauf sur les chemins des tuiles, du terrain, des courbes de niveau, des glyphes et des sprites, qui répondent en texte brut (voir Erreurs).

Le point de terminaison racine ne demande pas de clé, et il décrit les autres :

curl https://api.unmap.dev/
{
  "name": "unmap gateway",
  "endpoints": ["/tiles/{z}/{x}/{y}", "/styles/{style}.json", "/geocode/search", /* … */],
  "styles": ["base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel"],
  "auth": "API key required via 'Authorization: Bearer um_...', 'X-API-Key', or '?key='",
  "docs": "https://unmap.dev/docs",
  "openapi": "https://api.unmap.dev/openapi.json",
  "apiCatalog": "https://api.unmap.dev/.well-known/api-catalog",
  /* plus authMd, mcp, a2a, agentCard et protectedResource, et l'attribution des données */
}

Description lisible par machine

Si vous branchez un outil, un générateur de code ou un agent plutôt que de lire, deux autres documents sans authentification décrivent le même contrat dans des formats faits pour cela :

  • https://api.unmap.dev/openapi.json est une description OpenAPI 3.1 de chaque point de terminaison de cette page et des trois suivantes : les paramètres, les trois formes de clé comme schémas de sécurité, et les corps de réponse et d'erreur. Générez un client à partir de ce document, ou donnez-le à un agent.
  • https://unmap.dev/.well-known/api-catalog (aussi sur api.unmap.dev) est un catalogue d'API RFC 9727 : un linkset qui nomme l'API, pointe vers le document OpenAPI et renvoie vers cette documentation en français et en anglais. Les deux pages d'accueil et la racine de l'API l'annoncent par un en-tête Link, rel="api-catalog".
curl https://api.unmap.dev/openapi.json | jq '.paths | keys'
curl https://unmap.dev/.well-known/api-catalog

La réponse de la racine renvoie vers quelques autres documents sans authentification dans le même esprit : authMd (/auth.md, comment un agent obtient une identification), protectedResource (les métadonnées RFC 9728), agentCard et mcpServerCard.

Le document OpenAPI ne couvre que le plan de données par clé d'API. L'API de compte derrière le tableau de bord s'authentifie par une session de navigateur et n'y figure pas.

Les deux mêmes requêtes, de trois façons

Géocodez une requête, puis conduisez à partir de la réponse. Les trois onglets sont un seul programme : choisissez le langage dans lequel vous travaillez. fetch est tout ce que @unmap/geocoding et @unmap/routing utilisent en dessous, et les seules choses que les paquets ajoutent sont la construction de l'URL, une erreur levée sur un statut non-2xx et des types TypeScript pour la réponse ; l'onglet fetch est donc la bibliothèque cliente écrite à la main, et il s'exécute tel quel dans Node, Deno, Bun, les Workers ou un navigateur. Placez votre clé dans une variable d'environnement et chaque onglet s'exécute tel qu'écrit.

export UNMAP_API_KEY=um_live_...
 
# Géocodage direct. La réponse est un tableau JSON, donc passez-la à jq pour la lire.
curl "https://api.unmap.dev/geocode/search?q=Calgary%20Tower&limit=1" \
  -H "Authorization: Bearer $UNMAP_API_KEY" | jq '.[0] | {name, lng, lat}'
 
# Conduire entre deux points. Les coordonnées sont lng,lat et la virgule n'a pas besoin d'encodage.
curl "https://api.unmap.dev/route?from=-114.0719,51.0447&to=-113.9871,51.0899&mode=auto" \
  -H "Authorization: Bearer $UNMAP_API_KEY" | jq '{distanceMeters, durationSeconds}'

Le premier appel affiche le nom et les coordonnées du meilleur résultat ; le second affiche une distance routière en mètres et une durée en secondes. Tous les autres points de terminaison suivent le même schéma : un chemin, quelques paramètres de requête, un en-tête. Les pages API de géocodage et API d'itinéraires donnent une ligne curl et une ligne fetch pour chacun.

Deux détails de ces onglets méritent de passer dans votre propre code. D'abord, laissez la bibliothèque encoder la requête : URL et URLSearchParams dans l'onglet fetch, params= dans l'onglet Python. C'est ce qui fait qu'une requête comme rue Sainte-Catherine, ou une requête écrite en syllabaire, arrive intacte. Lire le corps en cas d'échec vaut aussi la ligne supplémentaire : le message nomme le paramètre fautif, et le code distingue un 429 de limite de débit d'un 429 de quota de forfait.

Ensuite, fixez un délai d'attente, comme le fait l'onglet Python. Le géocodage et /route répondent en quelques centaines de millisecondes, mais une isochrone représente plusieurs passes de calcul dans le conteneur d'itinéraires et a été mesurée autour de 700 ms à chaud le 2026-09-02, donc un client sans aucun délai finira un jour par attendre dessus.

Afficher une carte avec un MapLibre standard

Une carte n'a pas non plus besoin de code @unmap. Le point de terminaison des styles renvoie un document de style MapLibre complet dont les URL de tuiles, de glyphes et de sprites pointent déjà vers la passerelle avec votre clé dans leurs paramètres, de sorte qu'un MapLibre GL JS ordinaire pointé vers cette seule URL découvre tout le reste lui-même. Voici la page entière, avec MapLibre chargé depuis un CDN et sans étape de compilation :

<link
  href="https://unpkg.com/maplibre-gl@6.7.0/dist/maplibre-gl.css"
  rel="stylesheet"
  integrity="sha384-Q5Blg3vUVAlUKqIPJYz7wGnz40Vwrx4pVuFVicI73+8c/26Zr5hhckfuIiIUflLE"
  crossorigin="anonymous"
/>
 
<div id="map" style="height: 400px"></div>
 
<script type="module">
  import { Map } from "https://unpkg.com/maplibre-gl@6.7.0/dist/maplibre-gl.mjs";
 
  new Map({
    container: "map",
    style: "https://api.unmap.dev/styles/base.json?mode=light&key=um_live_...",
    center: [-114.0719, 51.0447],
    zoom: 11,
  });
</script>

MapLibre 6 est distribué uniquement en ESM. Il n'y a plus de version UMD ni de variable globale maplibregl : le script est donc un module, et les éléments voulus sont importés par leur nom. La feuille de style porte toujours une empreinte integrity; le module, lui, ne le peut pas, car maplibre-gl.mjs charge un fichier voisin maplibre-gl-shared.mjs que le navigateur résout lui-même et que Subresource Integrity ne couvre pas. C'est la deuxième raison pour laquelle le CDN est ici une démonstration et non une recommandation : dans une application, installez maplibre-gl et servez-le depuis votre propre site, comme le fait le Guide de démarrage.

La clé doit être dans l'URL du style sous la forme ?key=, parce que c'est la seule forme que les requêtes de tuiles, de glyphes et de sprites lancées par MapLibre peuvent porter, et la passerelle recopie la clé qui a récupéré le style dans les URL qu'elle écrit. Tout ce que font les options style, mode, lang et theme du SDK est un paramètre sur cette même URL, de sorte que /styles/outdoor.json?mode=dark&lang=fr est une carte outdoor sombre avec des étiquettes en français et que ?theme=<code> applique un thème venu de /create. La liste complète des styles et des paramètres est sur l'API de cartes.

La même URL fonctionne partout où un style MapLibre est accepté : MapLibre Native sur iOS et Android, le greffon MapLibre pour Leaflet, ou tout moteur de rendu qui lit un document de spécification de style version 8. Vous installez notre cartographie, pas notre code.

Appeler depuis un navigateur

L'API répond aux requêtes intersites depuis n'importe quelle origine : GET, HEAD, OPTIONS et POST (ce dernier n'atteint que /mcp, le point de terminaison JSON-RPC ; tout ce qui figure sur cette page est un GET), avec les en-têtes Authorization et X-API-Key permis. Les deux formes d'en-tête fonctionnent donc depuis un fetch de navigateur, mais ?key= est le meilleur choix à cet endroit. Un en-tête personnalisé transforme chaque requête en requête avec vérification préalable, un aller-retour OPTIONS de plus avant le GET, tandis qu'un paramètre de requête est une requête simple qui passe directement, et c'est le mécanisme que les requêtes de tuiles de la carte utilisent déjà.

Ce qu'un navigateur envoie et qu'un serveur n'envoie pas est un en-tête Origin, et la passerelle le lit. Une requête depuis une origine de développement est comptée mais jamais facturée : localhost, 127.0.0.1, [::1], tout hôte *.localhost, et toute prévisualisation *.pages.dev ou *.workers.dev, pourvu que la clé autorise cette origine. Une requête depuis toute autre origine est facturée normalement. Si la clé porte une liste d'origines autorisées, tout ce qui n'y figure pas reçoit un 403 avec le code origin_not_allowed, y compris une requête qui n'envoie aucun Origin. Un appel côté serveur ne porte aucun Origin et compte toujours comme du trafic de production, quelle que soit la clé utilisée : donnez donc à votre serveur une clé sans origine autorisée. Les règles exactes sont sur Forfaits et limites.

Ce que les paquets faisaient pour vous

S'en passer signifie reprendre quelques responsabilités que les clients assumaient. Aucune n'est difficile ; elles sont listées pour que rien ne soit une surprise :

  • Les coordonnées sont en lng,lat, longitude d'abord, partout. /route les prend comme une paire séparée par une virgule par point, tandis que /geocode/reverse et /isochrone prennent lon et lat comme deux paramètres distincts. Les clients cachent cette différence derrière des tuples [lng, lat] ; en HTTP, vous écrivez chacune comme sa page le montre.
  • Encodez la requête. q=Calgary Tower doit voyager sous la forme Calgary%20Tower. Toute bibliothèque HTTP le fait quand vous passez les paramètres comme un dictionnaire plutôt qu'en concaténant une chaîne.
  • Vérifiez le statut vous-même. Les clients lèvent une erreur sur toute réponse non-2xx. En HTTP, un 400 a toujours un corps JSON, alors lisez-le ; le message nomme le paramètre fautif.
  • Respectez Retry-After. Un 429 en porte un. Attendez ce nombre de secondes au lieu de réessayer immédiatement ; la limite de rafale est de 1 000 requêtes par 60 secondes par clé, par site Cloudflare.
  • Laissez les en-têtes de cache travailler. Une réponse de géocodage est mise en cache à notre périphérie pendant 24 heures, un itinéraire ou une isochrone est en private, max-age=3600 (votre client peut la réutiliser, un cache partagé ne le doit pas), et une tuile est en public, max-age=86400. Un navigateur respecte tout cela automatiquement. Un client côté serveur ne le fait que s'il a un cache HTTP, et une requête d'itinéraire répétée sans cache est un appel facturable répété.
  • Gardez une clé um_live_ hors de tout ce que vous distribuez à des inconnus, ou inscrivez les origines d'où elle peut servir pour qu'une clé copiée soit inutile ailleurs. Sur le serveur, une variable d'environnement suffit, et cette clé ne devrait porter aucune origine autorisée.

Prochaines étapes