Documentation
Une API REST et un serveur MCP pour les cartes de classement local. 1 crédit = 1 point de la grille.
Démarrage rapide
1. Créez un compte et une clé de test (gga_test_) dans la console. 2. Envoyez une fiche Google, un mot-clé et le format de la carte à POST /v1/grids. 3. Lisez le résultat avec GET /v1/grids/{id} ou recevez-le par webhook.
curl https://api.geogridapi.io/v1/grids \
-H "Authorization: Bearer gga_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: plombier-vauban-2026-10" \
-d '{
"place_id": "ChIJ…",
"keyword": "plombier",
"size": 7,
"radius_km": 3,
"webhook_url": "https://example.com/webhooks/geogridapi"
}'L’API répond tout de suite 202 avec l’identifiant de la grille, statut pending. La mesure prend de 20 secondes à 2 minutes selon la taille.
curl https://api.geogridapi.io/v1/grids/3f2c… -H "Authorization: Bearer gga_test_…"Authentification
Chaque requête porte une clé dans l’en-tête Authorization: Bearer. Les clés gga_live_ mesurent de vraies grilles et débitent vos crédits (1 crédit = 1 point de la grille). Les clés gga_test_ renvoient des grilles fictives, gratuites et déterministes, pour intégrer sans payer. Une clé n’est affichée qu’une fois, à sa création ; une clé révoquée cesse de fonctionner immédiatement.
Créer une grille
POST /v1/grids. Indiquez soit place_id, soit query (nom et ville) : jamais les deux.
| Paramètre | Type | |
|---|---|---|
place_id | string | Identifiant Google de la fiche (ChIJ…). Trouvez-le avec GET /v1/places/search. |
query | string | Nom et ville de la fiche ; la première correspondance est mesurée. |
center | { lat, lng } | Facultatif : centre de la grille. Évite la résolution de la fiche. |
keyword | string | Mot-clé recherché sur Google Maps (1 à 120 caractères). |
shape | string | Forme de la grille : square (par défaut), circle, diamond, hexagon, cross, ellipse ou rectangle. |
size | 3 | 5 | 7 | 9 | Carré seulement : côté de la grille, 7 par défaut. Coût : size × size points. Interdit avec une autre forme. |
density | low | medium | high | Autres formes seulement : densité du treillis, medium par défaut. Points : cercle 13 / 29 / 49, losange 13 / 25 / 41, hexagone 17 / 27 / 47, croix 21 / 33 / 45, ellipse 19 / 35 / 45, rectangle 15 / 35 / 45. Interdit avec le carré. |
radius_km | number | Distance du centre au bord : de 0,5 à 30 km pour le carré (1,5 / 2,5 / 3,5 / 4,5 km par défaut selon la taille), de 1 à 30 km pour les autres formes (par défaut, la moitié de la plus grande dimension du treillis, en km). |
language | string | Langue de la recherche (code à 2 lettres). Par défaut : la langue du compte. |
country | string | Pays de la recherche (code ISO à 2 lettres), facultatif. |
webhook_url | string | URL HTTPS publique appelée à la fin de la grille. Par défaut : celle du compte. |
En-tête Idempotency-Key (facultatif) : une même clé renvoie la grille déjà créée, sans nouveau débit.
curl https://api.geogridapi.io/v1/grids \
-H "Authorization: Bearer gga_test_…" \
-H "Content-Type: application/json" \
-d '{
"query": "Plomberie Vauban, Lille",
"keyword": "plombier",
"shape": "circle",
"density": "medium",
"radius_km": 3.5
}'Sur le littoral, les points qui tombent en mer ne sont ni mesurés ni débités : points_total peut être inférieur au compte de la forme.
L’objet grille
ranks est une matrice rows × cols (size × size pour le carré) : rangée 0 au nord, colonne 0 à l’ouest. Chaque case donne la position de la fiche dans les résultats Google Maps à ce point ; null signifie absente du top 20. points détaille chaque point mesuré avec ses coordonnées et le top 3 des fiches.
Pour une forme autre que le carré, une case hors de la forme (ou en mer) vaut aussi null dans ranks : c’est points qui fait foi, une case absente de points n’a pas été mesurée. shape et density rappellent le format ; pour une forme, size vaut max(rows, cols).
summary : average_rank (moyenne sur les points où la fiche apparaît), average_rank_with_misses (absences comptées 21), top3_points, found_points.
{
"id": "3f2c9b1e-…",
"object": "grid",
"status": "completed",
"mode": "live",
"place": { "place_id": "ChIJ…", "name": "Plomberie Vauban", "lat": 50.63, "lng": 3.06 },
"keyword": "plombier",
"shape": "square",
"density": null,
"size": 7,
"rows": 7,
"cols": 7,
"radius_km": 3,
"points_total": 49,
"points_charged": 49,
"summary": { "average_rank": 4.3, "average_rank_with_misses": 4.6, "top3_points": 28, "found_points": 48 },
"ranks": [[4, 3, 2, 2, 3, 5, 8], …, [11, 8, 6, 5, 9, 15, null]],
"points": [{ "row": 0, "col": 0, "lat": 50.657, "lng": 3.024, "rank": 4,
"top": [{ "rank": 1, "place_id": "ChIJ…", "name": "…" }, …] }, …],
"error": null,
"created_at": "2026-10-16T10:00:00Z",
"completed_at": "2026-10-16T10:00:41Z"
}GET /v1/grids liste vos grilles, les plus récentes d’abord (limit jusqu’à 100, pagination par before = created_at du dernier élément), sans le détail des points.
Trouver une fiche et lire le solde
curl "https://api.geogridapi.io/v1/places/search?q=Plomberie%20Vauban%20Lille&language=fr" \
-H "Authorization: Bearer gga_live_…"GET /v1/balance renvoie votre solde de crédits. Ces deux appels sont gratuits.
Webhooks
À la fin de chaque grille, GeoGridAPI envoie un POST JSON : type grid.completed ou grid.failed, data = l’objet grille. Trois essais : immédiat, après 1 minute, après 10 minutes. Répondez par un code 2xx.
Chaque appel est signé : en-tête GeoGridAPI-Signature: t=<horodatage>,v1=<signature>, où la signature est un HMAC-SHA256 de « t.corps » avec votre secret whsec_ (console, section Webhook). Refusez un horodatage trop ancien.
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the raw request body, exactly as received.
export function verifyGeoGridSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}import hashlib, hmac, time
def verify_geogrid_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(kv.split("=", 1) for kv in header.split(","))
t = int(parts.get("t", "0"))
if not t or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Erreurs et limites
Toutes les erreurs ont la forme { "error": { "code", "message" } }.
| Statut | Code | Signification |
|---|---|---|
400 | invalid_body / invalid_json | Corps ou paramètres invalides ; le message nomme le champ. |
401 | invalid_key | Clé absente, inconnue ou révoquée. |
402 | insufficient_points | Crédits insuffisants ; required et balance sont indiqués. |
404 | not_found / place_not_found | Grille inconnue pour ce compte, ou aucune fiche correspondante. |
429 | rate_limited / too_many_open_grids | Plus de 60 requêtes par minute et par clé, ou 10 grilles en cours. |
503 | engine_unavailable | Moteur momentanément indisponible ; réessayez (en-tête Retry-After). |
Une grille qui échoue est remboursée automatiquement. Prix : 1 crédit par point de la grille, packs de crédits dès 20 € HT, crédits sans expiration, 500 crédits offerts dès l’inscription.
Serveur MCP
Le serveur MCP donne la grille à Claude, ChatGPT, Cursor et à tout client MCP compatible Streamable HTTP. Outils : find_business, run_grid (mêmes formats que l’API : shape, size ou density, radius_km), get_grid, get_balance. Il utilise vos clés : une clé de test pour essayer gratuitement.
Claude (claude.ai, application) et ChatGPT : ajoutez un connecteur personnalisé avec l’adresse https://api.geogridapi.io/mcp. La connexion ouvre GeoGridAPI : connectez-vous, puis autorisez l’assistant. Les grilles lancées ainsi sont réelles et débitent vos crédits.
claude mcp add --transport http geogridapi https://api.geogridapi.io/mcp \
--header "Authorization: Bearer gga_live_…"{
"mcpServers": {
"geogridapi": {
"url": "https://api.geogridapi.io/mcp",
"headers": { "Authorization": "Bearer gga_live_…" }
}
}
}