Problema: un anunț are nevoie de o imagine înainte ca vânzătorul să încarce una
Un site de anunțuri sau un catalog auto trăiește sau moare prin grila sa. Fiecare card are nevoie de o imagine din momentul în care apare anunțul, iar fotografia vânzătorului vine mai târziu, dacă vine, dintr-un unghi oarecare, pe o alee oarecare. Fotografiile de stoc au problema opusă: există o fotografie de presă licențiată pentru mașinile populare din anii populari, în unghiul și pe fundalul alese de producător, și pentru nimic altceva. Un catalog care acoperă perioada de la 1990 până la anul-model curent are zeci de mii de goluri.
Ce își doresc echipele care construiesc aceste site-uri este plictisitor în cel mai bun sens: o imagine de studio pentru fiecare an de model, același unghi și aceeași culoare pentru toate, transparentă ca să se potrivească pe orice fundal al cardului, suficient de mică pentru a fi servită de zece mii de ori pe zi.
Cum arată o integrare de catalog
Toate cataloagele construite astăzi pe API au aceeași structură, fie că este vorba de un site de anunțuri care își populează listările, de o aplicație de cumpărare de mașini care își randează cardurile de comparație sau de o aplicație mobilă care afișează o imagine principală pentru fiecare vehicul:
- O completare inițială, apoi un flux constant. Mii de randări în câteva ore pentru a acoperi catalogul, apoi câteva pe zi, pe măsură ce apar noi ani de model și noi solicitări.
- Un singur unghi, o singură culoare sau o paletă restrânsă. Majoritatea aleg
front-3-4sausideîn argintiu sau alb. Un catalog de cumpărături care îi permite utilizatorului să aleagă o culoare generează câteva culori predefinite per vehicul; fiecare înseamnă încă o imagine de vehicul, nu un nou flux de lucru. - WebP într-un cadru fix. Între 512 și 1024 pixeli lățime, livrat exact în cadrul folosit de card, astfel încât front-end-ul nu decupează niciodată imaginea și nu adaugă benzi goale.
- Stocate după id-ul vehiculului. Randările ajung în spațiul de stocare de obiecte sau CDN-ul propriu al echipei, ori paginile încarcă URL-uri semnate; în ambele cazuri, cheia este id-ul stabil
veh_…din catalog, nu un șir marcă-model-an.
Restul acestei pagini prezintă acest pipeline, pas cu pas, împreună cu codul. Presupune o cheie API cu scope-ul images:read și un plan dimensionat pentru catalog (consultați secțiunea de întrebări frecvente).
Pasul 1: parcurgeți catalogul după id, niciodată după denumire
Cea mai frecventă problemă la construirea unui catalog nu este randarea, ci ortografia. O implementare care generează denumirile mărcilor și modelelor dintr-o altă bază de date își poate cheltui majoritatea cererilor pe 404 vehicle_not_found: gratuite, dar fiecare înseamnă un drum dus-întors irosit, iar grila rămâne cu goluri. Soluția este să lăsați catalogul să vă spună ce există. GET /api/v1/vehicles este gratuit, nu necesită cheie, iar fiecare model returnat are un id de vehicul stabil (veh_ plus 13 caractere) care nu se schimbă niciodată între deploy-uri sau versiuni ale catalogului. Stocați acest id în anunț și randați pe baza lui.
# 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 listează fiecare an al unui model, cu câte un id pentru fiecare an, iar ?q= caută text liber când un vânzător tastează un nume. Același catalog este publicat ca pachetul deschis @meterapp/vehicle-db, astfel încât listele derulante și validarea pot rula offline pe aceeași listă. În ambele cazuri, la finalul acestui pas aveți o listă de id-uri și nimic din aval nu mai scrie vreodată numele unei mașini.
Anunțurile care își denumesc deja mașinile după grafia altei baze de date sunt mapate într-un singur apel: POST /api/v1/vehicles/check citește până la 2.000 dintre ele așa cum ar face-o o randare și răspunde pentru fiecare cu id-ul vehiculului, cu cele mai apropiate vehicule reale atunci când catalogul nu are o astfel de mașină sau cu o ratare de omis în toate unghiurile și culorile. car-image check listings.csv --out checked.csv scrie id-urile înapoi în fișier. Este gratuit și înlocuiește descoperirea lacunelor câte un 404 pe rând.
Pasul 2: o singură structură de cerere pentru fiecare anunț
Stabiliți cardul o singură dată și codificați-l în șirul de interogare. Tot ce urmează după vehicle= mai jos este o decizie de încadrare pe care o luați o singură dată pentru întregul catalog:
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=containlivrează exact 800×600, cu întreaga mașină vizibilă și margini transparente în jur, astfel încât fiecare card are aceeași suprafață.fit=coverumple în schimb caseta și decupează centrat.trim=1&padding=8decupează după conturul mașinii înainte de încadrare, cu o margine de 8% din latura sa mai lungă, astfel încât un Suburban și o Miata umplu cadrul la fel, în loc să plutească în cadrul sursă.color=silvereste valoarea implicită și cea mai sigură variantă neutră; oricare dintre presetări (15) sau orice cod hex se contorizează la fel, ca o singură imagine de vehicul.format=webpeste cea mai compactă codificare cu canal alfa fără pierderi. Când un browser încarcă direct URL-ul (URL-uri semnate, pasul 4),format=autonegociază WebP sau PNG pentru fiecare client.
Răspunsul include ETag, X-Image-Source (cache sau generated) și X-Vehicle-Id, vehiculul din catalog care a fost servit. Păstrați ETag-ul împreună cu fișierul: o cerere ulterioară cu If-None-Match primește răspunsul 304 și nu costă nimic, astfel o resincronizare nocturnă rămâne gratuită.
Pasul 3: eșalonați completarea retroactivă în limitele permise
Trei limite determină o completare retroactivă a catalogului și fiecare dintre ele se anunță prin headere, în loc să eșueze în tăcere:
- Cereri pe minut. 120 per cheie în planurile Free și Pro, 600 în Business, 1.200 în Enterprise, plus o limită per cont pentru toate cheile. Fiecare răspuns autentificat conține
RateLimit-Remaining; un429conțineRetry-After. Patru până la opt cereri simultane sunt suficiente. - Vederi noi ale vehiculelor. Prima solicitare pentru o vedere a unui vehicul pe care nu am pregătit-o încă durează puțin, cât timp o pregătim pentru a putea fi afișată în orice culoare; fiecare solicitare ulterioară, în orice culoare, este servită din cache. Pregătirea vederilor noi consumă din cota contului din bugetul zilnic de randare (2% pe Free, 10% pe Pro, 25% pe Business, 50% pe Enterprise). Peste această cotă, o randare nouă răspunde cu
429, cucode: account_generation_capșireset_at(miezul nopții UTC), nu se taxează nimic, iar imaginile din cache continuă să fie servite. Un import masiv al datelor existente durează câteva seri, nu una singură. - Imagini de vehicule pe lună. Free include 100, Pro 2.500, Business 10.000; Enterprise se negociază. Peste această alocare, o solicitare pentru o imagine încă necontorizată luna aceasta primește răspunsul
402(plan_image_limit) și nu contorizează nimic; o imagine deja contorizată luna aceasta se livrează întotdeauna, astfel încât o grilă live nu se strică niciodată.
Eșecurile de randare (502) își restituie automat imaginea de vehicul în alocare, solicitările simultane pentru aceeași variantă nouă împart o singură randare, iar un GET simplu poate fi reîncercat în siguranță. SDK-ul reîncearcă automat în cazul 429 și 503, respectând Retry-After, și nu reîncearcă niciodată un 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;
}
}
})
);Pasul 4: livrați-o din CDN-ul dumneavoastră sau prin URL-uri semnate
Există două modalități bune de a pune imaginea în fața cumpărătorului, iar implementările de catalog le folosesc pe amândouă:
- Stocați octeții. Salvați fiecare randare în propriul spațiu de stocare de obiecte sau CDN, sub id-ul vehiculului, și serviți-o ca pe orice alt fișier. Aceasta este soluția potrivită pentru o grilă afișată de milioane de ori: niciun apel către noi la fiecare încărcare, iar licența unui plan plătit acoperă copiile pe care le păstrați pentru a vă deservi produsul.
- Emiteți URL-uri semnate.
POST /api/v1/image-urlsacceptă până la cincizeci de imagini per apel, contorizează imaginea fiecărui URL ca imagine de vehicul a lunii în momentul emiterii și returnează URL-uri fără cheie pe care browserul le încarcă direct. Setațittl_secondsla cel mult șapte zile; adăugațirenew: truepentru paginile de anunțuri care rămân online mai mult de atât (o reînnoire contorizează imaginea o dată în luna în care are loc și nimic în plus dacă a fost deja contorizată în acea lună). Restricționați cheia la originile site-ului dumneavoastră din panoul de control, iar un URL copiat în altă parte va răspunde cu403.
Trimiteți un Idempotency-Key pentru fiecare pagină de anunțuri când generați URL-uri în loturi: un apel reîncercat cu aceeași cheie și același corp redă primul răspuns și nu mai contorizează nimic.
{
"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. O cheie care ajunge într-o pagină poate fi citită de oricine deschide instrumentele pentru dezvoltatori.Când un vehicul nu se află în catalog
Căutați mai întâi: GET /api/v1/vehicles?q= găsește un model după nume, prefix sau potrivire aproximativă, iar POST /api/v1/images/resolve transformă o expresie liberă în parametri exacți, cu un nivel de încredere pe baza căruia puteți acționa. Ce lipsește în continuare își are locul pe panoul de solicitări: POST /api/v1/requests creează solicitarea sau, când există deja una deschisă pentru aceeași marcă, același model și an, o votează în loc să o dubleze. Catalogul în sine este open source, iar completările ajung la toți clienții.
CLI, SDK și MCP
Întregul pipeline rulează din CLI, fără niciun cod, iar așa au fost populate inițial mai multe cataloage care folosesc API-ul:
# 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 })pentru enumerare,client.getImage({ vehicle, … })pentru randare,client.createImageUrls(…)pentru loturi de URL-uri semnate. Consultați documentația pentru imagini pentru toți parametrii. - MCP: un asistent conectat la
https://carimage.dev/api/mcpare același catalog (search_vehicles,resolve_vehicle) și aceleași randări (get_car_image,create_car_image_urls), ceea ce îl face instrumentul potrivit pentru un curator care adaugă manual un vehicul și cel nepotrivit pentru o completare retroactivă de mii de vehicule: pentru aceasta folosiți CLI-ul sau SDK-ul și păstrați asistentul pentru cazurile rămase. Configurarea este descrisă pe pagina MCP.
Întrebări frecvente
- Ce unghi ar trebui să folosească un anunț?
- front-3-4 este unghiul implicit și unghiul principal clasic, cu botul spre stânga. Fiecare unghi al părții stângi a mașinii are un geamăn -right (front-3-4-right, side-right, rear-3-4-right), astfel încât un card poate fi orientat spre interiorul paginii în loc de spre exterior. Alegeți unul și păstrați-l: consecvența între anunțuri reprezintă cea mai mare parte a valorii.
- Cât costă un catalog de 20.000 anunțuri?
- 20.000 de imagini de vehicule pe lună dacă fiecare anunț prezintă un vehicul într-o singură culoare și vedere, fiecare livrată de câte ori este nevoie, la orice dimensiune; anunțurile care au în comun vehiculul, culoarea și vederea reprezintă o singură imagine, iar reverificarea cu If-None-Match este gratuită. Pro include 2.500 de imagini de vehicule pe lună, iar Business 10.000, așa că un catalog de această dimensiune înseamnă Enterprise sau un Image Pack cu plată unică, pe care îl păstrați și îl găzduiți singur, definitiv. O completare retroactivă peste limită primește răspunsul 402 și nu se contorizează nimic.
- Putem păstra fișierele pe propriul nostru CDN?
- Da. Un plan plătit vă acordă licența de a folosi randările în produsul dumneavoastră, inclusiv copii pe propriile servere sau CDN, fără atribuire, cât timp planul este activ; în termen de 30 de zile de la încheierea acestuia, copiile stocate sunt șterse (imaginile dintr-un Image Pack pot fi păstrate definitiv). Licența nu permite oferirea lor ca bibliotecă, flux sau set de date de sine stătător. Un plan activ este cel care vă aduce randările pentru următorul an de model.
- Ce se întâmplă când în catalog apare un nou an de model?
- Catalogul @meterapp/vehicle-db este actualizat pe măsură ce sursele publică date, iar id-urile existente nu se schimbă niciodată. Enumerați noul an, randați id-urile pe care nu le aveți încă și lăsați restul neschimbat. Cererile au un ETag; o cerere repetată cu If-None-Match primește răspunsul 304 și nu costă nimic.
- De ce unele dintre șirurile noastre de marcă și model returnează 404?
- Pentru că au fost scrise după o altă bază de date. Catalogul acceptă nume, slug-uri și multe variante de embleme și tipuri de caroserie, dar un șir pe care nu l-a mai întâlnit primește un 404 vehicle_not_found (gratuit, dar o cerere irosită). Enumerați catalogul și stocați id-urile acestuia în loc să vă traduceți propriile șiruri, iar pentru cazurile rămase folosiți căutarea sau rezolvarea.