Car Image API

Kunden · Händler-, Import- und VIN-Tools

So machen Sie in Händlersoftware aus einer VIN oder einer Bestandsliste ein Foto für jedes Fahrzeug

Wie Händler- und Import-Tools von einer VIN oder einer Zeile in der Bestandsliste zu einem Studiobild für jeden Datensatz gelangen: kostenlose VIN-Dekodierung, resolve, stabile IDs und signierte URLs.

Das Problem: Der Datensatz hat eine VIN, Monate bevor es ein Foto gibt

Ein Bestandsdatensatz in einem CRM, einem ERP oder einem Dealer-Management-System beginnt mit einer VIN, einer Bestandsnummer und einem Preis. Das Foto kommt, wenn der Fotograf auf den Hof kommt, und bei manchen Fahrzeugen kommt es nie: ein Fahrzeug, das noch unterwegs ist, eine Inzahlungnahme, die noch aufbereitet wird, ein Import, dessen Modellname zu nichts im Bild-Feed des DMS passt, ein Auto aus den 1990ern, für das nie jemand ein Pressefoto lizenziert hat. Jeder Bildschirm, der diesen Datensatz zeigt – die Liste des Verkäufers, die Bewertung des Einkäufers, das Angebot für den Kunden –, zeigt stattdessen einen grauen Platzhalter.

Die Händler- und Import-Tools auf Basis der API füllen diesen Platzhalter mit dem, was der Datensatz bereits enthält. Hat er eine VIN, ist das Bild nur eine kostenlose Dekodierung entfernt. Steht er nur als Zeile in einer Bestandsliste, führt die Freitext-Auflösung (resolve) ans Ziel. So oder so enthält der Datensatz am Ende eine stabile Fahrzeug-ID, und jeder Bildschirm stellt dasselbe Fahrzeug auf dieselbe Weise dar.

So sieht eine Händlerintegration aus

  • Bei der Aufnahme dekodieren. Die VIN wird einmal dekodiert, wenn der Datensatz angelegt wird, und die Fahrzeug-ID aus dem Katalog wird daneben gespeichert. Nichts im weiteren Verlauf schreibt eine Marke oder ein Modell aus.
  • Eine Ansicht für die Liste, eine für die Detailseite. Eine gerade side-Ansicht in breitem Format für Bestandslisten; front-3-4 für die Detailseite und das Angebot.
  • Weit zurückreichende Baujahre. Import- und Gebrauchtwagenbestand reicht bis in die 1990er zurück und wird wie die Neuzugänge gerendert: Der Katalog umfasst 1990 bis zum aktuellen Modelljahr, japanische Inlandsmodelle eingeschlossen.
  • Signierte URLs in der App. Das CRM läuft im Browser, daher lädt es schlüsselfreie, vom Server erzeugte signierte URLs, bis zu fünfzig pro Aufruf, die sich für Datensätze verlängern, die länger als eine Woche bestehen.

Schritt 1: die VIN dekodieren – kostenlos

GET /api/v1/vin/{vin} kostet nichts und benötigt nur einen API-Schlüssel. Der Endpoint liefert Modelljahr, Marke, Modell, Ausstattungslinie, Karosserieklasse, Motor und jedes weitere Attribut, das die vPIC-Daten der NHTSA kennen, und – wenn der Katalog das Fahrzeug enthält – ein vehicle-Objekt mit seiner stabilen ID und einem fertigen image_path. Auch unvollständige VINs funktionieren: mindestens fünf Zeichen, * für jede unbekannte Stelle, plus ein optionaler year-Hinweis.

Dekodieren
curl -s -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/vin/1HGCM82633A004352"
200 OK (gekürzt)
{
  "data": {
    "vin": "1HGCM82633A004352",
    "valid": true,
    "year": 2003,
    "make": "Honda",
    "model": "Accord",
    "trim": "EX-V6",
    "body_class": "Coupe",
    "vehicle": {
      "id": "veh_3qfyk22gfhsx3",
      "make": "Honda",
      "model": "Accord",
      "year": 2003,
      "image_path": "/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3"
    }
  },
  "request_id": "req_01j9x…"
}

Schritt 1b: wenn Sie nur eine Zeile aus einer Bestandsliste haben

Importbestand kommt oft als Tabelle: ein Baujahr, eine Marke, ein Modellname in der Schreibweise, die das Auktionshaus verwendet hat, ein Grade- oder Ausstattungscode. POST /api/v1/images/resolve ist kostenlos und deterministisch: Der Endpunkt extrahiert Baujahr-, Farb- und Ansichtsbegriffe aus der Phrase, durchsucht den Katalog mit dem Rest und liefert exakte Parameter mit einem Konfidenzwert, auf dessen Grundlage Sie handeln können.

Eine Zeile aus der Bestandsliste auflösen
curl -s -X POST https://carimage.dev/api/v1/images/resolve \
  -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "2019 toyota voxy zs side"}'
200 OK (gekürzt)
{
  "data": {
    "params": {
      "vehicle_id": "veh_9w0nvw7jphp6r",
      "make": "toyota",
      "model": "voxy",
      "year": 2019,
      "view": "side",
      "color": "silver"
    },
    "display": {
      "make_name": "TOYOTA",
      "model_name": "VOXY"
    },
    "confidence": "low",
    "image_path": "/api/v1/images/car?make=toyota&model=voxy&year=2019&view=side&color=silver"
  },
  "request_id": "req_01j9x…"
}

Das Fahrzeug stimmt (der Toyota Voxy von 2019), aber die Konfidenz ist low, weil „ZS“ eine Ausstattungslinie ist, die der Katalog nicht kennt, und um sie herum abgeglichen werden musste. Behandeln Sie bei einem Batch-Import high als erledigt und legen Sie medium und low zusammen mit der candidates-Liste einem Menschen vor. Wenn die Liste Baujahr und Marke bereits in eigenen Spalten enthält, senden Sie sie als Felder year und make und lassen Sie nur das Modell in query; der Abgleich wird dadurch deutlich sauberer. Eine Eingabe ohne Fahrzeugbegriffe ergibt einen 400, und eine Marke ohne passendes Modell einen 404, der die Baujahre der Marke auflistet, damit Sie nach dem Modell fragen können.

Schritt 2: die Fahrzeug-ID im Datensatz speichern

Egal, auf welchem Weg Sie dorthin gelangt sind: Speichern Sie vehicle.id (oder params.vehicle_id) im Bestandsdatensatz als einfache String-Spalte. Die ID ist deterministisch und dauerhaft: Dasselbe Fahrzeug hat bei jedem Deploy und nach jedem Katalog-Release dieselbe ID, sie eignet sich also sicher als Fremdschlüssel. Ab dann lautet jede Bildanfrage vehicle=veh_…, was sich nicht falsch schreiben lässt, und eine vom Hersteller umbenannte oder von einer Quelle neu eingeordnete Marke macht nie eine gespeicherte URL ungültig.

Schritt 3: eine Ansicht für die Liste, eine für die Detailseite

Bestandslisten sind breit und flach, daher ist die gerade Seitenansicht in einem 2:1-Rahmen die übliche Wahl: Jedes Fahrzeug zeigt in dieselbe Richtung und steht auf derselben Grundlinie. trim=1&padding=6 beschneidet das Bild vor dem Einpassen auf das Auto, sodass ein Hilux und ein Yaris die Zeile gleich ausfüllen; fit=contain hält die Leinwand exakt bei 640×320 mit transparenten Rändern. Die Detailseite verwendet front-3-4 bei 1024, und beide teilen sich einen Cache: Eine nicht angegebene Lackfarbe ist bei beiden Silber.

Das Listenbild für einen Datensatz
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3&view=side&w=640&h=320&fit=contain&trim=1&padding=6&format=webp" \
  -o stock-4471.webp

Schritt 4: im CRM über signierte URLs anzeigen

Das CRM läuft im Browser, und ein Browser darf niemals den API-Schlüssel enthalten. Wenn eine Ansicht eine Seite des Bestands auflistet, erzeugt der Server mit POST /api/v1/image-urls eine signierte URL pro Datensatz (bis zu fünfzig pro Aufruf, je ein Credit), und die Seite lädt sie wie jedes andere Bild. Für einen Datensatz, der monatelang im Bestand bleibt, sind zwei Einstellungen wichtig:

  • ttl_seconds: 604800 mit renew: true: Die URL funktioniert über die siebentägige TTL hinaus, und jede weitere Woche, in der jemand den Datensatz tatsächlich öffnet, kostet einen weiteren Credit. Wochen, in denen niemand das Fahrzeug ansieht, kosten nichts.
  • Ein Idempotency-Key pro Seite der Synchronisierung: Eine wiederholte Anfrage spielt die erste Antwort erneut ab und erzeugt nichts doppelt.

Beschränken Sie den Schlüssel, der die URLs erzeugt, im Dashboard auf die Origin des CRM, dann antwortet eine URL, die auf eine andere Website gelangt, mit 403. Wird der Schlüssel widerrufen, werden alle von ihm erzeugten URLs ungültig. Sollte das Guthaben einmal aufgebraucht sein, antwortet eine Verlängerung mit 402, und dieselbe URL funktioniert wieder, sobald Credits eingehen; die automatische Aufladung beseitigt diese Fehlerquelle. Den vollständigen Lebenszyklus finden Sie in der Dokumentation zu signierten URLs.

Fahrzeugaufnahme und Bestandsliste mit dem TypeScript SDK
import { CarImageClient } from "@meterapp/car-image-sdk";

const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });

// On intake: VIN in, vehicle id on the record. Free.
const { data: decoded } = await client.decodeVin(stock.vin);
if (decoded.vehicle) {
  stock.vehicleId = decoded.vehicle.id; // veh_…, permanent; the record never spells a car again
  stock.trim = decoded.trim;            // shown next to the image, never claimed by it
}

// When the CRM draws the stock list: one signed URL per record, up to fifty per call.
// The browser loads them; the key never leaves the server.
const { data: urls } = await client.createImageUrls(
  page.map((row) => ({
    vehicle: row.vehicleId, view: "side",
    width: 640, height: 320, fit: "contain", trim: true, padding: 6, format: "auto",
  })),
  { ttlSeconds: 604_800, renew: true, renewDays: 365, idempotencyKey: `stock-list:${syncRunId}:${pageIndex}` }
);

CLI, SDK und MCP

npx @meterapp/car-image
# decode for free; the output names the vehicle id and a ready-to-run image command
npx @meterapp/car-image vin 1HGCM82633A004352
npx @meterapp/car-image vin 1HGCM82633A004352 --json | jq -r .data.vehicle.id

# a partial VIN from a faded door sticker: at least five characters, * for each unknown position
npx @meterapp/car-image vin "1HGCM826*3A" --year 2003

# the list image for one record
npx @meterapp/car-image get --vehicle veh_3qfyk22gfhsx3 --view side --width 640 --height 320 --fit contain --trim --padding 6 --format webp --out stock-4471.webp
  • SDK: client.decodeVin(vin, { year? }), client.resolve(query), client.getImage({ vehicle, … }) und client.createImageUrls(…), wie oben.
  • MCP: decode_vin, resolve_vehicle, get_car_image und create_car_image_urls gehören zum Kern-Toolset, sodass ein Assistent mit dem Bestandssystem im Kontext „Foto für Bestand 4471“ anhand der VIN im Datensatz beantworten kann. Die Einrichtung finden Sie auf der MCP-Seite, die Felder und Fehler des Decoders in der VIN-Dokumentation.

Häufig gestellte Fragen

Was kostet ein Foto für jeden Bestandsdatensatz?
Die VIN-Dekodierung ist kostenlos. Das erste Bild einer Kombination aus Fahrzeug, Ansicht und Lackfarbe kostet einen Credit und wird danach für alle zwischengespeichert; eine erneute Prüfung mit If-None-Match ist kostenlos. Eine signierte URL kostet beim Erzeugen einen Credit und mit renew: true einen weiteren Credit für jede weitere Woche, in der sie tatsächlich geöffnet wird. Eine Händlergruppe mit einigen tausend Fahrzeugen im Bestand gibt einmalig einige tausend Credits aus, danach fast nichts mehr.
Die VIN wird dekodiert, aber vehicle ist null. Was nun?
Der Decoder kannte Marke, Modell und Baujahr, aber der Katalog hat keinen Eintrag für diese Kombination: ein Anhänger, manche Nutzfahrzeug-Fahrgestelle, eine Modellbezeichnung, die die Quellen noch nicht erfasst haben. Zeigen Sie die decodierten Felder an, durchsuchen Sie den Katalog mit GET /api/v1/vehicles?q=, falls die Schreibweise abweicht, und beantragen Sie das Fahrzeug auf dem Anfrage-Board; POST /api/v1/requests stimmt für eine offene Anfrage ab, statt sie zu duplizieren.
Deckt die VIN-Dekodierung auch Importe und Fahrzeuge ab, die nie in den USA verkauft wurden?
Der Decoder nutzt die vPIC-Daten der NHTSA, daher wird eine VIN eines Herstellers, der bei der NHTSA meldet, vollständig dekodiert. Ein Fahrzeug, das nie für die USA zertifiziert wurde, liefert oft nur Hersteller und Baujahr ohne Modell, und manche lassen sich gar nicht dekodieren. Für solche Fahrzeuge ermitteln Sie das Modell stattdessen über den Fahrzeugschein: Der Katalog selbst ist weltweit, ab 1990, und auch Modelle für den japanischen Inlandsmarkt wie der Toyota Voxy sind enthalten.
Zeigt das Rendering die Ausstattungsvariante aus dem Datensatz?
Nein. Der Katalog unterscheidet nach Marke, Modell und Baujahr, daher werden ein EX-V6 Coupé und eine LX Limousine als derselbe Accord von 2003 gerendert. Behalten Sie trim und body_class aus der Dekodierung als Text neben dem Bild und beschreiben Sie das Rendering nie als das konkrete Fahrzeug.
Kann die Händler-Website dieselben URLs wie das CRM verwenden?
Erzeugen Sie sie mit einem Schlüssel, dessen erlaubte Origins die Website enthalten; dann antwortet eine URL, die ein Browser von einer anderen Website lädt, mit 403 origin_not_allowed. Aufrufe ohne Referer, etwa aus einem E-Mail-Client, werden durchgelassen. CRM und Website können einen Schlüssel und einen Satz URLs gemeinsam nutzen oder jeweils einen eigenen Schlüssel verwenden, sodass sich jeder einzeln widerrufen lässt.