MDS
GPRviewer API

API Pointé IA

L'API Pointé IA détecte automatiquement les armatures (fers à béton) dans vos fichiers radar GPR. Envoyez un fichier, recevez la position (m) et la profondeur (cm) de chaque armature, en quelques secondes.

Le même modèle que le Pointé IA de GPRviewer, entraîné et validé par les experts MDS.

Sans abonnement · Facturation au mètre de profil · Intégration en quelques lignes

Radargramme d'une dalle en béton armé
Extrait de 1,2 m · calcul en moins d'une seconde

Comment ça marche

1

Créez votre clé

Créez votre compte gratuit dans votre espace API, puis une clé API en un clic. Déjà utilisateur de GPRviewer ? Le même compte fonctionne.

2

Envoyez le fichier

Un simple POST avec le fichier radar brut, depuis votre logiciel, un script ou la ligne de commande.

3

Recevez le JSON

La liste des armatures détectées, avec pour chacune la distance, la profondeur, le temps aller-retour et un indice de confiance.

Formats acceptés

  • DZT GSSI (.dzt)SIR 3000 / 4000, StructureScan Mini / XT / LXT, Flex NX, UtilityScan DF, Mini LT, NX25 / NX15
  • Compass (.nx)
  • SEG-Y (.sgy)

Fichiers jusqu'à 150 Mo.

Pour un fichier à deux voies, la voie 1 est analysée.

Performances

Chaque profil est traité en quelques secondes.

Le modèle est réentraîné régulièrement sur des pointés validés par nos experts.

Les résultats sont une aide à l'interprétation et doivent être vérifiés par un opérateur qualifié.

Tarifs

Crédits prépayés, sans abonnement. Facturation au mètre de profil traité, arrondi au mètre supérieur, avec un minimum de 1 m par fichier. Prix HT.

Essai offert 200 m à la création de votre première clé
Découverte 1 000 m 50 € HT soit 0,050 € / m
Volume 25 000 m 750 € HT soit 0,030 € / m

Documentation

API REST, réponses en JSON, exclusivement en HTTPS.

Exemple réel, sur le profil affiché

            

URL de base

https://gprviewer.com/api

Les chemins ci-dessous s'ajoutent à cette base : POST /v1/rebars correspond à https://gprviewer.com/api/v1/rebars.

Authentification

Transmettez votre clé dans l'en-tête Authorization: Bearer gpv_live_… ou, à défaut, dans X-API-Key: gpv_live_….

Authorization: Bearer gpv_live_xxxxxxxxxxxxxxxxxxxxxxxx
La clé n'est affichée qu'une seule fois, à sa création : conservez-la en lieu sûr. Vous pouvez la révoquer à tout moment depuis votre espace client. Ne la placez jamais dans du code exécuté chez vos utilisateurs (navigateur, application distribuée).

Analyser un fichier

POST/v1/rebars?filename=SCAN_001.DZT[&epsr=6.5][&wait=30]

Corps de la requête : le fichier brut (Content-Type: application/octet-stream), ou un envoi multipart/form-data avec un champ file.

ParamètreDescription
filenameObligatoire. Nom du fichier, extension comprise (ou en-tête X-File-Name) : l'extension détermine le format.
epsrFacultatif, de 1 à 81. Permittivité relative utilisée pour la conversion temps → profondeur. À défaut, celle enregistrée dans le fichier.
waitFacultatif, de 0 à 50 s (30 par défaut). Durée pendant laquelle la requête attend le résultat. Si le calcul n'est pas terminé à temps, la réponse est un 202 avec l'identifiant de la tâche.
curl -X POST "https://gprviewer.com/api/v1/rebars?filename=SCAN_001.DZT&wait=30" \
  -H "Authorization: Bearer $GPV_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @SCAN_001.DZT

Variante multipart :

curl -X POST "https://gprviewer.com/api/v1/rebars?filename=SCAN_001.DZT" \
  -H "Authorization: Bearer $GPV_API_KEY" \
  -F "file=@SCAN_001.DZT"

Réponse 200 — calcul terminé

{
  "id": "0c180c12-864e-471b-9312-76ac22fa72fb",
  "status": "done",
  "created_at": "2026-11-06T10:51:36.31Z",
  "finished_at": "2026-11-06T10:51:37.38Z",
  "result": {
    "engine": "2026.11.06",
    "file": { "device": "DF", "antenna": "D50800", "channels": 2, "traces": 397, "samples": 512,
              "traces_per_m": 100, "range_ns": 33.333, "length_m": 3.97, "epsr": 9.042 },
    "channel": 1,
    "surface_sample": 51,
    "polarity": "normal",
    "rebars": [
      { "distance_m": 1.63, "depth_cm": 19.8, "time_ns": 3.971, "confidence": 0.965, "trace": 163, "sample": 61 },
      { "distance_m": 1.9,  "depth_cm": 18.8, "time_ns": 3.776, "confidence": 0.945, "trace": 190, "sample": 58 }
    ],
    "count": 2
  },
  "usage": { "length_m": 3.97, "charged_m": 4 },
  "balance_m": 192
}

Signification des champs

ChampSignification
distance_mDistance depuis le début du profil, en mètres.
depth_cmProfondeur depuis la surface, en centimètres, calculée avec l'εr du fichier ou celui passé dans epsr.
time_nsTemps aller-retour depuis la surface, en nanosecondes.
confidenceIndice de confiance du modèle, de 0 à 1.
traceIndice de la trace dans le fichier.
sampleIndice de l'échantillon, compté depuis la surface détectée.
result.fileCaractéristiques du fichier lu : appareil, antenne, nombre de voies, traces, échantillons, traces par mètre, fenêtre temporelle (ns), longueur (m), εr retenu.
result.channelVoie analysée.
result.surface_sampleÉchantillon de la surface détectée dans le fichier.
result.polarity« normal » ou « inverted » : sens du signal retenu. Pour un SEG-Y, la polarité est détectée automatiquement (certains appareils enregistrent en polarité inverse).
usageLongueur du profil et mètres facturés (arrondis au mètre supérieur).
balance_mSolde restant après ce fichier, en mètres.

Réponse 202 — calcul en cours

{ "id": "0c180c12-864e-471b-9312-76ac22fa72fb", "status": "queued", "poll": "/v1/jobs/0c180c12-864e-471b-9312-76ac22fa72fb" }

Si le résultat n'est pas prêt dans le délai wait, la réponse donne l'identifiant de la tâche. Interrogez alors GET /v1/jobs/{id} (avec ?wait=20 pour attendre jusqu'à 20 s par appel) jusqu'à obtenir le statut done ou failed. Une tâche terminée porte l'objet result décrit plus haut.

GET/v1/jobs/{id}[?wait=20]

Consulter le solde

GET/v1/balance
{ "balance_m": 192, "unit": "m" }

Consulter la consommation

GET/v1/usage

Solde, consommation des 30 derniers jours (par jour) et 50 derniers traitements du compte. Le détail complet, les clés et les factures sont aussi dans votre espace API sur gprviewer.com.

{
  "balance_m": 133, "unit": "m",
  "last_30_days": {
    "jobs": 6, "charged_m": 67,
    "by_day": [ { "day": "2026-11-06", "jobs": 6, "charged_m": 67 } ]
  },
  "recent": [
    { "id": "f44ada08-…", "created_at": "2026-11-06T13:37:30Z", "status": "done",
      "format": "dzt", "length_m": 0.75, "charged_m": 1, "count": 5 }
  ]
}

Erreurs

En cas d'erreur, la réponse porte un code HTTP explicite et un corps de la forme :

{ "error": { "code": "insufficient_credits", "message": "…" } }
HTTPCodeSignification
400bad_epsrepsr hors de l'intervalle 1–81.
400empty_fileFichier vide.
400missing_fileAucun fichier dans la requête.
401invalid_api_keyClé absente, invalide ou révoquée.
402insufficient_creditsSolde insuffisant pour traiter ce fichier.
413file_too_largeFichier de plus de 150 Mo.
415unsupported_formatExtension absente ou format non pris en charge.
429too_many_jobsDéjà 5 fichiers en cours de calcul sur ce compte : attendez la fin de l'un d'eux.
500internal_errorErreur interne : réessayez plus tard. Le fichier n'est pas facturé.

Exemples complets

Chaque exemple envoie un fichier, interroge /v1/jobs/{id} si nécessaire jusqu'à la fin du calcul, puis affiche les armatures. La clé est lue dans la variable d'environnement GPV_API_KEY.

#!/bin/sh
# Usage: GPV_API_KEY=gpv_live_... ./pick-rebars.sh SCAN_001.DZT
API="https://gprviewer.com/api"
FILE="$1"
NAME=$(basename "$FILE")

# 1. Upload the file (waits up to 30 s for the result).
#    X-File-Name instead of ?filename= : no URL-encoding needed for spaces.
curl -s -X POST "$API/v1/rebars?wait=30" \
  -H "Authorization: Bearer $GPV_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  -H "X-File-Name: $NAME" \
  --data-binary "@$FILE" > result.json

# 2. Still processing (202)? Poll the job until it is done or failed
while :; do
  STATUS=$(jq -r '.status // "error"' result.json 2>/dev/null || echo error)
  case "$STATUS" in done|failed|error) break ;; esac
  JOB=$(jq -r '.id' result.json)
  sleep 1
  curl -s "$API/v1/jobs/$JOB?wait=20" \
    -H "Authorization: Bearer $GPV_API_KEY" > result.json
done

# 3. Print the rebars, or the error
if [ "$STATUS" = "done" ]; then
  jq -r '.result.rebars[] | "\(.distance_m) m\t\(.depth_cm) cm\tconfidence \(.confidence)"' result.json
  jq -r '"\(.result.count) rebar(s)"' result.json
else
  cat result.json; exit 1
fi

Nécessite curl et jq.

import os
import requests

API = "https://gprviewer.com/api"
HEADERS = {"Authorization": f"Bearer {os.environ['GPV_API_KEY']}"}


def check(r):
    """Return the JSON body, or raise with the API error code."""
    body = r.json()
    if r.status_code not in (200, 202):
        err = body.get("error", {})
        raise RuntimeError(f"HTTP {r.status_code} {err.get('code')}: {err.get('message')}")
    return body


def pick_rebars(path, epsr=None):
    params = {"filename": os.path.basename(path), "wait": 30}
    if epsr is not None:
        params["epsr"] = epsr

    # 1. Upload the raw file
    with open(path, "rb") as f:
        job = check(requests.post(
            f"{API}/v1/rebars", params=params, data=f,
            headers={**HEADERS, "Content-Type": "application/octet-stream"},
            timeout=120,
        ))

    # 2. 202 = still processing: poll the job
    while job["status"] not in ("done", "failed"):
        job = check(requests.get(
            f"{API}/v1/jobs/{job['id']}", params={"wait": 20},
            headers=HEADERS, timeout=60,
        ))

    if job["status"] == "failed":
        raise RuntimeError(f"Job {job['id']} failed: {job.get('error')}")
    return job["result"]


if __name__ == "__main__":
    result = pick_rebars("SCAN_001.DZT")
    for b in result["rebars"]:
        print(f"{b['distance_m']:7.2f} m  {b['depth_cm']:6.1f} cm  confidence {b['confidence']:.2f}")
    print(f"{result['count']} rebar(s) on {result['file']['length_m']} m")

Python 3.8+ et requests (pip install requests).

// pick-rebars.mjs — Node.js 18+
import { readFile } from "node:fs/promises";
import { basename } from "node:path";

const API = "https://gprviewer.com/api";
const AUTH = { Authorization: `Bearer ${process.env.GPV_API_KEY}` };

// Return the JSON body, or throw with the API error code
async function call(url, init = {}) {
  const res = await fetch(url, { ...init, headers: { ...AUTH, ...init.headers } });
  const body = await res.json();
  if (!res.ok) {
    const e = body.error ?? {};
    throw new Error(`HTTP ${res.status} ${e.code}: ${e.message}`);
  }
  return body;
}

async function pickRebars(path, { epsr } = {}) {
  const params = new URLSearchParams({ filename: basename(path), wait: "30" });
  if (epsr !== undefined) params.set("epsr", String(epsr));

  // 1. Upload the raw file
  let job = await call(`${API}/v1/rebars?${params}`, {
    method: "POST",
    headers: { "Content-Type": "application/octet-stream" },
    body: await readFile(path),
  });

  // 2. 202 = still processing: poll the job
  while (job.status !== "done" && job.status !== "failed") {
    job = await call(`${API}/v1/jobs/${job.id}?wait=20`);
  }
  if (job.status === "failed") throw new Error(`Job ${job.id} failed`);
  return job.result;
}

const result = await pickRebars(process.argv[2] ?? "SCAN_001.DZT");
for (const b of result.rebars) {
  console.log(`${b.distance_m.toFixed(2)} m  ${b.depth_cm.toFixed(1)} cm  confidence ${b.confidence.toFixed(2)}`);
}
console.log(`${result.count} rebar(s) on ${result.file.length_m} m`);

Node.js 18+ (fetch natif). Enregistrez le script sous pick-rebars.mjs.

Confidentialité

  • Le fichier est conservé uniquement le temps du calcul, puis supprimé (au plus une heure après l'envoi).
  • Le nom du fichier n'est jamais enregistré.
  • Aucune donnée n'est utilisée pour l'entraînement sans votre accord.

Conditions d'utilisation (résumé)

  • L'usage de l'API est soumis aux conditions générales d'utilisation de GPRviewer.
  • Il est interdit d'utiliser les résultats pour entraîner ou reproduire un modèle concurrent.
  • Débit limité : 5 fichiers en cours de calcul simultanément par compte.
  • Les résultats sont fournis comme une aide à l'interprétation, sans garantie de détection exhaustive.
  • Contact : info@mds-paris.com · 01 46 27 36 35

Prêt à intégrer le Pointé IA ?

Créez votre compte, générez votre clé et traitez vos 200 premiers mètres gratuitement.

Essayer gratuitement — 200 m offerts