Aller au contenu
API Plaque Immatriculation
Tutoriel 10 min de lecture

Intégrer un VIN decoder en PHP et JavaScript

Tutoriel pas à pas : un relais serveur en PHP et en JavaScript (Next.js) pour décoder un VIN, avec validation, gestion des erreurs et clé API protégée.

Poste de développeur avec deux écrans affichant du code coloré et une voiture miniature posée sur le bureau

Appeler une API VIN depuis un curl de démonstration prend une minute. L'intégrer dans un site en production demande un peu plus : un endpoint côté serveur qui garde la clé secrète, valide la saisie, gère les délais d'attente et traduit chaque échec en message compréhensible.

Ce tutoriel construit ce relais deux fois, en PHP puis en JavaScript (route Next.js), avec exactement le même comportement. Le front-end n'appelle jamais l'API VIN directement : il appelle votre serveur.

L'architecture en trois étages

Navigateur ──► Votre endpoint /api/vin ──► API VIN Decoder (RapidAPI)
               (clé lue dans l'environnement)

Pourquoi ce détour ? Parce qu'une clé placée dans du JavaScript de navigateur ou dans une application mobile peut être lue par n'importe qui. Une fois extraite, elle peut consommer votre quota. Le relais serveur permet aussi de valider la saisie, de mettre en cache et de filtrer les champs renvoyés.

Prérequis

  • Une clé obtenue en souscrivant à l'API VIN Decoder sur RapidAPI.
  • La clé stockée dans une variable d'environnement du serveur, ici RAPIDAPI_KEY. Selon votre hébergement : fichier .env exclu du dépôt Git, variable définie dans le panneau de l'hébergeur ou dans la configuration du serveur web.
  • PHP 8.1 ou plus avec l'extension cURL, ou Node.js 18 ou plus pour la version JavaScript.

D'après notre référence API, l'appel est une requête GET sur https://api-vin-decoder.p.rapidapi.com/vin avec le paramètre vin, et les en-têtes x-rapidapi-key et x-rapidapi-host. La réponse contient un objet data, un champ code_erreur et un champ message.

Les règles communes aux deux versions

  1. Lire la clé dans l'environnement ; si elle manque, répondre 500 sans détail technique.
  2. Normaliser le VIN (majuscules, sans espaces ni tirets) et valider sa forme : 17 caractères, sans I, O ni Q. Un VIN invalide reçoit une réponse 400 sans appel à l'API.
  3. Appeler l'API avec un délai d'attente.
  4. Vérifier trois indicateurs : le statut HTTP, code_erreur et data.erreur.
  5. Renvoyer au navigateur une liste blanche de champs, avec null pour les valeurs vides.
  6. Traduire les échecs : véhicule introuvable (404), saturation ou quota (503), autre erreur (502). Le détail technique va dans les journaux du serveur, pas dans la réponse.

Version PHP

Placez ce fichier, par exemple, dans public/api/vin.php. Il répond à GET /api/vin.php?vin=....

<?php
// vin.php : relais serveur entre votre front-end et l'API VIN Decoder (PHP 8.1+, extension cURL).
declare(strict_types=1);
 
const API_HOST = 'api-vin-decoder.p.rapidapi.com';
 
header('Content-Type: application/json; charset=utf-8');
 
function respond(int $status, array $body): never
{
    http_response_code($status);
    echo json_encode($body, JSON_UNESCAPED_UNICODE);
    exit;
}
 
// 1. La clé est lue dans l'environnement du serveur, jamais dans le code ni côté navigateur.
$apiKey = getenv('RAPIDAPI_KEY');
if ($apiKey === false || $apiKey === '') {
    error_log('RAPIDAPI_KEY absente');
    respond(500, ['error' => 'Service de décodage non configuré.']);
}
 
// 2. Normalisation et validation du VIN avant tout appel.
$vin = strtoupper(preg_replace('/[\s-]/', '', (string) ($_GET['vin'] ?? '')));
if (!preg_match('/^[A-HJ-NPR-Z0-9]{17}$/', $vin)) {
    respond(400, ['error' => 'Le VIN doit compter 17 caractères, sans I, O ni Q.']);
}
 
// 3. Appel de l'API avec délais d'attente.
$ch = curl_init('https://' . API_HOST . '/vin?' . http_build_query(['vin' => $vin]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => [
        'x-rapidapi-host: ' . API_HOST,
        'x-rapidapi-key: ' . $apiKey,
    ],
]);
$raw = curl_exec($ch);
$httpStatus = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$curlError = curl_error($ch);
curl_close($ch);
 
if ($raw === false) {
    error_log('API VIN injoignable : ' . $curlError);
    respond(502, ['error' => 'Service de décodage injoignable. Réessayez dans quelques instants.']);
}
 
// 4. Contrôle du statut HTTP, de code_erreur et de data.erreur.
$json = json_decode($raw, true);
$data = is_array($json) && is_array($json['data'] ?? null) ? $json['data'] : null;
$apiCode = (int) ($json['code_erreur'] ?? $httpStatus);
 
if ($httpStatus === 200 && $apiCode === 200 && $data !== null && ($data['erreur'] ?? '') === '') {
    // 5. On ne renvoie au navigateur que les champs utiles, valeurs vides converties en null.
    $fields = ['vin', 'marque', 'modele', 'version', 'energieNGC', 'puisFiscReelCH', 'boite_vitesse', 'debut_modele', 'fin_modele', 'k_type'];
    $vehicle = [];
    foreach ($fields as $field) {
        $value = trim((string) ($data[$field] ?? ''));
        $vehicle[$field] = $value === '' ? null : $value;
    }
    respond(200, ['vehicle' => $vehicle]);
}
 
error_log(sprintf('API VIN : HTTP %d, code_erreur %d', $httpStatus, $apiCode));
[$status, $message] = match (true) {
    $httpStatus === 404, $apiCode === 404, $data !== null && ($data['erreur'] ?? '') !== '' => [404, 'Aucun véhicule trouvé pour ce VIN.'],
    $httpStatus === 429, $apiCode === 429 => [503, 'Trop de demandes pour le moment. Réessayez plus tard.'],
    default => [502, 'Le décodage a échoué. Réessayez plus tard.'],
};
respond($status, ['error' => $message]);

Quelques choix à noter :

  • respond() centralise la sortie JSON et termine le script ; le type de retour never impose PHP 8.1.
  • CURLOPT_CONNECTTIMEOUT et CURLOPT_TIMEOUT évitent qu'un appel bloqué n'immobilise un processus PHP.
  • La clé n'apparaît ni dans les messages d'erreur ni dans les journaux : seuls le statut HTTP et code_erreur sont journalisés.

Avec un framework comme Laravel, la logique est la même : la clé se lit via la configuration (config('services.rapidapi.key')) alimentée par le fichier .env.

Version JavaScript (Next.js)

Avec Next.js (App Router), un fichier app/api/vin/route.js s'exécute côté serveur et répond à GET /api/vin?vin=.... La variable RAPIDAPI_KEY ne doit pas être préfixée par NEXT_PUBLIC_, sinon elle serait intégrée au code envoyé au navigateur.

// app/api/vin/route.js : route Next.js (App Router) exécutée côté serveur.
const API_HOST = "api-vin-decoder.p.rapidapi.com";
const FIELDS = ["vin", "marque", "modele", "version", "energieNGC", "puisFiscReelCH", "boite_vitesse", "debut_modele", "fin_modele", "k_type"];
 
function error(status, message) {
  return Response.json({ error: message }, { status });
}
 
export async function GET(request) {
  const apiKey = process.env.RAPIDAPI_KEY;
  if (!apiKey) {
    console.error("RAPIDAPI_KEY absente");
    return error(500, "Service de décodage non configuré.");
  }
 
  const vin = (new URL(request.url).searchParams.get("vin") ?? "").toUpperCase().replace(/[\s-]/g, "");
  if (!/^[A-HJ-NPR-Z0-9]{17}$/.test(vin)) {
    return error(400, "Le VIN doit compter 17 caractères, sans I, O ni Q.");
  }
 
  let response;
  try {
    response = await fetch(`https://${API_HOST}/vin?${new URLSearchParams({ vin })}`, {
      headers: { "x-rapidapi-host": API_HOST, "x-rapidapi-key": apiKey },
      signal: AbortSignal.timeout(10_000),
    });
  } catch (cause) {
    console.error("API VIN injoignable :", cause);
    return error(502, "Service de décodage injoignable. Réessayez dans quelques instants.");
  }
 
  const json = await response.json().catch(() => null);
  const data = json && typeof json.data === "object" ? json.data : null;
  const apiCode = Number(json?.code_erreur ?? response.status);
 
  if (response.ok && apiCode === 200 && data && !data.erreur) {
    const vehicle = Object.fromEntries(
      FIELDS.map((field) => {
        const value = String(data[field] ?? "").trim();
        return [field, value === "" ? null : value];
      }),
    );
    return Response.json({ vehicle });
  }
 
  console.error(`API VIN : HTTP ${response.status}, code_erreur ${apiCode}`);
  if (response.status === 404 || apiCode === 404 || data?.erreur) return error(404, "Aucun véhicule trouvé pour ce VIN.");
  if (response.status === 429 || apiCode === 429) return error(503, "Trop de demandes pour le moment. Réessayez plus tard.");
  return error(502, "Le décodage a échoué. Réessayez plus tard.");
}

Le même code s'adapte à Express ou à une fonction serverless : seules la lecture de la requête et la construction de la réponse changent. AbortSignal.timeout est disponible à partir de Node.js 18.

Côté navigateur

Le front-end appelle uniquement votre endpoint. Il n'a besoin d'aucune clé :

async function decodeVin(vin) {
  const response = await fetch(`/api/vin?vin=${encodeURIComponent(vin)}`);
  const body = await response.json();
  if (!response.ok) throw new Error(body.error);
  return body.vehicle;
}
 
try {
  const vehicle = await decodeVin("VF1DZ0N0641118804");
  console.log(vehicle.marque, vehicle.modele, vehicle.version);
} catch (error) {
  // Message déjà rédigé pour l'utilisateur par le serveur.
  showMessage(error.message);
}

showMessage représente ici votre propre fonction d'affichage.

Résultats des tests

Voici les réponses obtenues avec le serveur de test local décrit plus haut. Elles sont identiques pour les versions PHP et JavaScript.

ScénarioSaisie envoyéeRéponse du relais
VIN de l'exemple, saisi avec tirets et minusculesvf1-dz0n06-41118804200, fiche Renault Mégane III 1.9 dCi
VIN trop court16 caractères400, message de format
Lettre O à la place d'un zéroVF1DZ0N0O41118804400, message de format
Véhicule inconnu (404 simulé)VIN fictif404, « Aucun véhicule trouvé »
Quota dépassé (429 simulé)VIN fictif503, « Trop de demandes »
Erreur serveur (500 simulée)VIN fictif502, « Le décodage a échoué »
Variable d'environnement absenteVIN de l'exemple500, « Service non configuré »
Serveur injoignableVIN de l'exemple502, « Service injoignable »

La réponse 200 renvoyée au navigateur pour le VIN de l'exemple était :

{
  "vehicle": {
    "vin": "VF1DZ0N0641118804",
    "marque": "RENAULT",
    "modele": "MEGANE III",
    "version": "1.9 dCi",
    "energieNGC": "DIESEL",
    "puisFiscReelCH": "131 CH",
    "boite_vitesse": "M",
    "debut_modele": "2008-11",
    "fin_modele": "2015-08",
    "k_type": "31164"
  }
}

Aller plus loin

  • Cache : les caractéristiques d'origine d'un VIN ne changent pas. Un cache (Redis, APCu, base de données) indexé sur le VIN normalisé réduit la consommation de requêtes.
  • Limitation de débit : votre endpoint est public. Limitez le nombre d'appels par adresse IP ou par utilisateur connecté pour éviter qu'un tiers ne consomme votre quota à travers lui.
  • Contrôle de la position 9 : pour les VIN nord-américains, un contrôle supplémentaire est possible côté serveur. Notre article VIN décodeur : comprendre la structure d'un numéro de châssis explique pourquoi il ne faut pas l'appliquer aux VIN européens.
  • Recherche par plaque : le même modèle de relais fonctionne avec l'API plaque. Voir API plaque d'immatriculation France : fonctionnement et intégration.

Cas concret : un formulaire de devis carrosserie

Un carrossier ajoute un champ « VIN » à son formulaire de devis en ligne, sur un site PHP existant. Il dépose vin.php, définit RAPIDAPI_KEY dans la configuration de son hébergement et ajoute la fonction decodeVin au formulaire. Quand le client quitte le champ VIN, la marque, le modèle et la version s'affichent pour confirmation. En cas d'erreur, le message du serveur s'affiche sous le champ et le client peut saisir son véhicule manuellement.

Conclusion

Un VIN decoder bien intégré tient en un fichier côté serveur : clé lue dans l'environnement, validation avant l'appel, délai d'attente, contrôle de trois indicateurs d'erreur et réponse filtrée. Les versions PHP et JavaScript présentées ici suivent les mêmes règles. Il reste à les tester avec votre clé et à ajouter un cache et une limitation de débit adaptés à votre trafic. Pour comprendre ce que contient la fiche renvoyée, lisez VIN info : quelles informations peut-on obtenir à partir d'un VIN ?.

Obtenez votre clé API VIN

Souscrivez sur RapidAPI, ajoutez la clé à vos variables d'environnement et testez le relais sur vos propres VIN.

FAQ

Puis-je appeler l'API VIN directement depuis le navigateur ?

Techniquement oui, mais la clé serait visible dans le code ou dans les requêtes réseau. Passez toujours par un endpoint serveur.

Où stocker la clé API en production ?

Dans une variable d'environnement définie sur le serveur ou dans le panneau de votre hébergeur. Ne la commitez jamais dans le dépôt Git.

Pourquoi valider le VIN côté serveur si le formulaire le fait déjà ?

La validation côté navigateur se contourne facilement. Celle du serveur protège votre quota et garantit des messages d'erreur cohérents.

Pourquoi ne pas renvoyer la réponse complète de l'API au navigateur ?

Une liste blanche de champs limite le volume transféré, stabilise votre format de réponse et évite d'exposer des données dont votre interface n'a pas besoin.

Comment tester sans consommer de requêtes ?

Comme pour ce tutoriel, un petit serveur de test local qui renvoie la réponse d'exemple publiée permet de vérifier la gestion des erreurs avant d'utiliser votre clé.

Articles liés

Tutoriel 6 min

API plaque Espagne : intégrer la recherche par matrícula

Format des plaques espagnoles, normalisation des saisies et cas d'usage : comment intégrer une recherche de véhicule par matrícula avec le paramètre pays=ES.

  • #api plaque espagne
  • #matricula
  • #espagne

Api Plaque Immatriculation ·

Testez l'API plaque d'immatriculation gratuitement

10 requêtes offertes chaque mois, sans engagement. Votre clé est disponible immédiatement sur RapidAPI.