Car Image API

ユースケース · レンタカー

レンタカーのフリートページ向け車両画像

すべてのレンタルクラスを同じ見せ方で表示します。クラスごとに代表車種を 1 台、trim・padding・fit による統一された構図、名前付きの塗装色を 1 色、そしてキャッシュできる URL。

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

課題:レンタルするのは車ではなくクラス

レンタカーの利用者が予約するのはコンパクト、ミッドサイズ SUV、ミニバンといったクラスで、実際にはそのクラスで営業所にある車両のいずれかに乗って出発します。それでも予約ページには各クラスの画像が必要で、代表的なモデルを1台掲載して「または同等車」と添えるのが慣例です。

そうした画像は、たいてい見つかった場所からそれぞれ集められています。こちらは報道用写真、あちらはディーラーの写真、角度も背景もばらばらで、カード内での車の収まり方も異なります。そのように寄せ集めたグリッドは、寄せ集めに見えてしまいます。車両一覧ページに必要なのはその逆で、すべてのクラスを同じ方法で描き、カードごとに変わるのは車だけ、という状態です。

連携の全体像

  • クラスごとに代表モデルを 1 つ選び、カタログの車両 ID として保持します。「または同等車種」はラベルに記載し、画像には含めません。
  • すべてのカードで同じリクエスト形式:ビュー、ボックス、フィット、クロップ、余白、塗装色、背景、フォーマットをすべて揃えます。
  • 1 クラスにつき画像は最大 2 枚:グリッドのカードと、クラスページのメイン画像です。
  • キャッシュします。グリッドはすべての訪問者で同じだからです。ファイルを CDN に置くか、署名付き URL を一度発行して再利用します。

ステップ1:クラスごとに代表的なモデルをIDで選ぶ

各クラスの販売に使うモデルを選び、その現行年式を調べます。GET /api/v1/vehicles?make=&model= は無料の公開エンドポイントで、1つのモデルの全年式を、保存用の固定の車両IDとともに一覧表示します。POST /api/v1/images/resolve は「2026 toyota rav4」のようなフレーズから同じ処理を行います。

1つのモデルイヤーのID
# one model's years, each with its vehicle id (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?make=toyota&model=rav4" | jq -c '.data.vehicles[] | select(.year == 2026)'
# {"year":2026,"id":"veh_e5w75c93f6cvk"}
fleet.ts
// One representative model per class: its current model year, by catalog id.
// "or similar" lives in the label; the picture never promises the car on the lot.
export const CLASS_IMAGES = {
  compact:        { vehicle: "veh_fy7r2yvwt4p1v", label: "Toyota Corolla or similar" },    // 2026 Toyota Corolla
  "midsize-suv":  { vehicle: "veh_e5w75c93f6cvk", label: "Toyota RAV4 or similar" },       // 2026 Toyota RAV4
  "fullsize-suv": { vehicle: "veh_9mzh9ejqjye43", label: "Chevrolet Tahoe or similar" },   // 2026 Chevrolet Tahoe
  minivan:        { vehicle: "veh_9jck7rrybf35p", label: "Chrysler Pacifica or similar" }, // 2026 Chrysler Pacifica
};

id は変わらないため、フリートの変更が影響するのはこのマップだけです。ミッドサイズクラスを別のモデルや次の年式に切り替えるときは id を1つ置き換えるだけで、このマップを参照するすべてのカード、ヒーロー画像、メールが追従します。

ステップ 2:すべてのクラスに同じリクエスト形式を

8 種類のカメラビューは、カタログ内のすべての車両で同じ構図になっているため、グリッドがきれいに揃います。カードのそれ以外の部分は、一度決めるだけのいくつかのパラメーターです:

  • カードには view=side を使用します。すべての車が同じ向き、同じベースラインに揃います。左側の各アングルには -right の対となるアングル(side-right)があるため、カードをレイアウトの内側に向けられます。クラスページではヒーローアングルの front-3-4 を使用します。
  • w=640&h=320&fit=contain:すべてのクラスでぴったり 640×320、車全体が表示されます。レスポンスの X-Image-Width と X-Image-Height でキャンバスサイズを確認できます。
  • trim=1&padding=6:フィットさせる前に車の部分で切り抜き、長辺の6%を余白にします。これにより Corolla も Tahoe も、正方形の元フレームの中で浮いて見えることなく、カードを同じように埋めます。
  • background=f4f4f4 を指定すると、カードの背景がグリッドと同じグレーで塗りつぶされます。指定しなければ背景は透過のままで、カードの色が透けて見えます。
  • 保存するファイルには format=webp、署名付き URL には format=auto を使います。後者はブラウザごとに WebP または PNG を配信します。
1つのクラス用のカードとヒーロー画像
# the class card: the same box, view, crop, paint and backdrop for every class
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=side&color=white&w=640&h=320&fit=contain&trim=1&padding=6&background=f4f4f4&format=webp" \
  -o midsize-suv-card.webp

# the class page's hero: the three-quarter view, the full width the API delivers
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=front-3-4&color=white&w=1024&format=webp" \
  -o midsize-suv-hero.webp

ステップ 3:グリッド全体で名前付きの塗装色を 1 色に統一

15 種類のプリセットは名前付きの塗装色です:white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple。デフォルトは silver で、grey は gray として扱われます。カードが統一感のあるセットに見えるよう、すべてのクラスで同じ1色を選んでください。上の例は white です。各プリセットは同名のCSSカラーではなく独自の塗装見本色を持ちます(red は #b3151b)。hex指定を含むすべての塗装色は1つのニュートラルなレンダリングを再着色したものなので、料金は同じ 1 クレジット です。

ステップ 4:ファイルまたは URL をキャッシュする

クラスのグリッドはすべての訪問者に同じ内容が表示されるため、訪問ごとのコストはほぼゼロに抑えるべきです。方法は 2 つあります:

  • ファイルを保存する。各カードとヒーロー画像を GET /api/v1/images/car で一度取得し、他のアセットと同様に CDN から配信してください。有料プランでは、自社製品の提供のために自社サーバーにコピーを保存でき、プラン終了後もそのライセンスは有効です。各ファイルの ETag を保持しておけば、If-None-Match による毎晩の再確認は 304 を返し、費用はかかりません。上記のフリートの場合、必要なのは一度だけの 8 クレジットです。
  • または、署名付き URL を一度発行して再利用します。POST /api/v1/image-urls は1回の呼び出しで最大 50 枚の画像を受け付け、最長 7 日間有効なキー不要の URL を返します。使用回数無制限の URL は有効期間内であれば公開キャッシュが可能なため、すべての訪問者が同じ URL を読み込む場合、2回目以降は CDN から無料で配信されます。renew: true を追加すると、保存した URL は有効期間を過ぎても使い続けられ、その後読み込まれた週ごとに画像1枚あたり 1 クレジット かかります。上記のフリートなら最大で週 8 クレジット です。

ダッシュボードで、URLを発行するキーを予約サイトのドメインに制限すると、別のサイトにコピーされたURLは403を返します。

TypeScript SDK でグリッドの URL を一度だけ発行する
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";
import { CLASS_IMAGES } from "./fleet";

const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });

// Framed once, for every class.
const card = (vehicle: string): ImageParams => ({
  vehicle, view: "side", color: "white", width: 640, height: 320,
  fit: "contain", trim: true, padding: 6, background: "#f4f4f4", format: "auto",
});
const hero = (vehicle: string): ImageParams => ({ vehicle, view: "front-3-4", color: "white", width: 1024, format: "auto" });

// Every visitor sees the same grid: mint the URLs once and serve the stored ones.
// renew keeps each URL working past its 7-day lifetime, one credit per image
// for each further week in which it is loaded.
const classes = Object.entries(CLASS_IMAGES);
const { data } = await client.createImageUrls(
  classes.flatMap(([, entry]) => [card(entry.vehicle), hero(entry.vehicle)]),
  { ttlSeconds: 604800, renew: true, idempotencyKey: "class-grid-2026-10" }
);
await saveClassImages(
  classes.map(([key], index) => ({ key, card: data[2 * index].url, hero: data[2 * index + 1].url, renewsUntil: data[2 * index].renews_until }))
);

ステップ 5:予約確認

予約確認メールには予約されたクラスが表示され、メールは借り手の都合のよいときに開封されます。その画像は更新型 URL として発行してください。ttl_seconds を最大の 7 日に、renew: true を指定し、renew_days は返却日より後まで設定します。誰もメールを開かない週には費用がかからず、レンタル終了後は URL の更新が止まります。予約ごとに Idempotency-Key を付けると、送信を再試行しても2つ目の URL を発行せず最初の URL が再利用されます。また、URL を予約データに保存しておけば、以降の再送でも同じ URL を使えます。当社のライフサイクルメールもこの仕組みで動いています。仕組みと HTML はメールの事例をご覧ください。

予約確認 1 件分の自動更新 URL
// The booking confirmation: opened today, again at the counter, maybe never.
const { data: [classImage] } = await client.createImageUrls(card(booking.classVehicle), {
  ttlSeconds: 604800,
  renew: true,
  // A week past the return date, and no renewals after it (365 days at most).
  renewDays: Math.min(daysUntil(booking.returnDate) + 7, 365),
  // A retried send replays the first URL instead of minting a second one.
  idempotencyKey: `booking:${booking.id}:class`,
});
booking.classImageUrl = classImage.url; // later resends reuse it

CLI、SDK、MCP

  • CLI: npx @meterapp/car-image get --vehicle veh_… --view side --width 640 --height 320 --fit contain --trim --padding 6 --out card.webp はカードをファイルに書き出し、car-image url --batch classes.json --renew は URL をまとめて発行します。
  • SDK:ID には client.vehicles({ make, model })、ファイルには client.getImage(…)、URL には client.createImageUrls(…) を使います。
  • MCP:resolve_vehicleとcreate_car_image_urlsを使うと、アシスタントが「ミッドサイズSUV、RAV4または同等クラス」といった指定からクラスカードの下書きを作成できます。設定方法はMCPページをご覧ください。

よくある質問

カードに、お客様が実際に借りる車両そのものを表示できますか?
いいえ、また表示しようとすべきでもありません。レンタカーのクラスは「または同等車種」として貸し出されるもので、レンダリング画像が示すのはカタログ上のメーカー、モデル、年式であり、貴社の店舗にある実車ではありません。代表的なモデルを1つ、ラベルに「または同等車種」と添えて表示してください。カウンターで特定の車両が割り当てられた後は、そのVINを無料でデコードしてカタログ上の車両を特定できるため、必要に応じてそのモデルを表示することもできます。
グリッド内の車の縮尺が揃っていないのはなぜですか?
trim が各レンダーを車体に合わせて切り抜き、fit=contain がカードいっぱいに車を収めるため、コンパクトカーもフルサイズ SUV も同じようにカードを埋めます。これによりグリッドが整って見えます。サイズは言葉で伝えてください。クラス名、乗車人数、荷物の個数です。
クラスグリッドの費用はどれくらいですか?
4クラスのフリートで、各クラスにカードとヒーロー画像を1枚ずつ用意すると8クレジットです。ファイルをご自身のCDNに保存すれば費用はそれだけで、ETagによる再確認は無料です。代わりに自動更新される署名付きURLで配信する場合でも、ページが閲覧された週に限り、週あたり最大8クレジットです。
グリッドにはどの塗装色を使うべきですか?
すべてのクラスで同じプリセットを1つ使えば、カードに統一感が出ます。15種類のプリセットはwhite, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purpleで、デフォルトはsilverです。任意のhexも同じ料金で使えます。お客様に貸し出されるのは店舗にある車の色であり、カードがその色を約束しているわけではありません。
車両クラスのモデルがカタログにない場合はどうなりますか?
存在しない車両をリクエストすると、候補とともに404が返されます。候補はカタログ内で最も近い車両で、それぞれにIDが付いています。そのいずれかを使うか、公開リクエストボードで車両の追加をリクエストしてください。カタログはオープンソースです。

続きを読む

導入事例 · 開発者ツールリンク切れしない署名付きURLでメールに車の画像を埋め込む方法当社のライフサイクルメールには、各顧客の最初のレンダリングそのものを、1年間自動で更新されるライブな署名付き URL として埋め込んでいます。その仕組みと再現方法をご紹介します。