Car Image API

Zákazníci · Inzertní portály a katalogy pro nákup aut

Jak přidat ke každému inzerátu na tržišti studiový obrázek auta

Jak inzertní weby a katalogy vozů vykreslují jeden hlavní obrázek pro každý modelový rok v měřítku celého katalogu: ID vozidel, kompozice záběru, tempo požadavků, cachování a kód.

Problém: inzerát potřebuje obrázek dřív, než ho prodejce nahraje

Inzertní web nebo katalog vozů stojí a padá se svou mřížkou. Každá karta potřebuje obrázek ve chvíli, kdy inzerát vznikne, a vlastní fotka prodejce dorazí později, pokud vůbec, z náhodného úhlu na náhodné příjezdové cestě. Fotobanky mají opačný problém: licencovaná tisková fotka existuje pro oblíbená auta z oblíbených ročníků, v úhlu a na pozadí, které zvolil výrobce, a pro nic jiného. Katalog pokrývající roky 1990 až aktuální modelový rok má desítky tisíc mezer.

To, co týmy budující tyto weby chtějí, je v tom nejlepším slova smyslu nudné: jeden studiový obrázek na modelový rok, stejný úhel a stejný lak pro všechny, průhledný, aby seděl na jakémkoli pozadí karty, a dost malý na to, aby se dal zobrazit desettisíckrát denně.

Jak vypadá integrace katalogu

Katalogy, které dnes na API běží, mají všechny stejnou podobu, ať už jde o inzertní web plnící své inzeráty, aplikaci pro nákup aut vykreslující srovnávací karty, nebo mobilní aplikaci zobrazující jeden hlavní obrázek ke každému vozidlu:

  • Nejprve hromadné doplnění, pak průběžný přísun. Tisíce renderů během několika hodin pro pokrytí katalogu, pak pár denně, jak přibývají nové modelové roky a požadavky.
  • Jeden úhel, jeden lak nebo krátká paleta. Většina volí front-3-4 nebo side ve stříbrné nebo bílé. Nákupní katalog, ve kterém si uživatel vybírá barvu, vykresluje pro každé vozidlo několik předvoleb; každá je jeden obrázek vozidla navíc, ne nový pracovní postup.
  • WebP v pevném rámečku. Šířka 512 až 1024 pixelů, dodáno přesně v rozměru, který karta používá, takže front-end nikdy nic neořezává ani nepřidává okraje.
  • Ukládáno podle ID vozidla. Rendery končí ve vlastním objektovém úložišti nebo CDN týmu, případně stránky načítají podepsané URL; v obou případech je klíčem stabilní katalogové ID veh_…, nikoli řetězec značka-model-rok.

Zbytek této stránky popisuje tento postup krok za krokem i s kódem. Předpokládá klíč API s oprávněním images:read a tarif dimenzovaný podle velikosti katalogu (viz FAQ).

Krok 1: procházejte katalog podle id, nikdy podle zápisu názvu

Nejčastější chybou při budování katalogu není renderování, ale pravopis. Sestavení, které generuje názvy značek a modelů z jiné databáze, může většinu požadavků promrhat na 404 vehicle_not_found: jsou zdarma, ale každý znamená zbytečný požadavek navíc a mřížku plnou děr. Řešením je nechat si od katalogu říct, co existuje. GET /api/v1/vehicles je zdarma, nevyžaduje klíč a každý model, který vrátí, nese stabilní ID vozidla (veh_ plus 13 znaků), které se nikdy nemění napříč nasazeními ani vydáními katalogu. Uložte toto ID k inzerátu a renderujte podle něj.

Projděte jeden modelový rok
# 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"
200 OK (zkráceno)
{
  "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 vypíše všechny ročníky jednoho modelu s id pro každý ročník a ?q= hledá volný text, když prodejce zadá název. Stejný katalog je publikován jako otevřený balíček @meterapp/vehicle-db, takže rozbalovací seznamy a validace mohou běžet offline nad identickým seznamem. Tak či onak, na konci tohoto kroku máte seznam id a nic v dalších krocích už nikdy nevypisuje název auta.

Inzeráty, které už auta pojmenovávají v zápisu jiné databáze, se namapují jedním voláním: POST /api/v1/vehicles/check jich přečte až 2 000 stejně, jako by to udělal render, a ke každému vrátí ID vozidla, nejbližší skutečná vozidla, pokud katalog takové auto nemá, nebo neshodu, kterou přeskočíte ve všech pohledech i barvách. car-image check listings.csv --out checked.csv zapíše ID zpět do souboru. Je to zdarma a nahrazuje to odhalování mezer po jednom 404.

Krok 2: jedna podoba požadavku pro všechny inzeráty

Rozhodněte o podobě karty jednou a zakódujte ji do řetězce dotazu. Vše za vehicle= níže je rozhodnutí o rámování, které uděláte jednou pro celý katalog:

Jeden obrázek inzerátu
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.webp
  • w=800&h=600&fit=contain vrátí přesně 800×600 s celým viditelným vozem a průhledným okrajem kolem, takže každá karta má stejné plátno. fit=cover naopak vyplní celý rámec a obrázek ořízne na střed.
  • trim=1&padding=8 před umístěním ořízne obrázek podle obrysu auta s okrajem 8 % jeho delší strany, takže Suburban i Miata vyplní rámeček stejně, místo aby se ztrácely v původním záběru.
  • color=silver je výchozí a nejbezpečnější neutrální volba; kterákoli z 15 předvoleb nebo libovolný hex se počítá stejně, jako jeden obrázek vozidla.
  • format=webp je nejmenší kódování s bezztrátovým alfa kanálem. Když prohlížeč načítá URL přímo (podepsané URL, krok 4), format=auto vyjedná WebP nebo PNG podle klienta.

Odpověď obsahuje ETag, X-Image-Source (cache nebo generated) a X-Vehicle-Id, tedy vozidlo z katalogu, které bylo doručeno. ETag si uložte spolu se souborem: pozdější požadavek s If-None-Match vrátí 304 a nic nestojí, a díky tomu je noční opětovná synchronizace zdarma.

Krok 3: rozvrhněte zpětné doplnění tak, aby nepřekročilo limity

Hromadné zpětné doplnění katalogu ovlivňují tři limity a každý z nich se ohlásí v hlavičkách, místo aby tiše selhal:

  • Požadavky za minutu. 120 na klíč v tarifech Free a Pro, 600 v tarifu Business, 1 200 v tarifu Enterprise, plus limit na účet napříč všemi klíči. Každá ověřená odpověď obsahuje RateLimit-Remaining; odpověď 429 obsahuje Retry-After. Čtyři až osm souběžných požadavků úplně stačí.
  • Nové pohledy na vozidla. První požadavek na pohled na vozidlo, který jsme ještě nepřipravili, chvíli trvá, protože ho připravujeme tak, aby se dal zobrazit v libovolné barvě; každý další požadavek, v jakékoli barvě, se obslouží z cache. Příprava nových pohledů čerpá z podílu účtu na denním rozpočtu vykreslování (2 % na Free, 10 % na Pro, 25 % na Business, 50 % na Enterprise). Po jeho vyčerpání odpoví nové vykreslení 429 s code: account_generation_cap a reset_at (půlnoc UTC), nic se neúčtuje a obrázky v cache se dál zobrazují. Velké hromadné doplnění zabere několik večerů, ne jeden.
  • Obrázky vozidel za měsíc. Free zahrnuje 100, Pro 2 500, Business 10 000; Enterprise se sjednává individuálně. Po vyčerpání limitu odpoví požadavek na obrázek, který tento měsíc ještě nebyl započítán, kódem 402 (plan_image_limit) a nic se nezapočítá; obrázek již tento měsíc započítaný se zobrazí vždy, takže živá mřížka se nikdy nerozbije.

Při selhání vykreslení (502) se obrázek vozidla automaticky vrátí do limitu, souběžné požadavky na stejnou novou variantu sdílejí jedno vykreslení a prostý GET lze bezpečně opakovat. SDK za vás opakuje 429 a 503 s ohledem na Retry-After a nikdy neopakuje 402.

Hromadné doplnění obrázků s možností obnovení pomocí TypeScript SDK
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;
      }
    }
  })
);

Krok 4: obrázek poskytujte z vlastní CDN nebo přes podepsané URL

Obrázek lze zákazníkovi zobrazit dvěma dobrými způsoby a katalogová řešení používají oba:

  • Ukládejte si data. Každý render zapište do vlastního úložiště objektů nebo CDN pod jeho ID vozidla a servírujte ho jako jakýkoli jiný asset. To je správná volba pro mřížku, která se zobrazí milionkrát: žádné volání k nám při každém načtení a licence u placeného tarifu pokrývá kopie, které si uchováváte pro provoz svého produktu.
  • Generujte podepsané URL. POST /api/v1/image-urls přijme až padesát obrázků na jedno volání, obrázek každé URL započítá jako obrázek vozidla daného měsíce v okamžiku vygenerování a vrátí URL bez klíče, které prohlížeč načítá přímo. Nastavte ttl_seconds až na sedm dní; pro stránky inzerátů, které existují déle, přidejte renew: true (obnovení započítá obrázek jednou v měsíci, do kterého spadne, a nic dalšího, pokud už byl v tom měsíci započítán). Omezte klíč na dashboardu na domény (origins) svého webu a URL zkopírovaná jinam odpoví 403.

Při hromadném vytváření posílejte Idempotency-Key pro každou stránku inzerátů: opakované volání se stejným klíčem a tělem zopakuje první odpověď a nic znovu nezapočítá.

listings.json — po padesáti, s rámováním jako v kroku 2
{
  "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"
    }
  ]
}

Když vozidlo není v katalogu

Nejprve hledejte: GET /api/v1/vehicles?q= najde model podle názvu, prefixu nebo přibližné shody a POST /api/v1/images/resolve převede volný text na přesné parametry s mírou jistoty, podle které se můžete rozhodnout. Co i potom chybí, patří na nástěnku požadavků: POST /api/v1/requests požadavek vytvoří, nebo pokud už pro stejnou značku, model a rok existuje otevřený, přidá mu hlas místo vytvoření duplicity. Samotný katalog je open source a doplňky se dostanou ke všem zákazníkům.

CLI, SDK a MCP

Celý proces běží z CLI úplně bez kódu, a právě tak bylo naplněno víc než jeden katalog na API:

npx @meterapp/car-image
# 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 }) pro výčet, client.getImage({ vehicle, … }) pro vykreslení, client.createImageUrls(…) pro dávky podepsaných URL. Všechny parametry najdete v dokumentaci k obrázkům.
  • MCP: asistent připojený k https://carimage.dev/api/mcp má stejný katalog (search_vehicles, resolve_vehicle) i stejné rendery (get_car_image, create_car_image_urls), což je správný nástroj pro kurátora, který přidává vozidlo ručně, a nevhodný pro hromadné doplnění tisíců vozidel: na to použijte CLI nebo SDK a asistenta si nechte na zbývající případy. Nastavení najdete na stránce MCP.

Často kladené otázky

Jaký úhel použít pro inzerát?
front-3-4 je výchozí a klasický hlavní úhel, příď vlevo. Každý úhel levé strany auta má dvojče -right (front-3-4-right, side-right, rear-3-4-right), takže karta může směřovat dovnitř rozvržení místo ven. Vyberte si jeden a držte se ho: jednotnost napříč inzeráty tvoří většinu hodnoty.
Kolik stojí katalog s 20 000 inzeráty?
20 000 obrázků vozidel měsíčně, pokud každý inzerát zobrazuje jedno vozidlo v jednom laku a pohledu, každý doručený tak často, jak je potřeba, v libovolné velikosti; inzeráty se stejným vozidlem, lakem a pohledem jsou jeden obrázek a opětovná kontrola pomocí If-None-Match je zdarma. Pro zahrnuje 2 500 obrázků vozidel měsíčně a Business 10 000, takže katalog této velikosti je pro Enterprise, nebo pro jednorázový Image Pack, který si ponecháte a natrvalo hostujete sami. Doplnění nad rámec limitu vrátí 402 a nic se nezapočítá.
Můžeme mít soubory na vlastní CDN?
Ano. Placený tarif licencuje rendery k použití ve vašem produktu, včetně kopií na vašich vlastních serverech nebo CDN, bez uvedení autora, po dobu, kdy je tarif aktivní; do 30 dnů po jeho skončení se uložené kopie smažou (obrázky z balíčku Image Pack si můžete ponechat natrvalo). Licence však neumožňuje nabízet je jako samostatnou knihovnu, feed nebo datovou sadu. Rendery dalšího modelového roku získáte jen s aktivním tarifem.
Co se stane, když v katalogu přibude nový modelový rok?
Katalog @meterapp/vehicle-db se aktualizuje průběžně, jak zdroje zveřejňují data, a stávající id se nikdy nemění. Projděte nový ročník, vyrenderujte id, která ještě nemáte, a zbytek nechte být. Požadavky pracují s ETagem; opakovaný požadavek s If-None-Match vrátí 304 a nic nestojí.
Proč se některé naše řetězce značek a modelů vracejí s chybou 404?
Protože je zapsala jiná databáze. Katalog přijímá názvy, slugy a mnoho variant označení a typů karoserie, ale řetězec, který nikdy neviděl, skončí chybou 404 vehicle_not_found (zdarma, ale jde o zbytečný požadavek). Místo překládání vlastních řetězců projděte katalog, uložte si jeho ID a zbývající případy dohledejte vyhledáváním nebo přes resolve.