SignalQuest External API v1
Documentation API externe SignalQuest
Contrat public pour lire les antennes, speedtests, points de couverture, photos et archives ANFR. L'API est majoritairement read-only; l'upload photo partenaire est la seule écriture exposée.
15
5
1
Base URL
https://signalquest.fr/api/external/v1Tous les exemples ci-dessous utilisent cette base, avec Authorization: Bearer sq_live_....
Mode signé (HMAC)
Active pour générer les exemples avec signature HMAC (clés MOBILE et SERVER + requireSignature).
Authentification
Header obligatoire :
Authorization: Bearer sq_live_…ou X-API-Key: sq_live_…
⚠️ Le passage en query string ?api_key=… n'est plus accepté.
3 modes de clés (BROWSER / SERVER / MOBILE) avec contraintes propres — voir Modes & signing.
Quotas
Par défaut par clé :
- 60 requêtes / minute
- 10 000 requêtes / jour
Headers X-RateLimit-* sur chaque réponse.
Données publiques
Les réponses externes excluent :
- userId, email et IP
- EXIF bruts et GPS EXIF des photos
Les champs radio et réseau restent exposés quand ils sont publics.
Écriture limitée
Une seule route écrit :
POST /sites/{siteId}/photosElle requiert une clé API partenaire avec photos:write.
Modes de clés & signing HMAC
Une clé API est rattachée à un mode qui détermine ses contraintes de sécurité. Le mode est choisi à la création et garde les invariants alignés avec le type de client (SPA, backend, app native).
Site web / SPA
Pour les apps front qui appellent l'API depuis un navigateur. Le contrôle d'origine empêche un autre site de réutiliser la clé via un XHR.
Requis
allowedOrigins non-vide (config du client, niveau ApiClient)
Header Authorization: Bearer ou X-API-Key
Optionnel
— Aucun HMAC signing — protégé par CORS + Origin allowlist
Cas d'usage
App web tiers consommant les antennes pour affichage carte.
Backend serveur
Pour les VPS, dédiés et conteneurs avec IP fixe. Le contrôle par CIDR évite la réutilisation depuis un autre serveur.
Requis
allowedIpCidrs non-vide (IPv4 ou IPv6 CIDR — ex. 79.88.27.93/32 ou 2a04:800::/48)
Header Authorization: Bearer ou X-API-Key
Optionnel
— requireSignature = true → HMAC X-Sq-Signature obligatoire (recommandé sur clés sensibles)
Cas d'usage
Script ETL côté tiers qui aggrège les speedtests une fois par heure.
App native iOS / Android
Pour les apps mobiles. La clé est embarquée dans le binaire — le HMAC permet de détecter une réutilisation post-extraction et de bloquer le replay.
Requis
Header Authorization: Bearer
Header X-Sq-Signature: t=<unix>,v1=<hex> sur chaque requête
signingSecret HMAC-SHA256 (généré et affiché une seule fois à la création)
Optionnel
— allowedIpCidrs si besoin de restreindre (mais une box résidentielle change d'IP)
Cas d'usage
App Android GeoTower qui consomme /sites/[id]/photos et /speedtests/site.
Signature HMAC — détails du protocole
Obligatoire en mode MOBILE et optionnel pour SERVER+requireSignature. Le serveur valide la signature avant le check de scope, donc une signature absente ou invalide renvoie 401 SIGNATURE_REQUIRED.
Format du header
X-Sq-Signature: t=<unix-seconds>,v1=<hex-64-chars>Payload signé (4 lignes séparées par \n)
<unix-seconds>
<METHOD-uppercase>
<pathname-sans-query>
<sha256-body-hex>- •
tdoit être à ±5 minutes de l'heure serveur (UTC) — anti-rejeu - •
METHODen majuscules :GET,POST, etc. - •
pathname= path sans la query string (ex./api/external/v1/antennas) - •
sha256(body)hex 64 chars ; pour un GET sans body :e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - •
v1= HMAC-SHA256(signingSecret, payload) en hex 64 chars
Exemple d'implémentation
# Remplace les 2 valeurs
KEY="sq_live_..."
SECRET="<TON-SIGNING-SECRET>"
# Requête à signer
METHOD="GET"
PATH_="/api/external/v1/antennas"
BODY=""
# 1) Timestamp Unix
TS=$(date +%s)
# 2) SHA-256 du body (vide pour GET)
BODY_HASH=$(printf "%s" "$BODY" | shasum -a 256 | awk '{print $1}')
# 3) Payload signé : 4 lignes
PAYLOAD=$(printf "%s\n%s\n%s\n%s" "$TS" "$METHOD" "$PATH_" "$BODY_HASH")
# 4) HMAC-SHA256 hex
SIG=$(printf "%s" "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')
curl -H "Authorization: Bearer $KEY" \
-H "X-Sq-Signature: t=$TS,v1=$SIG" \
"https://signalquest.fr$PATH_?limit=1"⚠️ Pièges classiques
- • Inclure la query string dans le
pathnamesigné → signature invalide - • Utiliser un
ttrop ancien (recalcule à chaque appel) - • Confondre HMAC-SHA256 vs SHA-256 nu — c'est bien le HMAC avec le secret
- • Encoder en base64 au lieu de hex pour
v1
Marchés et sources
Les champs génériques nationalSiteCode et sourceCode sont à privilégier. anfrCode reste un alias legacy France/DROM.
| Marché | Source | Opérateurs | Filtres | Note |
|---|---|---|---|---|
FR France métropolitaine Disponible | ANFR | SFR, BOUYGUES, ORANGE, FREE, ALL | department, generation, status, bbox | Archives ANFR disponibles uniquement pour ce marché. |
DROM DROM Disponible | ANFR DROM | ORANGE, FREE_CARAIBES, DIGICEL, OUTREMER_TELECOM, SRR, TELCO_OI, ZEOP, MAORE_MOBILE, ALL | territory/department, generation, status, bbox | Utilisez territory ou department: 971, 972, 973, 974, 976. |
CA Canada Disponible | ISED spectrum data | BELL, ROGERS, TELUS, VIDEOTRON_FREEDOM, REGIONAL, ALL | province, generation, status, bbox | Les identifiants principaux sont siteKey, nationalSiteCode, licence et reference, pas ANFR. |
BE Belgique Partiel | Vlaanderen / Bruxelles Environnement | PROXIMUS_BE, CITYMESH_BE, TELENET_BASE_BE, ORANGE_BE, ALL | operator, generation, status, bbox | Seules les sources publiques machine-readable et redistribuables sont exposées. |
CH Suisse Disponible | OFCOM / BAKOM | SWISSCOM_CH, SUNRISE_CH, SALT_CH, ALL | operator, generation, status, bbox | Données issues de geo.admin.ch, avec attribution de la source. |
PT, ES, BA Pays pilotes sans inventaire public exploitable Non disponible | Autorités nationales identifiées | Voir market/operator; réponse vide si source indisponible. | operator | L API retourne sourceUnavailableReason au lieu d inventer des antennes. |
Catalogue des endpoints
15 routesVue courte pour choisir la bonne route avant d'ouvrir le détail complet, les paramètres et l'exemple JSON.
/antennasLister les antennes
Retourne une liste paginee d antennes multi-pays avec filtres radio et geographiques.
/antennas/exportExporter un snapshot antennes
Retourne un snapshot cacheable JSON ou GeoJSON pour ingestion complete ou carte tierce.
/antennas/searchRechercher une antenne
Recherche multi-criteres par identifiant source, site, commune ou texte libre.
/antennas/lookup-by-radioTrouver une antenne par identifiant radio
Résout un eNB, gNB, PCI, Cell ID ou CI/NCI vers un ou plusieurs sites. Canada utilise les shards spectrum; les autres marchés utilisent les validations SignalQuest disponibles.
/antennas/{id}Detail d une antenne
Retourne le detail radio et support d une antenne. `{id}` accepte le site id, siteKey ou code source legacy.
/sites/{siteId}Resume d un site
Retourne la synthese d adresse et de partage reseau pour un site.
/sites/{siteId}/photosPhotos d un site
Retourne les photos publiques approuvees associees a un site donne.
/sites/{siteId}/photosEnvoyer une photo sur un site
Accepte un upload multipart et publie la photo immediatement au nom du client API.
/stats/departmentsStats par departement FR/DROM
Retourne une vue agregee par departement/territoire sur le parc radio FR/DROM.
/sites-hsSites indisponibles
Expose les interruptions publiees dans les flux incidents publics disponibles.
/anfr/archivesLister les archives ANFR
Retourne les dates de snapshot disponibles et la date courante active.
/anfr/archive/{date}Lire une archive ANFR
Retourne le snapshot brut de la date demandee.
/speedtestsLister les speedtests publics
Retourne les speedtests publics visibles sur la carte, avec filtres marche, operateur et bbox.
/speedtests/siteSpeedtests d une antenne
Retourne les speedtests publics associes a une antenne via siteId, code source legacy, eNB ou gNB.
/coverage/pointsPoints de couverture publics
Retourne les points de couverture publics, sans userId, deviceId, email, IP ni traces privees.
Codes d'erreur
Chaque réponse d'erreur contient un champ code et un X-Request-Id pour le diagnostic.
Une app cliente peut afficher directement le message du champ error, adapter son UI selon le code, et conserver le requestId pour le support. En cas de 429, elle peut aussi lire le header Retry-After.
| HTTP | Code | Description |
|---|---|---|
| 401 | API_KEY_REQUIRED | Clé API absente pour une intégration tierce. |
| 401 | INVALID_API_KEY | Clé absente, invalide, expirée ou révoquée. |
| 401 | SIGNATURE_REQUIRED | Header X-Sq-Signature absent ou invalide (modes MOBILE et SERVER+signing). |
| 401 | FIRST_PARTY_TOKEN_REQUIRED | Attestation Play Integrity requise — mettez à jour l'application. |
| 403 | IP_NOT_ALLOWED | Adresse IP hors de la liste CIDR autorisée (IPv4 ou IPv6). |
| 403 | ORIGIN_NOT_ALLOWED | Origin HTTP non autorisée pour ce client. |
| 403 | INSUFFICIENT_SCOPE | La clé n'a pas le scope requis pour cet endpoint. |
| 410 | Deprecation+Sunset | Endpoint historique arrêté (ex. /api/public/antennas après 2026-08-01). Voir successor-version dans le header Link. |
| 400 | INVALID_BBOX | Coordonnées bbox invalides ou incomplètes. |
| 400 | INVALID_OPERATOR | Opérateur non compatible avec le marché demandé. |
| 400 | MISSING_RADIO_VALUE | Lookup radio: value, eNB, gNB, PCI, Cell ID ou CI absent. |
| 400 | MISSING_ANTENNA_IDENTIFIER | Speedtests site: aucun identifiant d antenne fourni. |
| 400 | INVALID_DATE | Archive ANFR: date invalide, format attendu YYYY-MM-DD. |
| 400 | MISSING_FILE | Upload photo: le champ multipart `file` est absent. |
| 400 | UNSUPPORTED_IMAGE_FORMAT | Upload photo: format image non supporté. |
| 400 | IMAGE_TOO_LARGE | Upload photo: l'image dépasse 20 MB avant traitement. |
| 400 | HEIC_CONVERSION_FAILED | Upload photo: impossible de convertir le fichier HEIC/HEIF. |
| 429 | RATE_LIMITED | Quota minute ou journalier dépassé. Lire Retry-After et X-RateLimit-*. |
| 404 | ANTENNA_NOT_FOUND | L'identifiant de l'antenne ne correspond à aucune ressource. |
| 404 | SITE_NOT_FOUND | Site introuvable ou sans photo publique visible selon la route. |
| 404 | ARCHIVE_NOT_FOUND | Archive ANFR inexistante pour la date demandée. |
| 500 | PHOTO_UPLOAD_FAILED | Upload photo impossible hors erreurs de format/taille. |
| 500 | EXTERNAL_*_FAILED | Erreur interne sur un endpoint externe; contactez le support avec X-Request-Id. |
Exemple de réponse d'erreur
{
"error": "Format d image non supporte",
"code": "UNSUPPORTED_IMAGE_FORMAT",
"requestId": "req_example_01"
}/antennasantennas:readLister les antennes
Retourne une liste paginee d antennes multi-pays avec filtres radio et geographiques.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/antennas?market=FR&operator=SFR&limit=2"Paramètres
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorOperateur compatible avec le market; utilisez ALL pour un snapshot multi-operateurs.
departmentFR/DROM uniquement: code departement ou territoire, par ex. 075, 013, 972.
territoryDROM uniquement: alias explicite de department pour 971, 972, 973, 974, 976.
provinceCanada: code province, par ex. QC, ON, BC.
generation3G, 4G ou 5G.
statusFiltre libre sur le statut.
qRecherche texte libre.
north/south/east/westBounding box recommandee pour cartes. Active une limite max de 5000 resultats.
limit1 a 1000 par defaut, 1 a 5000 avec bbox valide.
offsetPagination offset.
`anfrCode` reste expose pour compatibilite France/DROM. Utilisez `nationalSiteCode` ou `sourceCode` pour un code multi-pays.
Pour charger toutes les antennes d un site tiers, preferez /antennas/export puis des requetes bbox pour les rafraichissements.
Exemple de réponse
{
"data": [
{
"id": "23444",
"anfrCode": "0032700020",
"nationalSiteCode": "0032700020",
"sourceCode": "0032700020",
"siteKey": null,
"market": "FR",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
},
"coordinates": {
"lat": 46.177499999999995,
"lng": 3.3741666666666665
},
"location": {
"department": "003",
"provinceCode": null,
"commune": null,
"postalCode": "03110",
"address": "rte de vendat",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
}
},
"status": null,
"technologies": [
"5G NR 2100",
"5G NR 3500",
"GSM 900",
"LTE 1800",
"LTE 2100",
"LTE 2600",
"LTE 700",
"LTE 800",
"UMTS 900"
],
"operators": [
"SFR",
"BOUYGUES"
],
"sharedNetwork": true,
"updatedAt": null,
"source": null
},
{
"id": "23522",
"anfrCode": "0042700027",
"nationalSiteCode": "0042700027",
"sourceCode": "0042700027",
"siteKey": null,
"market": "FR",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "004",
"label": "004"
},
"coordinates": {
"lat": 43.82027777777778,
"lng": 6.0825
},
"location": {
"department": "004",
"provinceCode": null,
"commune": null,
"postalCode": "04500",
"address": "chem du relais",
"administrativeArea": {
"type": "department",
"code": "004",
"label": "004"
}
},
"status": null,
"technologies": [
"5G NR 2100",
"5G NR 3500",
"GSM 900",
"LTE 1800",
"LTE 2100",
"LTE 2600",
"LTE 700",
"LTE 800",
"UMTS 900"
],
"operators": [
"SFR",
"BOUYGUES"
],
"sharedNetwork": true,
"updatedAt": null,
"source": null
}
],
"meta": {
"total": 32369,
"totalMatched": 32369,
"returned": 2,
"limit": 2,
"offset": 0,
"hasMore": true,
"nextOffset": 2,
"market": "FR",
"operator": "SFR",
"bbox": null,
"sourceDate": null,
"sourceLabel": "ANFR",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceUnavailableReason": null
},
"requestId": "req_example_01"
}/antennas/exportantennas:readspécialiséExporter un snapshot antennes
Retourne un snapshot cacheable JSON ou GeoJSON pour ingestion complete ou carte tierce.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/antennas/export?market=FR&operator=SFR&format=geojson"Paramètres
marketValeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorOperateur compatible avec le marché, ou ALL.
formatjson par defaut, ou geojson.
departmentFR/DROM uniquement.
territoryDROM uniquement: 971, 972, 973, 974, 976.
provinceCanada uniquement.
generation3G, 4G ou 5G.
statusFiltre libre sur le statut.
Endpoint recommande pour afficher toutes les antennes sur une carte sans paginer agressivement l API dynamique.
Les pays sans inventaire public exploitable retournent un snapshot vide avec `sourceUnavailableReason`.
Exemple de réponse
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "23444",
"geometry": {
"type": "Point",
"coordinates": [
3.3741666666666665,
46.177499999999995
]
},
"properties": {
"id": "23444",
"market": "FR",
"countryCode": "FR",
"nationalSiteCode": "0032700020",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"operators": [
"SFR",
"BOUYGUES"
]
}
}
],
"meta": {
"export": true,
"format": "geojson",
"total": 32369,
"totalMatched": 32369,
"returned": 2,
"market": "FR",
"operator": "SFR",
"sourceLabel": "ANFR",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceUnavailableReason": null
},
"requestId": "req_example_01"
}/antennas/searchantennas:readRechercher une antenne
Recherche multi-criteres par identifiant source, site, commune ou texte libre.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/antennas/search?operator=SFR&supId=23444&limit=2"Paramètres
qTexte libre global.
supIdIdentifiant support / site.
anfrAlias legacy France/DROM du code source.
nationalSiteCodeCode national/source multi-pays.
sourceCodeCode brut expose par la source du pays.
communeNom de commune.
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
departmentFR/DROM uniquement: code departement ou territoire.
territoryDROM uniquement: alias de department.
provinceCanada: code province, par ex. QC, ON, BC.
generation3G, 4G ou 5G.
statusFiltre libre sur le statut.
operatorOperateur compatible avec le market.
limit1 a 100.
offsetPagination offset.
Exemple de réponse
{
"data": [
{
"id": "23444",
"anfrCode": "0032700020",
"nationalSiteCode": "0032700020",
"sourceCode": "0032700020",
"siteKey": null,
"market": "FR",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
},
"coordinates": {
"lat": 46.177499999999995,
"lng": 3.3741666666666665
},
"location": {
"department": "003",
"provinceCode": null,
"commune": null,
"postalCode": "03110",
"address": "rte de vendat",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
}
},
"status": null,
"technologies": [
"5G NR 2100",
"5G NR 3500",
"GSM 900",
"LTE 1800",
"LTE 2100",
"LTE 2600",
"LTE 700",
"LTE 800",
"UMTS 900"
],
"operators": [
"SFR",
"BOUYGUES"
],
"sharedNetwork": true,
"updatedAt": null,
"source": null
},
{
"id": "623444",
"anfrCode": "0762700271",
"nationalSiteCode": "0762700271",
"sourceCode": "0762700271",
"siteKey": null,
"market": "FR",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "076",
"label": "076"
},
"coordinates": {
"lat": 49.39277777777778,
"lng": 1.1419444444444444
},
"location": {
"department": "076",
"provinceCode": null,
"commune": null,
"postalCode": "76240",
"address": "Proche Route de Mesnil Esnard",
"administrativeArea": {
"type": "department",
"code": "076",
"label": "076"
}
},
"status": null,
"technologies": [
"GSM 900",
"LTE 1800",
"LTE 2100",
"LTE 2600",
"LTE 800",
"UMTS 900"
],
"operators": [
"SFR"
],
"sharedNetwork": false,
"updatedAt": null,
"source": null
}
],
"meta": {
"total": 3,
"limit": 2,
"offset": 0,
"hasMore": true,
"market": "FR",
"operator": "SFR"
},
"requestId": "req_example_01"
}/antennas/lookup-by-radioantennas:readTrouver une antenne par identifiant radio
Résout un eNB, gNB, PCI, Cell ID ou CI/NCI vers un ou plusieurs sites. Canada utilise les shards spectrum; les autres marchés utilisent les validations SignalQuest disponibles.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/antennas/lookup-by-radio?market=CA&type=enb&value=23444&operator=ROGERS"Paramètres
valuerequisValeur radio a rechercher. Alias acceptes: enb, gnb, pci, cellId, ci.
typeauto, enb, gnb, pci, cellid ou ci.
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorFiltre operateur compatible avec le marché.
limit1 a 100.
Exemple de réponse
{
"data": [
{
"site": {
"id": "C0091",
"anfrCode": "CA-ROGERS-C0091",
"nationalSiteCode": "C0091",
"sourceCode": "C0091",
"siteKey": "ca-site-c0091",
"market": "CA",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
},
"coordinates": {
"lat": 46.177499999999995,
"lng": 3.3741666666666665
},
"location": {
"department": null,
"provinceCode": "ON",
"commune": "Toronto",
"postalCode": "M5V",
"address": "rte de vendat",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
}
},
"status": null,
"technologies": [
"5G NR 2100",
"5G NR 3500",
"LTE 1800",
"LTE 700",
"UMTS 900"
],
"operators": [
"ROGERS"
],
"sharedNetwork": true,
"updatedAt": null,
"source": null
},
"matches": [
{
"type": "enb",
"value": "23444"
}
]
}
],
"meta": {
"market": "CA",
"operator": "ROGERS",
"type": "enb",
"value": "23444",
"returned": 1
},
"requestId": "req_example_01"
}/antennas/{id}antennas:readDetail d une antenne
Retourne le detail radio et support d une antenne. `{id}` accepte le site id, siteKey ou code source legacy.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/antennas/23444?operator=SFR"Paramètres
idrequissup_id, siteKey, nationalSiteCode ou anfrCode legacy.
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorFiltre operateur compatible avec le marché.
Exemple de réponse
{
"data": {
"id": "23444",
"anfrCode": "0032700020",
"nationalSiteCode": "0032700020",
"sourceCode": "0032700020",
"siteKey": null,
"market": "FR",
"countryCode": "FR",
"marketLabel": "France",
"sourceAuthority": "Agence nationale des fréquences (ANFR)",
"sourceLabel": "ANFR",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
},
"coordinates": {
"lat": 46.177499999999995,
"lng": 3.3741666666666665
},
"location": {
"department": "003",
"provinceCode": null,
"commune": null,
"postalCode": "03110",
"address": "rte de vendat",
"administrativeArea": {
"type": "department",
"code": "003",
"label": "003"
}
},
"status": null,
"technologies": [
"5G NR 2100",
"5G NR 3500",
"GSM 900",
"LTE 1800",
"LTE 2100",
"LTE 2600",
"LTE 700",
"LTE 800",
"UMTS 900"
],
"operators": [
"SFR",
"BOUYGUES"
],
"sharedNetwork": true,
"updatedAt": null,
"source": null,
"support": {
"height": null,
"antennaHeight": null,
"supportInfo": {
"hauteur": "20",
"nature": "23",
"type_proprietaire": "72"
}
},
"radio": {
"generation": "5G",
"azimuts": [
90
],
"hasFh": false,
"latestDeploymentDate": "2023-01-31",
"technologiesInProject": {
"lte": [],
"5g": []
},
"physicalIds": [],
"cellIds": [],
"txFrequencies": [],
"rxFrequencies": []
},
"dates": {
"deployment": null,
"lastUpdated": null,
"implementation": null,
"commissioning": null
}
},
"requestId": "req_example_01"
}/sites/{siteId}antennas:readResume d un site
Retourne la synthese d adresse et de partage reseau pour un site.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/sites/23444"Paramètres
siteIdrequisIdentifiant site/source. Les codes ANFR restent acceptes pour FR/DROM.
Exemple de réponse
{
"data": {
"siteId": "23444",
"anfrCode": "0032290175",
"operators": [
"ORANGE"
],
"operatorTag": "ORANGE",
"isSharedSite": false,
"isCrozon": false,
"crozonLeader": null,
"addressLine1": "rte de vendat",
"addressLine2": null,
"addressLine3": null,
"lieuDit": "LE BOUCHEREAUD",
"postalCode": "03110",
"commune": "Saint-Rémy-en-Rollat",
"fullAddress": "rte de vendat, LE BOUCHEREAUD, 03110, Saint-Rémy-en-Rollat",
"latitude": 46.177499999999995,
"longitude": 3.3741666666666665
},
"requestId": "req_example_01"
}/sites/{siteId}/photosphotos:readPhotos d un site
Retourne les photos publiques approuvees associees a un site donne.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/sites/23444/photos?limit=2"Paramètres
siteIdrequisIdentifiant site/source. Les codes ANFR restent acceptes pour FR/DROM.
operatorOptionnel: operateur compatible avec les photos du site, ou ALL.
limit1 a 100.
offsetPagination offset.
La reponse ne contient ni email ni userId. Seules les photos approuvees sont exposees.
publicMetadata contient uniquement les champs EXIF publics autorises: modele appareil, distance EXIF-site, date de prise de vue au jour pres, mois de prise de vue, direction GPSImgDirection et orientation. Les EXIF bruts, l heure exacte et les coordonnees GPS EXIF ne sont jamais exposes.
Exemple de réponse
{
"data": [
{
"id": "cmphotoexample1",
"siteId": "23444",
"imageUrl": "https://cdn.example.com/photos/23444_main.webp",
"thumbnailUrl": "https://cdn.example.com/photos/23444_thumb.webp",
"ogImageUrl": "https://cdn.example.com/photos/23444_og.webp",
"caption": "Vue du pylone principal",
"authorName": "GeoTower",
"likeCount": 12,
"commentCount": 3,
"operator": "SFR",
"uploadedAt": "2026-02-28T10:15:00.000Z",
"publicMetadata": {
"cameraModel": "Pixel 10",
"distanceToSiteMeters": 18.4,
"takenDate": "2026-02-28",
"takenDateLabel": "28 fevrier 2026",
"takenMonth": "2026-02",
"takenMonthLabel": "fevrier 2026",
"gpsImgDirectionDegrees": 84,
"orientationDegrees": 90
}
}
],
"meta": {
"total": 1,
"limit": 2,
"offset": 0,
"hasMore": false,
"site": {
"siteId": "23444",
"anfrCode": "0032290175",
"operators": [
"ORANGE"
],
"operatorTag": "ORANGE",
"fullAddress": "rte de vendat, LE BOUCHEREAUD, 03110, Saint-Rémy-en-Rollat",
"commune": "Saint-Rémy-en-Rollat",
"postalCode": "03110"
}
},
"requestId": "req_example_01"
}/sites/{siteId}/photosphotos:writeécritureEnvoyer une photo sur un site
Accepte un upload multipart et publie la photo immediatement au nom du client API.
Requête exemple
curl -X POST \
-H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
-F "file=@photo.jpg" \
-F "description=Vue facade nord" \
-F "operator=SFR" \
"https://signalquest.fr/api/external/v1/sites/23444/photos"Paramètres
siteIdrequisIdentifiant site/source dans le chemin. Les codes ANFR restent acceptes pour FR/DROM.
filerequisFichier image a envoyer en multipart/form-data.
descriptionLegende optionnelle de la photo.
operatorOptionnel: operateur compatible avec le site.
anfrCodeOptionnel legacy FR/DROM. Pour les nouveaux clients, utilisez l identifiant site/source du chemin.
nationalSiteCodeAlias generique accepte en upload, conserve pour compatibilite client.
sourceCodeAlias generique accepte en upload, conserve pour compatibilite client.
exifMetadataOptionnel: JSON de metadonnees EXIF deja lues cote client/partenaire. Alias accepte: clientExifMetadata. Le serveur relit aussi les EXIF du fichier quand ils sont presents.
Le nom affiche cote photo est celui du client API configure dans l admin.
Les photos envoyees par cette route sont en approved=true par defaut.
Formats acceptes: JPEG, JPG, PNG, WebP, GIF, BMP, TIFF, TIF, HEIC, HEIF, AVIF, SVG et plus largement image/*.
Taille maximale avant traitement: 20 MB.
Toutes les images sont recompressees en WebP: image principale max 1920x1920, cible sous 2 MB; thumbnail 400x400 WebP; image Open Graph 1200x630 WebP.
Les metadonnees EXIF sensibles sont nettoyees des images publiques. La reponse expose seulement publicMetadata, jamais les EXIF bruts ni les coordonnees GPS EXIF.
Exemple de réponse
{
"data": {
"id": "cmuploadexample1",
"siteId": "23444",
"enb": "0032700020",
"imageUrl": "https://cdn.example.com/photos/23444_uploaded.webp",
"thumbnailUrl": "https://cdn.example.com/photos/23444_uploaded_thumb.webp",
"ogImageUrl": "https://cdn.example.com/photos/23444_uploaded_og.webp",
"caption": "Vue facade nord",
"approved": true,
"operator": "SFR",
"authorName": "GeoTower",
"uploadedAt": "2026-03-13T11:25:00.000Z",
"publicMetadata": {
"cameraModel": "Pixel 10",
"distanceToSiteMeters": 22.7,
"takenDate": "2026-03-13",
"takenDateLabel": "13 mars 2026",
"takenMonth": "2026-03",
"takenMonthLabel": "mars 2026",
"gpsImgDirectionDegrees": 76,
"orientationDegrees": 0
}
},
"meta": {
"source": "api_client",
"clientId": "cmclientexample1",
"clientName": "GeoTower"
},
"requestId": "req_example_01"
}/stats/departmentsstats:readspécialiséStats par departement FR/DROM
Retourne une vue agregee par departement/territoire sur le parc radio FR/DROM.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/stats/departments?operator=SFR"Paramètres
operatorFR/DROM uniquement: SFR, BOUYGUES, ORANGE, FREE ou ALL selon la source disponible.
Exemple de réponse
{
"data": [
{
"code": "075",
"name": "Departement 075",
"total": 997,
"fiveG3500": 461,
"fiveG2100": 23,
"fiveG": 484,
"fourG": 917,
"threeG": 834,
"enService": 0,
"techOp": 0,
"projet": 0
},
{
"code": "013",
"name": "Departement 013",
"total": 990,
"fiveG3500": 561,
"fiveG2100": 186,
"fiveG": 747,
"fourG": 946,
"threeG": 898,
"enService": 0,
"techOp": 0,
"projet": 0
},
{
"code": "059",
"name": "Departement 059",
"total": 882,
"fiveG3500": 397,
"fiveG2100": 225,
"fiveG": 622,
"fourG": 824,
"threeG": 784,
"enService": 0,
"techOp": 0,
"projet": 0
}
],
"meta": {
"total": 32369,
"totalDepartments": 96,
"operator": "SFR"
},
"requestId": "req_example_01"
}/sites-hsnetwork-status:readSites indisponibles
Expose les interruptions publiees dans les flux incidents publics disponibles.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/sites-hs?market=FR&operator=SFR"Paramètres
marketFR par defaut. Flux disponibles selon le marche: FR, DROM ou CA; les autres marches retournent une reponse vide explicite.
operatorOperateur compatible avec le flux incident du marche, ou ALL quand la source le permet.
territoryDROM uniquement: territoire/departement.
Les colonnes de `data` proviennent directement du CSV public de l operateur.
Exemple de réponse
{
"data": [
{
"sourceId": "fr-sfr-incidents",
"market": "FR",
"operator": "SFR",
"territory": null,
"code_site_op": "010036",
"Lat": 45.824166666667,
"Lon": 4.9483333333333,
"commune": "MIRIBEL",
"departement": "Ain",
"code_insee": "01249",
"services": {
"voice2g": "OK",
"voice3g": "HS",
"voice4g": "HS",
"data3g": "HS",
"data4g": "HS",
"data5g": "HS",
"voice": "DE",
"data": "HS"
},
"raison": "MAINT",
"debut": "2026-05-26T07:46:05.000Z",
"fin_prev": "2026-05-30T20:00:00.000Z",
"raw": {
"code_site_op": "010036",
"Antenne relais gérée par SFR": "OUI",
"region": "Auvergne-Rhône-Alpes",
"departement": "Ain",
"commune": "MIRIBEL",
"code_insee": "01249",
"Lat": "45.824166666667",
"Lon": "4.9483333333333",
"2Gvoix": "OK",
"3Gvoix": "HS",
"4Gvoix": "HS",
"3Gdata": "HS",
"4Gdata": "HS",
"5Gdata": "HS",
"voix": "DE",
"data": "HS",
"raison": "MAINT",
"detail": "",
"debut": "2026-05-26 07:46:05",
"fin_prev": "2026-05-30 20:00:00"
},
"region": "Auvergne-Rhône-Alpes",
"managedBySfr": true,
"voixSms": "DE",
"voix": "DE",
"data": "HS",
"Antenne relais gérée par SFR": "OUI",
"2Gvoix": "OK",
"3Gvoix": "HS",
"4Gvoix": "HS",
"3Gdata": "HS",
"4Gdata": "HS",
"5Gdata": "HS"
},
{
"sourceId": "fr-sfr-incidents",
"market": "FR",
"operator": "SFR",
"territory": null,
"code_site_op": "020231",
"Lat": 49.427222222222,
"Lon": 3.9591666666667,
"commune": "CONDE SUR SUIPPE",
"departement": "Aisne",
"code_insee": "02211",
"services": {
"voice2g": "OK",
"voice3g": "HS",
"voice4g": "HS",
"data3g": "HS",
"data4g": "HS",
"data5g": "NE",
"voice": "DE",
"data": "HS"
},
"raison": "INT",
"debut": "2026-05-11T11:31:48.000Z",
"raw": {
"code_site_op": "020231",
"Antenne relais gérée par SFR": "OUI",
"region": "Hauts-de-France",
"departement": "Aisne",
"commune": "CONDE SUR SUIPPE",
"code_insee": "02211",
"Lat": "49.427222222222",
"Lon": "3.9591666666667",
"2Gvoix": "OK",
"3Gvoix": "HS",
"4Gvoix": "HS",
"3Gdata": "HS",
"4Gdata": "HS",
"5Gdata": "NE",
"voix": "DE",
"data": "HS",
"raison": "INT",
"detail": "",
"debut": "2026-05-11 11:31:48",
"fin_prev": ""
},
"region": "Hauts-de-France",
"managedBySfr": true,
"voixSms": "DE",
"voix": "DE",
"data": "HS",
"Antenne relais gérée par SFR": "OUI",
"2Gvoix": "OK",
"3Gvoix": "HS",
"4Gvoix": "HS",
"3Gdata": "HS",
"4Gdata": "HS",
"5Gdata": "NE"
}
],
"meta": {
"operator": "SFR",
"cached": false,
"count": 228,
"lastUpdate": "2026-05-27T00:46:15.193Z"
},
"requestId": "req_example_01"
}/anfr/archivesanfr:readspécialiséLister les archives ANFR
Retourne les dates de snapshot disponibles et la date courante active.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/anfr/archives"FR/DROM only: ces archives sont liees a la source ANFR et ne concernent pas les autres marches.
Exemple de réponse
{
"data": [
"2026-05-14",
"2026-05-07",
"2026-04-23"
],
"meta": {
"current": "2026-05-21"
},
"requestId": "req_example_01"
}/anfr/archive/{date}anfr:readspécialiséLire une archive ANFR
Retourne le snapshot brut de la date demandee.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/anfr/archive/2026-05-14"Paramètres
daterequisDate au format YYYY-MM-DD.
La reponse est un objet brut indexe par identifiant de site, pas une liste plate.
FR/DROM only: ces archives sont liees a la source ANFR.
Exemple de réponse
{
"data": {
"72975": {
"info": {
"sup_id": "72975",
"coordonnees": "43.14111111111111 , 2.8866666666666667",
"lat": "43.14111111111111",
"lon": "2.8866666666666667",
"city": "BIZANET"
},
"antennas": [
{
"id": "403560",
"sup_id": "72975",
"coordonnees": "43.14111111111111 , 2.8866666666666667",
"coord": "43°8'28''N 2°53'12''E",
"lat": "43.14111111111111",
"lon": "2.8866666666666667",
"city": "BIZANET",
"adm_lb_nom": "ORANGE",
"emr_lb_systeme": "LTE 700",
"statut": "En service",
"emr_dt": "2026-05-11",
"generation": "4G",
"date_maj": "",
"sta_nm_anfr": "0112290056",
"nat_id": "23",
"sup_nm_haut": "24",
"tpo_id": "74",
"adr_lb_lieu": "La Bade",
"adr_lb_add1": "Domaine de St-Julien",
"adr_lb_add2": "",
"adr_lb_add3": "",
"adr_nm_cp": "11200",
"sta_nm_dpt": "011",
"code_insee": "11040",
"com_cd_insee": "11040",
"type": "activated"
}
]
}
},
"requestId": "req_example_01"
}/speedtestsspeedtest:readspécialiséLister les speedtests publics
Retourne les speedtests publics visibles sur la carte, avec filtres marche, operateur et bbox.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/speedtests?market=CA&operator=ROGERS&north=46&south=45&east=-73&west=-74&limit=100"Paramètres
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorFiltre operateur compatible avec le marché.
north/south/east/westBounding box optionnelle.
daysFenetre temporelle en jours, max 365.
limit1 a 1000.
Exemple de réponse
{
"data": [
{
"id": "speedtest_public_example",
"timestamp": "2026-04-07T12:00:00.000Z",
"coordinates": {
"lat": 45.5017,
"lng": -73.5673
},
"downloadSpeed": 420.5,
"averageSpeed": 385.2,
"maxSpeed": 450.1,
"uploadSpeed": 58.4,
"ping": 18,
"mcc": 302,
"mnc": 720,
"mobileOperator": "Rogers",
"networkType": "CELLULAR",
"connectionType": "5G",
"deviceType": "Android",
"radio": {
"enb": "23444",
"gnb": null,
"cellId": "6001664",
"pci": 123,
"rsrp": -88,
"rsrq": -9,
"snr": 22
}
}
],
"meta": {
"limit": 100,
"returned": 1,
"market": "CA",
"operator": "ROGERS"
},
"requestId": "req_example_01"
}/speedtests/sitespeedtest:readspécialiséSpeedtests d une antenne
Retourne les speedtests publics associes a une antenne via siteId, code source legacy, eNB ou gNB.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/speedtests/site?siteId=23444&operator=ORANGE&bestOnly=true"Paramètres
siteIdIdentifiant site/support.
anfrCodeAlias legacy FR/DROM du code source.
nationalSiteCodeAlias generique accepte pour le code source du site.
sourceCodeAlias generique accepte pour le code brut de la source.
enbeNB ou gNB direct.
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorFiltre operateur compatible avec le marché. Optionnel, ALL par defaut.
mncFiltre exact Mobile Network Code optionnel. Exemple: 1 pour Orange FR.
mccFiltre exact Mobile Country Code optionnel. Infere pour FR/CA quand mnc est fourni seul.
bestOnlytrue pour ne retourner que le meilleur speedtest.
limit1 a 100.
offsetPagination offset, ignore si bestOnly=true.
Au moins un identifiant parmi siteId, anfrCode, nationalSiteCode, sourceCode ou enb est requis.
Meilleur speedtest: averageSpeed le plus élevé, puis downloadSpeed/MAX public, puis timestamp le plus récent.
Exemple de réponse
{
"data": [
{
"id": "speedtest_public_example",
"timestamp": "2026-04-07T12:00:00.000Z",
"coordinates": {
"lat": 45.5017,
"lng": -73.5673
},
"downloadSpeed": 420.5,
"averageSpeed": 385.2,
"maxSpeed": 450.1,
"uploadSpeed": 58.4,
"ping": 18,
"mcc": 302,
"mnc": 720,
"mobileOperator": "Rogers",
"networkType": "CELLULAR",
"connectionType": "5G",
"deviceType": "Android",
"radio": {
"enb": "23444",
"gnb": null,
"cellId": "6001664",
"pci": 123,
"rsrp": -88,
"rsrq": -9,
"snr": 22
}
}
],
"meta": {
"total": 1,
"limit": 1,
"offset": 0,
"bestOnly": true,
"market": "FR",
"operator": "ALL"
},
"requestId": "req_example_01"
}/coverage/pointscoverage:readspécialiséPoints de couverture publics
Retourne les points de couverture publics, sans userId, deviceId, email, IP ni traces privees.
Requête exemple
curl -s -H "Authorization: Bearer sq_live_VOTRE_CLE_API" \
"https://signalquest.fr/api/external/v1/coverage/points?market=CA&operator=ROGERS&technology=5G&limit=1000"Paramètres
marketFR par defaut. Valeurs: FR, DROM, CA, BE, CH, PT, ES, BA.
operatorFiltre operateur compatible avec le marché.
technologyFiltre optionnel: 2G, 3G, 4G, 5G, LTE, NR...
north/south/east/westBounding box optionnelle.
daysFenetre temporelle en jours, max 365.
limit1 a 5000.
Exemple de réponse
{
"data": [
{
"id": "coverage_public_example",
"timestamp": "2026-04-07T12:00:00.000Z",
"coordinates": {
"lat": 45.5017,
"lng": -73.5673
},
"signalStrength": -88,
"rsrq": -9,
"snr": 22,
"technology": "5G",
"networkType": "NR",
"mobileOperator": "Rogers",
"mcc": 302,
"mnc": 720,
"radio": {
"enb": "23444",
"gnb": null,
"cellId": "6001664",
"pci": 123
}
}
],
"meta": {
"limit": 1000,
"returned": 1,
"market": "CA",
"operator": "ROGERS",
"technology": "5G"
},
"requestId": "req_example_01"
}Serveur MCP — Model Context Protocol
Streamable HTTP · 28 outilsPermet à Claude et ChatGPT de lire les données SignalQuest en langage naturel — sites, archives ANFR, pannes, antennes prévisionnelles, photos et speedtests. Le serveur expose aussi les outils search et fetch pour la compatibilité ChatGPT deep research / company knowledge. Deux modes d'auth : clé API Bearer pour Claude Code et les backends, OAuth 2.1 + PKCE pour Claude Web et ChatGPT.
Endpoint MCP
https://signalquest.fr/api/mcpAuth (Code / API)
Bearer sq_live_... · scope internal:ai
Auth (Web / ChatGPT)
OAuth 2.1 + PKCE — flux automatique
- 1Ouvrir claude.ai → Settings → Integrations (ou icône puzzle).
- 2Cliquer Add integration, entrer l'URL :
https://signalquest.fr/api/mcp - 3Laisser OAuth Client ID et Client Secret vides (détection automatique). Si Claude demande un Client ID, entrer
signalquest-claude-public. - 4Cliquer Connect → se connecter à SignalQuest → Autoriser.
- 5L'intégration apparaît comme connectée. Demande directement à Claude par exemple : "Quelles antennes SFR sont en panne près de Lyon ?"
28 outils — tous read-only
signalquest_capabilitiessearchfetchlocation_geocodeanfr_searchanfr_list_archivesanfr_list_snapshot_sitesanfr_get_site_snapshotanfr_compare_site_datesanfr_get_site_historysites_searchsites_find_by_radiosites_getsites_get_validationssites_get_photossites_nearbysites_nearby_from_placesites_nearby_from_sitesites_hs_listsites_hs_nearbysites_hs_nearby_from_placesites_planned_nearbysites_planned_nearby_from_placesites_get_speedtestsspeedtests_nearbyspeedtests_nearby_from_placespeedtests_area_summaryspeedtests_area_summary_from_placeMigration
Les routes /api/public/antennas* sont dépréciées depuis le 10 mai 2026. Migrez vers /api/external/v1/antennas.
Base URL : https://signalquest.fr/api/external/v1