Przewodnik wdrożeniowy oparty na udokumentowanym działaniu API; nie wymienia żadnego klienta. Opublikowano .
Problem: sprzedaż rusza, zanim pojawią się zdjęcia
Katalog aukcji trafia do sieci, gdy tylko spłyną zgłoszenia: dla każdego samochodu numer VIN, numer pozycji i data sprzedaży. Zdjęcia pojawiają się, gdy samochód dotrze na plac, a fotograf do samochodu, co w przypadku części aukcji następuje już po otwarciu licytacji. Do tego czasu każda z tych pozycji to szary prostokąt obok linijki tekstu.
Katalogowy render wypełnia to miejsce właściwym rocznikiem. Aukcja to jednak również miejsce, w którym render nigdy nie może uchodzić za dany samochód: licytujący wyceniają stan pojazdu, a render go nie pokazuje, ponieważ nie przedstawia żadnego konkretnego fizycznego pojazdu. Wszystko, co poniżej, wynika z jednoczesnego uwzględnienia obu tych faktów.
Jak wygląda integracja
- Render dla każdego lotu na podstawie numeru VIN, wyświetlany tylko do czasu pojawienia się własnych zdjęć lotu.
- Wybór z katalogu dla lotów, których VIN się nie dekoduje.
- Oznaczenie na obrazie: „Render katalogowy, nie ta pozycja”.
- Jeden URL na pozycję aukcyjną, generowany raz z
Idempotency-Key, ważny przez cały czas trwania sprzedaży.
Krok 1: od numeru VIN pozycji do identyfikatora pojazdu
GET /api/v1/vin/{vin} jest bezpłatny. Gdy katalog zawiera dany pojazd, odpowiedź zawiera data.vehicle.id – stały identyfikator do zapisania przy locie; zdekodowana wersja wyposażenia, nadwozie i silnik pozostają w tekście lotu. Katalog obejmuje lata od 1990 do 2027, więc starsze pojazdy na aukcji renderują się tak samo jak najnowsze. Przewodnik dla ubezpieczeń omawia dekodowanie pole po polu, a dokumentacja VIN opisuje częściowe numery VIN i błędy.
# the lot's VIN to the vehicle id the lot stores (free); nothing printed means no catalog match
npx @meterapp/car-image vin "$LOT_VIN" --json | jq -r '.data.vehicle.id // empty'Krok 2: brak VIN do zdekodowania? Wybór pojazdu z katalogu
Niektóre pozycje aukcyjne trafiają bez numeru VIN, który da się zdekodować: pojazd wyprodukowany na inny rynek, VIN błędnie wpisany przez źródło, karta komisowa z rokiem, marką i modelem i niczym więcej. Wystarczy dodać do formularza komisowego trzy listy rozwijane zasilane przez GET /api/v1/vehicles, które jest bezpłatne i publiczne: ?year= zwraca marki mające model w danym roku, a ?year=&makeId= zwraca modele tej marki, każdy z identyfikatorem pojazdu dla danego rocznika.
# every make with a 2006 model year (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?year=2006"
# {"data":{"year":2006,"makes":[…,{"id":476,"name":"DODGE","slug":"dodge"},…]},…}
# that make's 2006 models, each with the id of its 2006 model year
curl -s "https://carimage.dev/api/v1/vehicles?year=2006&makeId=476"{
"data": {
"year": 2006,
"make_id": 476,
"models": [
{
"name": "Caliber",
"slug": "caliber",
"vehicle_type": "Passenger Car",
"id": "veh_294vq81yq0d3y"
},
{
"name": "Charger",
"slug": "charger",
"vehicle_type": "Passenger Car",
"id": "veh_96j8z47jqfd4r"
},
"…"
]
}
}Ten sam katalog jest dostępny jako otwarty pakiet @meterapp/vehicle-db, więc listy rozwijane mogą działać także offline na identycznej liście. Tak czy inaczej pozycja aukcyjna otrzymuje ten sam rodzaj id co zdekodowany VIN i na dalszych etapach nic nie zapisuje nazwy samochodu tekstem.
Gdy cała lista zgłoszonych pojazdów przychodzi naraz, POST /api/v1/vehicles/check przyjmuje do 2000 pozycji na wywołanie, każdą zapisaną tak jak na liście i z numerem lotu jako ref, i odpowiada na każdą z nich: dopasowaniem z id do zapisania, sugestiami, brakiem dopasowania lub informacją o nieprawidłowym wierszu. Nic nie jest renderowane ani naliczane; szczegóły znajdą Państwo w dokumentacji vehicles.
Krok 3: oznaczenie renderu jako renderu
Oznaczenie należy umieścić na samym zdjęciu, tam, gdzie pada wzrok licytującego, oraz w tekście alternatywnym, a nie tylko w opisie pod spodem. Stan, przebieg, uszkodzenia, wyposażenie dodatkowe i lakier samochodu należą do opisu pozycji aukcyjnej oraz – gdy już powstaną – do zdjęć. Gdy pojawią się zdjęcia, render znika.
<figure class="lot-image">
<img src="https://carimage.dev/api/v1/delivery/eyJhbGciOi…" width="800" height="450"
alt="Catalog render of a 2006 Dodge Charger, not a photo of lot 117">
<span class="lot-image__badge">Catalog render, not this lot</span>
</figure>
<p>Photos of this lot are on their way. Condition, mileage and damage are in the report.</p>Krok 4: jeden URL na pozycję, generowany raz
URL pozycji należy wygenerować przy jej przyjęciu za pomocą POST /api/v1/image-urls z nagłówkiem Idempotency-Key utworzonym z aukcji i pozycji, złożonym z liter, cyfr oraz znaków ., _, : i -. URL liczy swój obraz jako obraz pojazdu w danym miesiącu w chwili wygenerowania, więc bez klucza ponowione przyjęcie wygenerowałoby drugi URL. Z kluczem:
- Ten sam klucz z tą samą treścią w ciągu 24 godz. odtwarza pierwszą odpowiedź, łącznie z tym samym URL, z nagłówkiem
Idempotent-Replayed: truei bez ponownego obciążenia. - Ten sam klucz z inną treścią żądania daje
422, a ponowienie, które wyprzedzi pierwsze żądanie, daje409z nagłówkiemRetry-After. Dlatego całą treść żądania, łącznie z czasem ważności, należy ustalić przy tworzeniu pozycji aukcyjnej i zapisać razem z nią. - Adres URL warto też zapisać przy pozycji: klucz chroni ponowienia, a zapisany URL jest tym, co strony odczytują do końca aukcji.
Przy imporcie całego katalogu naraz można wygenerować adresy dla maksymalnie 50 pozycji aukcyjnych na wywołanie, z jednym kluczem na partię; każdy URL w odpowiedzi zawiera pojazd i opcje, dla których został wygenerowany.
curl --fail-with-body -X POST https://carimage.dev/api/v1/image-urls \
-H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sale-2026-10-14:lot-117" \
-d '{"images":[{"vehicle":"veh_96j8z47jqfd4r","view":"side","width":800,"height":450,"fit":"contain","trim":true,"padding":6,"format":"auto"}],"ttl_seconds":259200}'
# run it again: the same URL comes back with Idempotent-Replayed: true, and nothing more is chargedKrok 5: dopasowanie czasu ważności URL do sprzedaży
ttl_seconds może wynosić maksymalnie 604800 (7 dni); dłuższa wartość zostanie do niej obcięta. W przypadku sprzedaży kończącej się w ciągu tygodnia należy ustawić czas pozostały do jej zamknięcia. Obraz poglądowy przestaje wtedy działać razem z końcem licytacji, a potem URL zwraca 403.
- Aukcja czasowa dłuższa niż 7 dni: należy wygenerować URL z
renew: true,ttl_secondsustawionym na maksimum irenew_daysobejmującym czas trwania aukcji. Odnowienie liczy obraz raz w miesiącu, w którym nastąpi, i nic więcej, jeśli obraz został już policzony w tym miesiącu; tygodnie, w których nikt nie otwiera pozycji, nic nie kosztują, a odnawianie kończy się narenews_until. - Należy pozostawić
max_usesna 0, czyli wartość domyślną: URL bez limitu może być publicznie buforowany przez cały okres ważności, a URL z limitem ma ustawienieprivate, no-storei kieruje każde wczytanie do serwera źródłowego. - Klucz generujący należy ograniczyć do domen aukcji w panelu: obraz pozycji aukcyjnej skopiowany na inną stronę zwraca
403.
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
const lotImage = (vehicle: string): ImageParams => ({
vehicle, view: "side", width: 800, height: 450, fit: "contain", trim: true, padding: 6, format: "auto",
});
// How long the lot's URL lives, decided once from the sale's close.
function lifetime(closesAt: Date) {
const seconds = secondsUntil(closesAt);
return seconds <= 604800
? { ttlSeconds: Math.max(seconds, 60) } // closes within the week
: { ttlSeconds: 604800, renew: true, renewDays: Math.min(daysUntil(closesAt), 365) }; // a longer timed sale
}
// At intake, once per lot. The lifetime is stored with the lot, so a retry sends
// the same body and replays the first answer instead of minting a second URL.
lot.imageLifetime ??= lifetime(sale.closesAt);
const { data: [placeholder] } = await client.createImageUrls(lotImage(lot.vehicleId), {
...lot.imageLifetime,
idempotencyKey: `${sale.id}:lot-${lot.number}`,
});
lot.placeholderUrl = placeholder.url; // replaced by the lot's own photos when they arrivePlan dopasowany do skali aukcji
Każda pozycja aukcyjna to jeden obraz pojazdu — jej pojazd w jej lakierze i widoku — chyba że dwie pozycje mają wszystkie trzy cechy wspólne. Każdy plan obejmuje określoną liczbę obrazów pojazdów miesięcznie: Free 100, Pro 2500 i Business 10 000; Enterprise podlega negocjacjom. Obraz już policzony w tym miesiącu jest zawsze wyświetlany, a partia, która przekroczyłaby limit, jest w całości odrzucana, zanim cokolwiek zostanie policzone; odpowiedź zawiera plan i limit. Plan warto dobrać do liczby pozycji wystawianych w miesiącu. Studium przypadku platformy ogłoszeniowej opisuje, jak rozłożyć duży import w czasie w ramach limitów żądań i renderowania.
Najczęściej zadawane pytania
- Czy render może pozostać, gdy pojawią się już zdjęcia pozycji aukcyjnej?
- Należy go zastąpić. Render jest obrazem zastępczym dla rocznika modelu, a licytujący nigdy nie powinien się zastanawiać, które zdjęcie przedstawia sprzedawany samochód. Jeśli strona zachowuje render w celach poglądowych, etykieta pozostaje razem z nim.
- Co dzieje się z URL po sprzedaży?
- Wygasa, gdy kończy się jego czas ważności lub gdy odnawiany URL osiągnie koniec okresu odnawiania, i wtedy zwraca 403. Do tego czasu pozycja ma już własne zdjęcia lub wynik, a archiwum aukcji powinno pokazywać zdjęcia. Unieważnienie klucza, którym wygenerowano URL-e, kończy ważność ich wszystkich naraz.
- Ponowna próba zwróciła 422. Dlaczego?
- Ten sam Idempotency-Key wrócił z inną treścią żądania. Zwykle przyczyną jest czas ważności przeliczany na podstawie zegara w chwili ponowienia. Należy ustalić czas ważności przy tworzeniu pozycji i go zapisać – wtedy ponowienie wyśle tę samą treść i odtworzy pierwszą odpowiedź.
- Czy render może pokazać lakier danej pozycji?
- Tak: parametr color przyjmuje dowolną z predefiniowanych nazw lub wartość hex, więc obraz może odpowiadać lakierowi z karty przyjęcia pojazdu bez dodatkowych kosztów. Nadal jest to model w tym lakierze, a nie konkretna pozycja aukcyjna, więc etykieta pozostaje.
- Ile kosztuje aukcja obejmująca 400 pozycji?
- 400 obrazów pojazdów w miesiącu: 1 obraz pojazdu na pozycję aukcyjną w chwili wygenerowania jej URL-a, a ponowiona próba wygenerowania nie jest liczona drugi raz. Każde wczytanie w okresie ważności URL-a jest bezpłatne. Miesięczne limity obrazów pojazdów w planach: Free 100, Pro 2500 i Business 10 000, a pozycje z tym samym pojazdem, lakierem i ujęciem są liczone raz.