Appeler l'API directement
Appelez toute API Unmap directement par HTTPS à https://api.unmap.dev. Les paquets @unmap/*
joignent votre clé, préparent les requêtes et analysent les réponses, mais vous pouvez utiliser
curl, Python ou un client HTTP dans n'importe quel langage. Chaque point de terminaison définit
sa méthode HTTP, ses paramètres et son format de réponse.
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. Les chemins propres à Unmap n'ont pas de préfixe de version;
les chemins d'Optimize compatibles avec Google
acceptent aussi /v1/projects/.... Les points de terminaison
de lecture utilisent généralement GET avec des paramètres de requête. Le géocodage par lots,
les matrices, l'appariement GPS, l'optimisation des arrêts et des parcs de véhicules, les signalements et les importations
utilisent POST; supprimer une superposition utilise DELETE. /route et /isochrone
acceptent aussi POST avec un corps JSON, notamment pour les polygones avoid_areas.
La référence donne chaque méthode et forme de requête.
La clé voyage comme Authorization: Bearer <clé>, X-API-Key: <clé>, X-Goog-Api-Key: <clé> ou
?key=<clé>, vérifiés dans cet ordre.
Les URL de styles générées incluent la clé en paramètre pour les tuiles et ressources;
une intégration MapLibre personnalisée peut définir des en-têtes via transformRequest
(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. La plupart des échecs renvoient du JSON avec un message error et un code.
Les erreurs d'Optimize utilisent un objet error structuré au format de Google.
Une requête mal formée de tuile, de relief, de courbes, de glyphe ou de sprite répond en texte brut. Une archive de relief ou de courbes non configurée est un JSON 404 not_found. Un style dont la
publication active du fond de carte est absente est aussi du texte brut : 503
basemap release is misconfigured (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", "atlas", "swiss", "winter"],
"auth": "API key required via 'Authorization: Bearer um_...', 'X-API-Key', 'X-Goog-Api-Key', or '?key=', checked in that order",
"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.jsonest une description OpenAPI 3.1 des points de terminaison de données et des agents de la passerelle : 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 surapi.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êteLink,rel="api-catalog".
curl https://api.unmap.dev/openapi.json | jq '.paths | keys'
curl https://unmap.dev/.well-known/api-catalogLa 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}'const key = "um_live_...";
const base = "https://api.unmap.dev";
async function unmapGet<T>(path: string, params: Record<string, string>): Promise<T> {
const url = new URL(path, base);
for (const [name, value] of Object.entries(params)) url.searchParams.set(name, value);
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } });
if (!res.ok) {
const body = (await res.json()) as { error: string; code?: string };
throw new Error(`${res.status} ${body.code ?? ""} ${body.error}`.trim());
}
return (await res.json()) as T;
}
type Place = { id: string; name: string; layer: string; lng: number; lat: number };
type Route = {
distanceMeters: number;
durationSeconds: number;
geometry: { type: "LineString"; coordinates: [number, number][] };
};
const [tower] = await unmapGet<Place[]>("/geocode/search", { q: "Calgary Tower", limit: "1" });
const route = await unmapGet<Route>("/route", {
from: `${tower.lng},${tower.lat}`,
to: "-113.9871,51.0899",
mode: "auto",
});import os
import requests
BASE = "https://api.unmap.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['UNMAP_API_KEY']}"}
places = requests.get(f"{BASE}/geocode/search",
params={"q": "Calgary Tower", "limit": 1},
headers=HEADERS, timeout=10)
places.raise_for_status()
tower = places.json()[0]
route = requests.get(f"{BASE}/route",
params={"from": f"{tower['lng']},{tower['lat']}",
"to": "-113.9871,51.0899", "mode": "auto"},
headers=HEADERS, timeout=10)
route.raise_for_status()
print(route.json()["distanceMeters"], route.json()["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, et un 422 outside_coverage d'un 422 no_route.
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.13.0/dist/maplibre-gl.css"
rel="stylesheet"
integrity="sha384-yFNc3bs28S14jOJo1ZO9SnLiAmtyY2a3H9be8GkLMpDGlO5r1zZNP8fYu+fOCE/w"
crossorigin="anonymous"
/>
<div id="map" style="height: 400px"></div>
<script type="module">
import { Map } from "https://unpkg.com/maplibre-gl@6.13.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
une instruction import n'a aucun attribut pour la porter. 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 paires de coordonnées sont en
lng,lat, longitude d'abord. Un point est une paire séparée par une virgule :fromettosur/route,nearsur/geocode/reverse,/isochroneet les autres points de terminaison qui prennent un ancrage. Les clients prennent des tuples[lng, lat]et écrivent les paramètres HTTP pour vous./timezoneutiliselatetlonséparément;/identifyutiliseat=lng,lat. Les corps JSON utilisent des tableaux, comme"near": [lng, lat]surPOST /isochrone. - Encodez la requête.
q=Calgary Towerdoit voyager sous la formeCalgary%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
400a toujours un corps JSON, alors lisez-le ; le message nomme le paramètre fautif. - Respectez
Retry-After. Un429en porte un, sauf si son code estbilling_required. 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. Un502n'en porte pas : le client réessaie ce statut selon son propre délai. - 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 qui a des octets est enpublic, max-age=86400. Un204vide n'envoie pas cet en-tête. 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
- Authentification pour les trois formes de clé et la réponse à une mauvaise clé.
- L'API de cartes, Couches pour
GET /identifyet Couches pourGET /data/query, l'API de géocodage, l'API d'itinéraires et l'API d'altitude pour chaque paramètre et corps de réponse, avec une lignecurlpar point de terminaison. - Erreurs pour les codes de statut et l'union
codesur laquelle brancher. - Si vous voulez finalement les paquets, le Guide de démarrage est la version 60 secondes de cette page.