Car Image API

ユースケース · オークション

オークション出品向けの車両画像

写真が届く前にオークションのロットを掲載できます。各ロットにはそのモデルイヤーのスタジオ画像をカタログレンダーと明記して表示し、オークション終了とともに失効する署名付き URL で配信します。

API のドキュメントに記載された動作に基づいて作成した構築ガイドです。特定の顧客名は挙げていません。 公開。

課題:写真より先にオークションが始まる

オークションカタログは、出品車両が揃い次第公開されます。各車両にあるのはVIN、ロット番号、販売日だけです。写真が揃うのは車両がヤードに到着し、カメラマンが撮影してからで、一部の出品車両では入札開始後になります。それまで、そうしたロットはテキスト1行の横に置かれたグレーの枠にすぎません。

カタログのレンダリング画像なら、正しい年式で画像枠を埋められます。一方で、オークションはレンダリング画像を実車と見なさせてはならない場でもあります。入札者は車両の状態で価格を判断しますが、レンダリング画像は特定の実在車両を描写したものではないため、状態を一切示しません。以下の内容はすべて、この両方を同時に踏まえることから導かれます。

連携の全体像

  • VINから出品ごとにレンダリング画像を作成し、出品車両自体の写真が届くまでの間だけ表示します。
  • カタログピッカー:VIN をデコードできないロット向け。
  • 画像上のラベル:「カタログ画像です。この出品車両ではありません」。
  • Idempotency-Key を付けて出品ごとに URL を1つ、1回だけ発行し、オークション終了まで有効にします。

ステップ 1:ロットの VIN から車両 ID へ

GET /api/v1/vin/{vin} は無料です。カタログに該当車両がある場合、レスポンスには出品に保存する固定IDである data.vehicle.id が含まれます。デコードされたトリム、ボディ、エンジンは出品のテキストに記載してください。カタログは1990〜2027をカバーしているため、オークションの古い在庫も最新の車両と同じようにレンダリングできます。保険ガイドではデコード結果をフィールドごとに解説しており、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 からデータを取得する3つのドロップダウンを用意してください。?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パッケージとしても公開されているため、ドロップダウンをオフラインで同一のリストに対して動作させることもできます。いずれの場合も、出品車両にはデコードしたVINと同じ種類のIDが保持され、下流の処理で車名を文字列で扱う必要はありません。

出品リスト全体が一度に届いた場合は、POST /api/v1/vehicles/check で 1 回の呼び出しにつき最大 2,000 件のエントリーを読み込めます。各エントリーはリストの表記のまま、ロット番号を ref として渡します。すべてのエントリーに対し、保存すべき id を含む一致、候補、不一致、無効な行のいずれかが返されます。レンダリングも課金も行われません。詳しくはvehicles ドキュメントをご覧ください。

ステップ3:レンダリング画像であることを明示する

ラベルは下の説明文だけでなく、入札者の目が向く画像そのものと代替テキストにも入れてください。車両状態、走行距離、損傷、オプション、実車の塗装色は、ロットの説明文と、揃い次第写真に載せるべき情報です。写真が届いたら、レンダリング画像は外します。

実車写真が届くまでのロットの画像
<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 を 1 つ、発行は 1 回だけ

出品登録時に、POST /api/v1/image-urlsと、セールと出品番号から作成したIdempotency-Key(英字、数字、.、_、:、-を使用)で出品車両のURLを発行します。URLは発行時に課金される(1クレジット)ため、キーがないと出品登録を再試行した際に二重に発行・課金されてしまいます。キーを使う場合:

  • 24 時間以内に同じキーと同じリクエストボディで送信すると、最初のレスポンスが同じ URL も含めてそのまま返され、Idempotent-Replayed: true が付きます。二重に課金されることはありません。
  • 同じキーでリクエストボディが異なると 422 になり、最初のリクエストを追い越したリトライは Retry-After 付きの 409 になります。そのため、有効期間も含めてボディの内容はすべてロット作成時に確定し、ロットと一緒に保存してください。
  • URLもロットに保存してください。キーは再試行時の重複を防ぎ、オークションの残りの期間、ページは保存されたURLを読み込みます。

カタログ全体を一括インポートする場合、1 回の呼び出しで最大 50 件のロットを発行でき、バッチごとに冪等キーを 1 つ指定します。レスポンス内の各 URL には、発行対象となった車両とオプションがそのまま返されます。

ロット 117 の URL
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日)で、それより長い値は上限に切り詰められます。1週間以内に終了するセールでは、終了までの残り時間を設定してください。そうすれば入札の終了と同時にプレースホルダー画像も機能しなくなり、それ以降URLは403を返します。

  • 7 日を超える期間限定オークション: renew: true を指定し、ttl_seconds を最大値に、renew_days をオークション期間をカバーする値に設定して発行します。以降、出品が閲覧された週ごとに 1 クレジットがかかり、閲覧のない週は無料です。更新は renews_until で停止します。
  • max_uses はデフォルトの 0 のままにしてください。無制限の URL は有効期間中パブリックにキャッシュできますが、上限付きの URL は private, no-store となり、すべての読み込みがオリジンに送られます。
  • ダッシュボードで発行用キーをオークションのドメインに制限してください。他のサイトにコピーされたロット画像は 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 15,000。Enterprise には上限がありません。今月すでに提供済みの車両は常に許可されます。上限を超えるバッチは課金前に全体が拒否され、応答にはプランと上限が含まれます。プランはクレジットではなく、1 か月に出品するロット数に合わせて選んでください。レート制限とレンダリング制限の範囲内で大規模なインポートを進める方法は、マーケットプレイスの事例で紹介しています。

よくある質問

ロットの写真が届いた後もレンダーを掲載し続けられますか?
差し替えてください。レンダリングはモデルイヤーのプレースホルダーであり、入札者がどの画像が出品車両なのか迷うことがあってはなりません。参考として残す場合は、ラベルも一緒に表示してください。
落札後、URLはどうなりますか?
有効期間が終了したとき、または更新型 URL の更新期間が終了したときに失効し、以降は 403 を返します。その頃にはロットに実車の写真か落札結果があり、オークションのアーカイブには写真を表示するべきです。URL を発行したキーを失効させると、それらの URL はすべて一度に無効になります。
再試行で422が返されました。なぜですか?
同じIdempotency-Keyで異なるボディが送信されました。よくある原因は、再試行の時点の時刻から有効期間を再計算していることです。ロット作成時に有効期間を確定して保存しておけば、再試行時にも同じボディが送信され、最初のレスポンスがそのまま返されます。
レンダーにロットの車体色を反映できますか?
はい。color にはプリセット名か16進カラーコードを指定できるため、追加料金なしで出品票に記載された塗装色に画像を合わせられます。ただし、それはあくまでその塗装色のモデルであって出品車両そのものではないため、ラベルはそのまま表示します。
400 ロットのオークションの費用はいくらですか?
400クレジット:ロットのURLを発行した時点でロットあたり1クレジットが発生し、発行を再試行しても料金はかかりません。URLの有効期間内の読み込みはすべて無料です。プランでは月あたりの異なる車両数に上限があり(Free 100、Pro 2,500 と Business 15,000。Enterprise には上限がありません)、メーカー・モデル・年式が同じロットは1台として数えます。

続きを読む

導入事例 · 自動車マーケットプレイスすべてのマーケットプレイス掲載にスタジオ品質の車両画像を載せる方法出品者が写真をアップロードする前から、掲載には画像が必要です。マーケットプレイスはカタログ全体を1つのアングルと1つの塗装色で補完し、新しいモデルイヤーにも同じ形式のリクエストを使い続けます。