Het probleem: een advertentie heeft een afbeelding nodig voordat de verkoper er een uploadt
Een advertentiesite of autocatalogus staat of valt met zijn raster. Elke kaart heeft een afbeelding nodig zodra de advertentie bestaat, en de eigen foto van de verkoper komt later, als die al komt, vanuit een willekeurige hoek op een willekeurige oprit. Stockfotografie heeft het omgekeerde probleem: er bestaat een gelicentieerde persfoto voor de populaire auto's in de populaire jaren, vanuit de hoek en tegen de achtergrond die de fabrikant koos, en voor niets anders. Een catalogus die loopt van 1990 tot het huidige modeljaar heeft tienduizenden gaten.
Wat de teams achter deze sites willen, is saai op de beste manier: één studioafbeelding per modeljaar, voor allemaal dezelfde hoek en dezelfde lakkleur, transparant zodat die op elke kaartachtergrond past, en klein genoeg om tienduizend keer per dag te serveren.
Hoe een catalogusintegratie eruitziet
De catalogusbuilds die vandaag op de API draaien, hebben allemaal dezelfde opzet, of het nu gaat om een advertentiesite die haar aanbod vult, een autokoop-app die vergelijkingskaarten rendert of een mobiele app die één hero-afbeelding per voertuig toont:
- Eerst een backfill, dan een gestaag stroompje. Duizenden renders in een paar uur om de catalogus te dekken, daarna een handvol per dag naarmate er nieuwe modeljaren en aanvragen binnenkomen.
- Eén hoek, één lakkleur of een klein palet. De meesten kiezen
front-3-4ofsidein zilver of wit. Een winkelcatalogus waarin de gebruiker een kleur kiest, rendert een paar presets per voertuig; elke preset is één voertuigafbeelding extra, geen nieuwe pipeline. - WebP in een vast kader. 512 tot 1024 pixels breed, geleverd in precies het kader dat de kaart gebruikt, zodat de front-end nooit hoeft bij te snijden of zwarte balken hoeft toe te voegen.
- Opgeslagen op voertuig-id. De renders komen terecht in de eigen object store of CDN van het team, of de pagina's laden signed URLs; hoe dan ook is de sleutel de stabiele
veh_…-id uit de catalogus, niet een merk-model-jaar-string.
De rest van deze pagina beschrijft die pipeline stap voor stap, met de code. Er wordt uitgegaan van een API-sleutel met de scope images:read en een abonnement dat past bij de omvang van de catalogus (zie de FAQ).
Stap 1: doorloop de catalogus op id, nooit op spelling
De meest voorkomende fout bij het bouwen van een catalogus is niet het renderen, maar de spelling. Een build die merk- en modelnamen uit een andere database genereert, kan het grootste deel van zijn verzoeken kwijt zijn aan 404 vehicle_not_found: gratis, maar telkens een verspilde round trip, en een raster vol gaten. De oplossing: laat de catalogus u vertellen wat er bestaat. GET /api/v1/vehicles is gratis, vereist geen sleutel, en elk model dat het retourneert heeft een stabiele voertuig-id (veh_ plus 13 tekens) die nooit verandert tussen deploys of catalogusreleases. Sla die id op bij de advertentie en render op basis daarvan.
# 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 toont elk bouwjaar van één model met een id per jaar, en ?q= zoekt op vrije tekst wanneer een verkoper een naam typt. Dezelfde catalogus is gepubliceerd als het open pakket @meterapp/vehicle-db, zodat dropdowns en validatie offline tegen exact dezelfde lijst kunnen draaien. Hoe dan ook heeft u aan het eind van deze stap een lijst met ids, en hoeft niets verderop in de keten ooit nog een auto uit te spellen.
Advertenties die hun auto's al benoemen in de spelling van een andere database, worden in één call gekoppeld: POST /api/v1/vehicles/check leest er maximaal 2.000 op dezelfde manier als een render dat zou doen en beantwoordt elk ervan met het voertuig-id, de dichtstbijzijnde echte voertuigen als de catalogus zo'n auto niet heeft, of een misser die u in elke weergave en kleur kunt overslaan. car-image check listings.csv --out checked.csv schrijft de id's terug in het bestand. Het is gratis, en het voorkomt dat u de gaten één 404 tegelijk ontdekt.
Stap 2: één requeststructuur voor elke advertentie
Bepaal de kaart één keer en leg die vast in de querystring. Alles na vehicle= hieronder is een keuze over de framing die u één keer maakt voor de hele catalogus:
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=containlevert precies 800×600 op, met de hele auto zichtbaar en transparante opvulling eromheen, zodat elke kaart hetzelfde canvas heeft.fit=covervult in plaats daarvan het vak en snijdt vanuit het midden bij.trim=1&padding=8snijdt bij tot de omtrek van de auto voordat er wordt ingepast, met 8% van de langste zijde als marge, zodat een Suburban en een Miata het kader op dezelfde manier vullen in plaats van in het bronbeeld te zweven.color=silveris de standaard en de veiligste neutrale kleur; elk van de 15 presets of elke hexwaarde telt hetzelfde: één voertuigafbeelding.format=webpis de kleinste codering met een verliesvrij alfakanaal. Wanneer een browser de URL rechtstreeks laadt (ondertekende URL's, stap 4), kiestformat=autoper client tussen WebP en PNG.
Het antwoord bevat ETag, X-Image-Source (cache of generated) en X-Vehicle-Id, het catalogusvoertuig dat is geleverd. Bewaar de ETag bij het bestand: een latere request met If-None-Match krijgt 304 als antwoord en kost niets. Zo blijft een nachtelijke hersynchronisatie gratis.
Stap 3: spreid de backfill binnen de limieten
Drie limieten bepalen een backfill, en elk ervan maakt zich kenbaar in headers in plaats van stilletjes te falen:
- Verzoeken per minuut. 120 per sleutel bij Free en Pro, 600 bij Business, 1.200 bij Enterprise, plus een limiet per account over alle sleutels heen. Elke geauthenticeerde respons bevat
RateLimit-Remaining; een429bevatRetry-After. Vier tot acht gelijktijdige verzoeken is ruim voldoende. - Nieuwe voertuigweergaven. Het eerste verzoek voor een voertuigweergave die we nog niet hebben voorbereid, duurt even terwijl we die voorbereiden, zodat die in elke kleur kan worden getoond; elk volgend verzoek, in welke kleur ook, wordt vanuit de cache geleverd. Het voorbereiden van nieuwe weergaven gaat af van het aandeel van het account in het dagelijkse renderbudget (2% bij Free, 10% bij Pro, 25% bij Business, 50% bij Enterprise). Daarboven geeft een nieuwe render
429terug metcode: account_generation_capenreset_at(middernacht UTC), wordt er niets in rekening gebracht en worden gecachete afbeeldingen gewoon geleverd. Een grote backfill duurt een paar avonden, niet één. - Voertuigafbeeldingen per maand. Free bevat er 100, Pro 2.500, Business 10.000; over Enterprise wordt onderhandeld. Boven dat tegoed krijgt een verzoek om een afbeelding die deze maand nog niet is geteld het antwoord
402(plan_image_limit) en wordt er niets geteld; een afbeelding die deze maand al is geteld, wordt altijd geleverd, zodat een live raster nooit breekt.
Mislukte renders (502) geven hun voertuigafbeelding automatisch terug, gelijktijdige verzoeken voor dezelfde nieuwe variant delen één render, en een gewone GET kan veilig opnieuw worden geprobeerd. De SDK probeert 429 en 503 automatisch opnieuw, met inachtneming van Retry-After, en probeert een 402 nooit opnieuw.
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;
}
}
})
);Stap 4: lever het via uw CDN of via signed URL's
Er zijn twee goede manieren om de afbeelding bij een koper te krijgen, en bij het opbouwen van catalogi worden ze allebei gebruikt:
- Sla de bytes op. Schrijf elke render naar uw eigen objectopslag of CDN onder het voertuig-id en lever hem uit zoals elk ander asset. Dit is de juiste keuze voor een raster dat miljoenen keren wordt weergegeven: geen aanroep naar ons per keer laden, en de licentie van een betaald abonnement dekt kopieën die u bewaart om uw product te bedienen.
- Maak ondertekende URL’s aan.
POST /api/v1/image-urlsverwerkt tot vijftig afbeeldingen per aanroep, telt de afbeelding van elke URL als voertuigafbeelding van de maand wanneer de URL wordt aangemaakt, en geeft URL’s zonder sleutel terug die de browser direct laadt. Stelttl_secondsin op maximaal zeven dagen; voegrenew: truetoe voor advertentiepagina’s die langer online blijven (een verlenging telt de afbeelding één keer in de maand waarin die plaatsvindt, en niets extra als de afbeelding die maand al was geteld). Beperk de sleutel in het dashboard tot de origins van uw site, en een URL die elders wordt gekopieerd, geeft403terug.
Stuur een Idempotency-Key per pagina advertenties mee wanneer u in batches aanmaakt: een herhaalde aanroep met dezelfde sleutel en body geeft het eerste antwoord opnieuw terug en telt niets opnieuw mee.
{
"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-headers. Een sleutel die op een pagina terechtkomt, kan worden gelezen door iedereen die de ontwikkelaarstools opent.Als een voertuig niet in de catalogus staat
Zoek eerst: GET /api/v1/vehicles?q= vindt een model op naam, prefix of fuzzy match, en POST /api/v1/images/resolve zet een vrije tekst om in exacte parameters met een betrouwbaarheidsscore waarop u kunt handelen. Wat daarna nog ontbreekt, hoort op het verzoekenbord: POST /api/v1/requests maakt het verzoek aan of, als er al een open verzoek is voor hetzelfde merk, model en bouwjaar, stemt daarop in plaats van een duplicaat te maken. De catalogus zelf is open source en toevoegingen komen beschikbaar voor elke klant.
CLI, SDK en MCP
De hele pipeline draait vanuit de CLI zonder enige code, en zo is meer dan één catalogus op de API gevuld:
# 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 })om op te sommen,client.getImage({ vehicle, … })om te renderen,client.createImageUrls(…)voor batches signed URL's. Zie de documentatie over afbeeldingen voor alle parameters. - MCP: een assistent die verbonden is met
https://carimage.dev/api/mcpheeft dezelfde catalogus (search_vehicles,resolve_vehicle) en dezelfde renders (get_car_image,create_car_image_urls). Dat is het juiste hulpmiddel voor een curator die handmatig een voertuig toevoegt, en het verkeerde voor een backfill van duizenden voertuigen: gebruik daarvoor de CLI of de SDK en bewaar de assistent voor de achterblijvers. De installatie staat op de MCP-pagina.
Veelgestelde vragen
- Welk aanzicht past het best bij een advertentie?
- front-3-4 is de standaard en de klassieke hero-hoek, met de neus naar links. Elke hoek van de linkerkant van de auto heeft een -right-tegenhanger (front-3-4-right, side-right, rear-3-4-right), zodat een kaart naar de lay-out toe kan kijken in plaats van ervan weg. Kies er één en houd die aan: de consistentie tussen advertenties is het grootste deel van de waarde.
- Wat kost een catalogus van 20.000 advertenties?
- 20.000 voertuigafbeeldingen per maand als elke advertentie één voertuig in één lakkleur en aanzicht toont, elk zo vaak geleverd als nodig in elk formaat; advertenties met hetzelfde voertuig, dezelfde lakkleur en hetzelfde aanzicht zijn één afbeelding, en opnieuw controleren met If-None-Match is gratis. Pro bevat 2.500 voertuigafbeeldingen per maand en Business 10.000, dus een catalogus van die omvang valt onder Enterprise, of onder een eenmalig Image Pack dat u houdt en voorgoed zelf host. Een backfill boven het tegoed geeft 402 terug en telt niets.
- Kunnen we de bestanden op ons eigen CDN bewaren?
- Ja. Een betaald abonnement geeft u een licentie om de renders in uw product te gebruiken, kopieën op uw eigen servers of CDN inbegrepen, zonder naamsvermelding, zolang het abonnement actief is; binnen 30 dagen na het einde worden opgeslagen kopieën verwijderd (de afbeeldingen van een Image Pack mag u voorgoed houden). Wat de licentie niet toestaat, is ze aanbieden als losse bibliotheek, feed of dataset. Met een actief abonnement wordt ook het volgende modeljaar gerenderd.
- Wat gebeurt er als de catalogus een nieuw modeljaar krijgt?
- De @meterapp/vehicle-db-catalogus wordt bijgewerkt zodra bronnen publiceren, en bestaande id's veranderen nooit. Vraag het nieuwe bouwjaar op, render de id's die u nog niet hebt en laat de rest ongemoeid. Verzoeken hebben een ETag; een herhaald verzoek met If-None-Match krijgt 304 als antwoord en kost niets.
- Waarom geven sommige van onze merk- en modelnamen een 404 terug?
- Omdat ze door een andere database zijn gespeld. De catalogus accepteert namen, slugs en veel varianten van badges en carrosserievormen, maar een tekenreeks die hij nooit heeft gezien, levert een 404 vehicle_not_found op (gratis, maar een verspilde round trip). Doorloop de catalogus en sla de id's ervan op in plaats van uw eigen tekenreeksen te vertalen, en zoek of resolve de achterblijvers.