Car Image API

Zastosowania · Aukcje

Obrazy samochodów do ofert aukcyjnych

Wystawianie pozycji aukcyjnych przed nadejściem zdjęć – każda ze studyjnym obrazem swojego modelu i rocznika, oznaczonym jako render katalogowy, przez podpisane URL-e, które wygasają wraz z aukcją.

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.

Dekodowanie przy przyjęciu pojazdu
# 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.

Rok, potem marka, potem model
# 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"
200 OK (skrócone)
{
  "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.

Zdjęcie lotu do czasu nadejścia jego fotografii
<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: true i bez ponownego obciążenia.
  • Ten sam klucz z inną treścią żądania daje 422, a ponowienie, które wyprzedzi pierwsze żądanie, daje 409 z nagłówkiem Retry-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.

URL lotu 117
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 charged

Krok 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_seconds ustawionym na maksimum i renew_days obejmują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ę na renews_until.
  • Należy pozostawić max_uses na 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 ustawienie private, no-store i 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.
Rejestracja pozycji za pomocą TypeScript SDK
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 arrive

Plan 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.

Czytaj dalej

Historia klienta · Motoryzacyjne serwisy ogłoszenioweJak dodać studyjne zdjęcie samochodu do każdego ogłoszenia w serwisieOgłoszenie potrzebuje zdjęcia, zanim sprzedawca prześle własne. Serwisy ogłoszeniowe uzupełniają cały katalog jednym ujęciem i jednym lakierem, a potem zachowują ten sam kształt żądania dla każdego nowego rocznika.