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.jsonest 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 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.
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./routeles prend comme une paire séparée par une virgule par point, tandis que/geocode/reverseet/isochroneprennentlonetlatcomme 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 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. 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 enpublic, 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
- 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.