Das Problem: Ein Inserat braucht ein Bild, bevor der Verkäufer eines hochlädt
Ein Kleinanzeigenportal oder ein Autokatalog steht und fällt mit seinem Raster. Jede Karte braucht ein Bild, sobald das Inserat existiert. Das eigene Foto des Verkäufers kommt später, wenn überhaupt, aus einem beliebigen Winkel in einer beliebigen Einfahrt. Stockfotos haben das umgekehrte Problem: Ein lizenziertes Pressefoto gibt es nur für die beliebten Autos der beliebten Jahrgänge, in dem Winkel und vor dem Hintergrund, den der Hersteller gewählt hat. Für alles andere gibt es keines. Ein Katalog von 1990 bis zum aktuellen Modelljahr hat Zehntausende Lücken.
Was die Teams hinter diesen Websites wollen, ist im besten Sinne langweilig: ein Studiobild pro Modelljahr, derselbe Blickwinkel und dieselbe Lackfarbe für alle, transparent, damit es auf jedem Kartenhintergrund passt, und klein genug, um es zehntausendmal am Tag auszuliefern.
So sieht eine Katalog-Integration aus
Die Katalogprojekte, die heute über die API laufen, sind alle gleich aufgebaut – ob Kleinanzeigenportal, das seine Inserate befüllt, Autokauf-App, die ihre Vergleichskarten rendert, oder mobile App, die ein Hero-Bild pro Fahrzeug zeigt:
- Erst ein Backfill, dann stetiger Nachschub. Tausende Renderings in wenigen Stunden, um den Katalog abzudecken, danach eine Handvoll pro Tag, wenn neue Modelljahre und Anfragen hinzukommen.
- Ein Blickwinkel, ein Lack oder eine kleine Palette. Die meisten wählen
front-3-4odersidein Silber oder Weiß. Ein Autokauf-Katalog, in dem Nutzer eine Farbe wählen können, rendert einige Voreinstellungen pro Fahrzeug; jede kostet einen Credit mehr, keine neue Pipeline. - WebP in fester Box. 512 bis 1024 Pixel breit, geliefert in genau dem Format, das die Karte verwendet, sodass das Frontend nie zuschneiden oder Balken einfügen muss.
- Nach Fahrzeug-ID gespeichert. Die Renderings landen im eigenen Object Store oder CDN des Teams, oder die Seiten laden signierte URLs; in beiden Fällen ist der Schlüssel die stabile
veh_…-ID des Katalogs, kein Marke-Modell-Jahr-String.
Der Rest dieser Seite beschreibt diese Pipeline Schritt für Schritt, mit Code. Vorausgesetzt werden ein API-Schlüssel mit dem Scope images:read und ein auf den Katalog abgestimmter Tarif (siehe FAQ).
Schritt 1: Den Katalog nach ID durchgehen, nie nach Schreibweise
Der häufigste Fehler beim Aufbau eines Katalogs ist nicht das Rendering, sondern die Schreibweise. Ein Build, der Marken- und Modellbezeichnungen aus einer anderen Datenbank erzeugt, kann den Großteil seiner Anfragen auf 404 vehicle_not_found verschwenden: kostenlos, aber jedes Mal ein vergeblicher Roundtrip und am Ende ein Raster mit Lücken. Die Lösung: Lassen Sie sich vom Katalog sagen, was es gibt. GET /api/v1/vehicles ist kostenlos, benötigt keinen Schlüssel, und jedes zurückgegebene Modell trägt eine stabile Fahrzeug-ID (veh_ plus 13 Zeichen), die sich über Deployments und Katalog-Releases hinweg nie ändert. Speichern Sie diese ID beim Inserat und rendern Sie anhand dieser ID.
# every make with a 2024 model year (free, no key)
curl -s "https://carimage.dev/api/v1/vehicles?year=2024"
# every 2024 model of one make, each with the id you will store
curl -s "https://carimage.dev/api/v1/vehicles?year=2024&makeId=584"{
"data": {
"year": 2024,
"make_id": 584,
"models": [
{
"name": "718",
"slug": "718",
"vehicle_type": "Passenger Car",
"id": "veh_b4bf090fyb53k"
},
{
"name": "911",
"slug": "911",
"vehicle_type": "Passenger Car",
"id": "veh_78qtwrgh37bkr"
}
]
}
}?make=porsche&model=911 listet alle Baujahre eines Modells mit einer ID pro Jahr auf, und ?q= durchsucht Freitext, wenn ein Verkäufer einen Namen eingibt. Derselbe Katalog ist als offenes Paket @meterapp/vehicle-db veröffentlicht, sodass Dropdowns und Validierung offline gegen die identische Liste laufen können. So oder so haben Sie am Ende dieses Schritts eine Liste von IDs, und nichts im weiteren Ablauf muss je wieder einen Fahrzeugnamen ausschreiben.
Inserate, die ihre Fahrzeuge bereits in der Schreibweise einer anderen Datenbank benennen, werden mit einem Aufruf zugeordnet: POST /api/v1/vehicles/check liest bis zu 2.000 davon so, wie es ein Rendering tun würde, und beantwortet jedes mit seiner Fahrzeug-ID, den nächstliegenden realen Fahrzeugen, wenn der Katalog kein solches Fahrzeug enthält, oder einem Fehltreffer, den Sie in jeder Ansicht und Farbe überspringen. car-image check listings.csv --out checked.csv schreibt die IDs zurück in die Datei. Das ist kostenlos und erspart es Ihnen, die Lücken einzeln über 404-Fehler zu entdecken.
Schritt 2: ein Anfrageformat für alle Inserate
Legen Sie die Karte einmal fest und kodieren Sie sie im Query-String. Alles nach vehicle= unten ist eine Entscheidung zum Bildausschnitt, die Sie einmal für den gesamten Katalog treffen:
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/images/car?vehicle=veh_78qtwrgh37bkr&view=front-3-4&color=silver&w=800&h=600&fit=contain&trim=1&padding=8&format=webp" \
-o veh_78qtwrgh37bkr.webpw=800&h=600&fit=containliefert genau 800×600, wobei das ganze Auto sichtbar ist und ringsum transparenter Rand bleibt, sodass jede Karte dieselbe Fläche hat.fit=coverfüllt stattdessen den Rahmen und schneidet mittig zu.trim=1&padding=8schneidet vor dem Einpassen auf die Kontur des Autos zu, mit 8 % seiner längeren Seite als Rand, sodass ein Suburban und ein Miata die Fläche gleich ausfüllen, statt im Ausgangsbild zu schweben.color=silverist der Standard und die sicherste neutrale Wahl; jede der 15 Voreinstellungen oder jeder Hex-Wert kostet denselben einen Credit.format=webpist die kleinste verlustfreie Kodierung mit Alphakanal. Wenn ein Browser die URL direkt lädt (signierte URLs, Schritt 4), handeltformat=autopro Client WebP oder PNG aus.
Die Antwort enthält ETag, X-Image-Source (cache oder generated) und X-Vehicle-Id, das ausgelieferte Katalogfahrzeug. Speichern Sie das ETag zusammen mit der Datei: Eine spätere Anfrage mit If-None-Match wird mit 304 beantwortet und kostet nichts – so bleibt eine nächtliche Neusynchronisierung kostenlos.
Schritt 3: Den Backfill innerhalb der Limits dosieren
Drei Limits bestimmen einen Backfill, und jedes davon kündigt sich in den Headern an, statt stillschweigend zu scheitern:
- Anfragen pro Minute. 120 pro Schlüssel bei Free und Pro, 600 bei Business, 1.200 bei Enterprise, dazu ein Limit pro Konto über alle Schlüssel hinweg. Jede authentifizierte Antwort enthält
RateLimit-Remaining; ein429enthältRetry-After. Vier bis acht gleichzeitige Anfragen sind völlig ausreichend. - Kalte Renderings. Die erste Anfrage für eine Variante generiert sie, was einige Sekunden dauert; jede weitere Anfrage wird aus dem Cache bedient. Neue Renderings zehren vom Anteil des Kontos am täglichen Render-Budget (2 % bei Free, 10 % bei Pro, 25 % bei Business, 50 % bei Enterprise). Darüber hinaus antwortet ein neues Rendering mit
429undcode: account_generation_capsowiereset_at(Mitternacht UTC), es wird nichts berechnet, und gecachte Bilder werden weiter ausgeliefert. Ein großer Backfill dauert ein paar Abende, nicht nur einen. - Verschiedene Fahrzeuge pro Monat. Free erlaubt 100, Pro 2.500, Business 15.000; Enterprise hat keine Obergrenze. Eine Anfrage für ein Fahrzeug über der Obergrenze wird mit
402 plan_vehicle_limitbeantwortet, bevor etwas berechnet wird. Ein in diesem Monat bereits ausgeliefertes Fahrzeug ist immer erlaubt, sodass ein Live-Raster nie kaputtgeht.
Rendering-Fehler (502) werden automatisch erstattet, gleichzeitige Anfragen für dieselbe neue Variante teilen sich eine Generierung, und ein einfacher GET kann gefahrlos wiederholt werden. Das SDK wiederholt 429 und 503 automatisch unter Beachtung von Retry-After und wiederholt niemals einen 402.
import { CarImageClient, CarImageError } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
// One make, one model year: the ids are what you store and what you render by.
const { data } = await client.vehicles({ year: 2024, makeId: 584 });
const queue = data.models.flatMap((model) => (model.id ? [model.id] : []));
// Four in flight stays well inside 120 requests a minute, and a cold render
// that takes a few seconds never holds up the rest of the batch.
await Promise.all(
Array.from({ length: 4 }, async () => {
for (let id = queue.shift(); id; id = queue.shift()) {
try {
const image = await client.getImage({
vehicle: id, view: "front-3-4", color: "silver",
width: 800, height: 600, fit: "contain", trim: true, padding: 8, format: "webp",
});
await store(id, image.bytes, image.etag); // your object store or CDN, keyed by vehicle id
} catch (error) {
// 429 and 503 were already retried with Retry-After; a 404 is a vehicle to request, not a failure.
if (error instanceof CarImageError && error.status === 404) continue;
throw error;
}
}
})
);Schritt 4: Ausliefern über Ihr CDN oder signierte URLs
Es gibt zwei gute Wege, das Bild vor die Augen von Kaufinteressenten zu bringen, und Katalog-Integrationen nutzen beide:
- Speichern Sie die Bytes. Legen Sie jedes Rendering unter seiner Fahrzeug-ID in Ihrem eigenen Object Storage oder CDN ab und liefern Sie es aus wie jedes andere Asset. Das ist die richtige Lösung für ein Raster, das millionenfach gerendert wird: kein Aufruf bei uns pro Ladevorgang, und die Lizenz in einem kostenpflichtigen Plan deckt Kopien ab, die Sie zur Bereitstellung Ihres Produkts aufbewahren.
- Signierte URLs erstellen.
POST /api/v1/image-urlsnimmt bis zu fünfzig Bilder pro Aufruf entgegen, berechnet bei der Erstellung je einen Credit und liefert URLs ohne Schlüssel, die der Browser direkt lädt. Setzen Siettl_secondsauf bis zu sieben Tage; fügen Sierenew: truefür Inseratsseiten hinzu, die länger online bleiben (ein weiterer Credit pro Bild für jede Woche, in der es tatsächlich aufgerufen wird, keiner für Wochen ohne Aufrufe). Beschränken Sie den Schlüssel im Dashboard auf die Origins Ihrer Website, dann antwortet eine anderswohin kopierte URL mit403.
Senden Sie beim Erstellen in Batches einen Idempotency-Key pro Seite mit Inseraten: Ein wiederholter Aufruf mit demselben Schlüssel und demselben Body gibt die erste Antwort erneut zurück und kostet nichts.
{
"images": [
{
"vehicle": "veh_78qtwrgh37bkr",
"view": "front-3-4",
"color": "silver",
"width": 800,
"height": 600,
"fit": "contain",
"trim": true,
"padding": 8,
"format": "auto"
},
{
"vehicle": "veh_b4bf090fyb53k",
"view": "front-3-4",
"color": "silver",
"width": 800,
"height": 600,
"fit": "contain",
"trim": true,
"padding": 8,
"format": "auto"
}
]
}Authorization-Header. Ein Schlüssel, der auf eine Seite gelangt, kann von jedem gelesen werden, der die Entwicklertools öffnet.Wenn ein Fahrzeug nicht im Katalog ist
Suchen Sie zuerst: GET /api/v1/vehicles?q= findet ein Modell über Name, Präfix oder unscharfe Suche, und POST /api/v1/images/resolve wandelt eine Freitext-Phrase in exakte Parameter um, mit einem Konfidenzwert, auf dessen Grundlage Sie handeln können. Was danach noch fehlt, gehört auf das Anfrage-Board: POST /api/v1/requests legt die Anfrage an oder stimmt, wenn bereits eine offene Anfrage für dieselbe Marke, dasselbe Modell und Jahr existiert, für diese ab, statt sie zu duplizieren. Der Katalog selbst ist Open Source, und Ergänzungen stehen allen Kunden zur Verfügung.
CLI, SDK und MCP
Die gesamte Pipeline läuft ganz ohne Code über die CLI – so wurde mehr als ein Katalog auf der API befüllt:
# One file per vehicle id. Re-running skips what exists, so a stopped backfill resumes.
while read -r id; do
[ -f "cdn/$id.webp" ] || npx @meterapp/car-image get --vehicle "$id" \
--view front-3-4 --color silver --width 800 --height 600 --fit contain \
--trim --padding 8 --format webp --out "cdn/$id.webp"
done < vehicle-ids.txt
# Or fifty signed URLs in one call, for a page of listings the browser loads itself.
npx @meterapp/car-image url --batch listings.json --ttl 604800 --renew --json- SDK:
client.vehicles({ year, makeId })zum Auflisten,client.getImage({ vehicle, … })zum Rendern,client.createImageUrls(…)für Stapel signierter URLs. Alle Parameter finden Sie in der Bild-Dokumentation. - MCP: Ein mit
https://carimage.dev/api/mcpverbundener Assistent hat denselben Katalog (search_vehicles,resolve_vehicle) und dieselben Renderings (get_car_image,create_car_image_urls). Das ist das richtige Werkzeug, wenn jemand ein Fahrzeug von Hand im Katalog ergänzt, und das falsche für ein Backfill von Tausenden: Nutzen Sie dafür die CLI oder das SDK und behalten Sie den Assistenten für die Nachzügler. Die Einrichtung finden Sie auf der MCP-Seite.
Häufig gestellte Fragen
- Welche Ansicht sollte ein Inserat verwenden?
- front-3-4 ist die Standardansicht und der klassische Hero-Winkel, Fahrzeugfront nach links. Jeder Winkel der linken Fahrzeugseite hat ein -right-Gegenstück (front-3-4-right, side-right, rear-3-4-right), sodass eine Karte ins Layout hinein statt aus ihm heraus blicken kann. Wählen Sie einen Winkel und bleiben Sie dabei: Die Einheitlichkeit über alle Inserate hinweg macht den Großteil des Nutzens aus.
- Was kostet ein Katalog mit 20.000 Inseraten?
- 20.000 Credits, einmalig: ein Credit pro Fahrzeug und Ansicht, danach dauerhaft im Cache, und die erneute Prüfung mit If-None-Match ist kostenlos. Pro enthält 25.000 Credits pro Monat, erlaubt aber nur 2.500 verschiedene Fahrzeuge pro Monat; Business erlaubt 15.000, und Enterprise hat keine Obergrenze. Ein Backfill mit mehr neuen Fahrzeugen, als der Plan erlaubt, wird mit 402 plan_vehicle_limit beantwortet, und es wird nichts berechnet. Dimensionieren Sie den Plan daher nach dem Katalog, nicht nach den Credits.
- Können wir die Dateien auf unserem eigenen CDN speichern?
- Ja. Ein kostenpflichtiger Tarif lizenziert die Renderings für die Nutzung in Ihrem Produkt, einschließlich Kopien auf Ihren eigenen Servern oder Ihrem CDN, ohne Namensnennung, und die darüber gelieferten Renderings behalten diese Lizenz auch nach Ende des Tarifs. Nicht erlaubt ist, sie als eigenständige Bibliothek, als Feed oder als Datensatz anzubieten. Für das Rendern des nächsten Modelljahrs ist ein aktiver Tarif erforderlich.
- Was passiert, wenn der Katalog ein neues Modelljahr erhält?
- Der Katalog @meterapp/vehicle-db wird aktualisiert, sobald Quellen neue Daten veröffentlichen, und bestehende IDs ändern sich nie. Listen Sie das neue Baujahr auf, rendern Sie die IDs, die Sie noch nicht haben, und lassen Sie den Rest unverändert. Anfragen liefern ein ETag; eine wiederholte Anfrage mit If-None-Match liefert 304 und kostet nichts.
- Warum geben einige unserer Marken- und Modell-Strings 404 zurück?
- Weil sie von einer anderen Datenbank geschrieben wurden. Der Katalog akzeptiert Namen, Slugs und viele Varianten von Typbezeichnungen und Karosserieformen, aber eine Zeichenkette, die er nie gesehen hat, ergibt einen 404 vehicle_not_found (kostenlos, aber ein verschwendeter Roundtrip). Listen Sie den Katalog auf und speichern Sie seine IDs, statt Ihre eigenen Zeichenketten zu übersetzen, und suchen oder lösen Sie die Nachzügler auf.