Car Image API

Сценарии использования · Аукционы

Изображения автомобилей для аукционных объявлений

Размещайте аукционные лоты до появления фотографий — каждый со студийным изображением своей модели и года выпуска с пометкой «рендер из каталога» — через подписанные URL, срок действия которых истекает вместе с торгами.

Руководство по интеграции, написанное на основе документированного поведения API; конкретные клиенты в нём не упоминаются. Опубликовано .

Проблема: торги начинаются раньше, чем появляются фотографии

Каталог аукциона публикуется, как только поступают автомобили на продажу: для каждого — VIN, номер лота и дата торгов. Фотографии появляются, когда автомобиль прибывает на площадку, а фотограф добирается до автомобиля, — и для части лотов это происходит уже после начала торгов. До тех пор каждый такой лот — серый прямоугольник рядом со строкой текста.

Рендер из каталога заполняет место изображением нужного модельного года. Но аукцион — именно то место, где рендер никогда не должен выдаваться за сам автомобиль: участники торгов оценивают состояние, а рендер его не показывает, потому что он не изображает какой-либо конкретный физический автомобиль. Всё описанное ниже вытекает из необходимости учитывать оба этих факта одновременно.

Как выглядит интеграция

  • Рендер для каждого лота по его VIN, который показывается только до появления собственных фото лота.
  • Выбор из каталога для лотов, VIN которых не декодируется.
  • Подпись на изображении: «Рендер из каталога, не этот лот».
  • Один URL на лот, созданный один раз с Idempotency-Key и действующий до конца торгов.

Шаг 1: от VIN лота к id автомобиля

GET /api/v1/vin/{vin} — бесплатный метод. Если автомобиль есть в каталоге, ответ содержит data.vehicle.id — стабильный идентификатор, который нужно сохранить в лоте; декодированные комплектация, кузов и двигатель остаются в тексте лота. Каталог охватывает годы с 1990 по 2027, поэтому старые автомобили на аукционе получают рендеры так же, как новейшие. В руководстве по страхованию декодирование разобрано поле за полем, а в документации по VIN описаны неполные VIN и ошибки.

Декодирование VIN при приёмке
# 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'

Шаг 2: VIN не декодируется? Выберите автомобиль в каталоге

Некоторые лоты поступают без VIN, который удаётся декодировать: автомобиль, выпущенный для другого рынка, VIN с опечаткой в источнике, акт приёмки с годом, маркой и моделью — и больше ничего. Добавьте в форму приёмки три выпадающих списка на основе GET /api/v1/vehicles — это бесплатный и публичный эндпоинт: ?year= возвращает марки, у которых есть модель этого года, а ?year=&makeId= — модели этой марки, каждую с id автомобиля для этого модельного года.

Год, затем марка, затем модель
# 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 (сокращено)
{
  "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"
      },
      "…"
    ]
  }
}

Этот же каталог доступен как открытый пакет @meterapp/vehicle-db, поэтому выпадающие списки могут работать и офлайн с тем же самым списком. В любом случае у лота оказывается такой же id, как у декодированного VIN, и дальше по цепочке никто не вписывает название автомобиля вручную.

Когда весь список лотов от комитента приходит разом, POST /api/v1/vehicles/check принимает до 2 000 записей за вызов — каждую в том написании, что в списке, с номером лота в качестве ref — и отвечает по каждой: совпадение с id для сохранения, варианты, промах или некорректная строка. Ничего не рендерится и ничего не списывается; подробности — в документации по vehicles.

Шаг 3: подпишите рендер как рендер

Разместите пометку на самом изображении, куда падает взгляд участника торгов, и в alt-тексте, а не только в описании под ним. Состояние, пробег, повреждения, опции и фактический цвет автомобиля указываются в тексте лота, а когда появятся фотографии — на них. Когда приходят фотографии, рендер убирается.

Изображение лота до появления его фотографий
<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>

Шаг 4: один URL на лот, созданный один раз

Создавайте URL лота при приёмке через POST /api/v1/image-urls с Idempotency-Key, составленным из аукциона и лота, из букв, цифр, ., _, : и -. URL засчитывает своё изображение как изображение автомобиля за месяц в момент создания, поэтому без ключа повторная приёмка создала бы второй URL. С ключом:

  • Запрос с тем же ключом и тем же телом в течение 24 ч возвращает первый ответ повторно, включая тот же URL, с заголовком Idempotent-Replayed: true и без повторного списания.
  • Тот же ключ с другим телом запроса вернёт 422, а повторный запрос, опередивший первый, — 409 с Retry-After. Поэтому при создании лота зафиксируйте всё в теле запроса, включая срок действия, и храните его вместе с лотом.
  • Также сохраните URL в данных лота: ключ защищает повторные запросы, а сохранённый URL страницы используют до конца торгов.

При импорте всего каталога разом можно выпустить URL не более чем для 50 лотов за вызов, с отдельным ключом на каждый пакет; каждый URL в ответе повторяет автомобиль и параметры, для которых он был выпущен.

URL лота 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

Шаг 5: согласуйте срок действия URL со сроком торгов

ttl_seconds может составлять до 604800 (7 дн.); большее значение сокращается до этого предела. Если торги закрываются в течение недели, укажите время, оставшееся до закрытия. Тогда изображение-заглушка перестанет работать одновременно с окончанием торгов, а после этого URL будет возвращать 403.

  • Торги дольше 7 дн.: создавайте URL с renew: true, максимальным ttl_seconds и renew_days, покрывающим весь срок торгов. Продление засчитывает изображение один раз в том месяце, на который оно приходится, и ничего сверх того, если оно уже было учтено в этом месяце; недели, когда лот никто не открывает, ничего не стоят, а продление прекращается на renews_until.
  • Оставляйте max_uses равным 0 (значение по умолчанию): URL без ограничений публично кэшируется на весь срок действия, а URL с лимитом получает private, no-store и отправляет каждую загрузку на исходный сервер.
  • Ограничьте ключ, создающий URL, доменами вашего аукциона на панели управления: изображение лота, скопированное на другой сайт, вернёт 403.
Приём лотов с помощью 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

Подберите тариф под объём торгов

Каждый лот — это одно изображение автомобиля: его автомобиль в его цвете и ракурсе, если только два лота не совпадают по всем трём параметрам, и каждый тариф включает определённое число изображений автомобилей в месяц: Free — 100, Pro — 2 500 и Business — 10 000; для Enterprise — по договорённости. Изображение, уже учтённое в этом месяце, отдаётся всегда, а пакетный запрос, который превысил бы лимит, отклоняется целиком ещё до какого-либо учёта, и в ответе указываются тариф и лимит. Подбирайте тариф под количество лотов, которые вы выставляете за месяц. В истории маркетплейса рассказано, как распределить крупный импорт с учётом лимитов на запросы и рендеринг.

Часто задаваемые вопросы

Может ли рендер остаться после того, как появятся фотографии лота?
Замените его. Рендер — это заглушка для модельного года, и участник торгов никогда не должен гадать, на каком изображении продаваемый автомобиль. Если страница сохраняет рендер для справки, подпись остаётся вместе с ним.
Что происходит с URL после продажи?
URL перестаёт действовать, когда истекает его срок действия или когда продлеваемый URL достигает конца периода продления, после чего он возвращает 403. К этому времени у лота уже есть собственные фотографии или результат, и в архиве торгов должны отображаться фотографии. Отзыв ключа, которым были созданы URL, сразу аннулирует их все.
Повторный запрос вернул 422. Почему?
Тот же Idempotency-Key пришёл с другим телом запроса. Обычная причина — срок действия, заново вычисленный по часам в момент повторной попытки. Зафиксируйте срок действия при создании лота и сохраните его — тогда повторный запрос отправит то же тело и вернёт первый ответ.
Может ли рендер показать цвет кузова лота?
Да: параметр color принимает любое из предустановленных названий или hex-код, поэтому изображение может совпадать с цветом, указанным в документах на лот, без доплаты. Но это всё равно модель в этом цвете, а не сам лот, поэтому пометка остаётся.
Сколько стоят торги с количеством лотов 400?
400 изображений автомобиля за месяц: 1 изображение автомобиля на лот в момент создания его URL, а повторное создание ничего не добавляет. Каждая загрузка в течение срока действия URL бесплатна. Планы включают Free 100, Pro 2 500 и Business 10 000 изображений автомобилей в месяц, а лоты с одинаковыми автомобилем, цветом и ракурсом учитываются один раз.

Читайте также

История клиента · Автомобильные маркетплейсыКак добавить студийное изображение автомобиля к каждому объявлению на маркетплейсеОбъявлению нужна картинка ещё до того, как продавец загрузит свою. Маркетплейсы заполняют весь каталог одним ракурсом и одним цветом, а затем используют тот же формат запроса для каждого нового модельного года.