Car Image API

使用场景 · 汽车租赁

用于租车车队页面的汽车图片

以统一方式展示每个租车级别:每个级别一款代表车型,使用 trim、padding 和 fit 实现相同构图,采用一种指定车漆颜色,并使用可缓存的 URL。

依据 API 文档所述行为编写的构建指南,不涉及任何具体客户。 发布于 。

问题:您租的是车型类别,而不是某一辆车

租车客户预订的是一个车型类别,例如紧凑型车、中型 SUV、MPV,然后开走停车场上该类别中的任意一辆车。预订页面仍需为每个类别配图,惯例是使用一款代表车型并注明“或同类车型”。

这些图片往往东拼西凑,哪里能找到就用哪里的:这张是新闻图,那张是经销商照片,角度各异,背景不同,车辆在卡片中的占比也各不相同。这样拼凑出来的网格,一看就是拼凑的。车队页面需要的恰恰相反:每个车型类别都以相同方式呈现,卡片之间唯一变化的就是车本身。

集成方式一览

  • 每个类别一款代表车型,以目录车辆 ID 保存,“或同级车型”写在标签里,绝不体现在图片中。
  • 每张卡片使用统一的请求结构:相同的视角、尺寸框、适配方式、裁剪、内边距、车漆、背景和格式。
  • 每个车型类别最多两张图片:网格中的卡片图和类别页面上的主图。
  • 缓存,因为网格对每位访客都相同:文件放在您的 CDN 上,或签名 URL 只生成一次并重复使用。

第 1 步:按 id 为每个车辆级别选定一款代表车型

选择每个车型级别对应的销售车型,并查询其当前年款。GET /api/v1/vehicles?make=&model= 免费且公开,会列出某一车型的所有年款,每个年款都附带您需要存储的稳定车辆 ID;POST /api/v1/images/resolve 则可根据“2026 toyota rav4”这样的短语完成同样的操作。

单个车型年款的 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,所有读取该映射的卡片、首屏大图和邮件都会随之更新。

第 2 步:所有级别使用同一种请求格式

9 个机位视角对目录中的每辆车都采用完全相同的构图,这正是网格能够整齐对齐的原因。卡片的其余部分只需几个参数,一次确定即可:

  • 卡片使用 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。
单个车型级别的卡片和主视觉图
# 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 步:整个网格使用同一种命名车漆

这 15 种预设是命名车漆:white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple。默认值为 silver,grey 会被视为 gray。请为所有车型类别选用同一种,让卡片看起来成套;上方展示的是 white。每种预设都使用其专属的车漆色样,而非同名的 CSS 颜色(red 为 #b3151b);每种车漆(包括十六进制颜色)都是基于同一个已准备视角的重新着色,因此每种车漆计为一张车辆图片。

第 4 步:缓存文件或 URL

类别网格对每位访客都相同,因此每次访问的成本应几乎为零。有两种实现方式:

  • 存储文件。使用 GET /api/v1/images/car 将每张卡片图和主图获取一次,然后像其他静态资源一样通过您的 CDN 提供;付费套餐有效期间,允许在您自己的服务器上保存副本,用于为您的产品提供服务(套餐结束后 30 天内删除,或购买图片包以永久保留一组图片)。请保留每个文件的 ETag:每晚使用 If-None-Match 重新检查会返回 304,不产生任何费用。对于上述车队,这相当于当月 8 张车辆图片,可按需反复使用。
  • 或者一次性生成签名 URL 并重复使用。POST /api/v1/image-urls 每次调用最多可处理 50 张图片,返回无需密钥的 URL,有效期最长 7 天。不限使用次数的 URL 在有效期内可被公开缓存,因此当所有访客加载同一个 URL 时,重复请求将免费由 CDN 提供。添加 renew: true 后,已存储的 URL 在有效期过后仍可继续使用;续期只在其发生的月份将该图片计一次,若该图片当月已计过则不再重复计数,因此上述车队每月最多为 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,可使重试发送时重放第一个 URL,而不会再生成一个;将 URL 存储在预订记录上,之后每次重发都可复用它。我们自己的生命周期邮件就是这样运作的;邮件案例中介绍了相关机制和 HTML。

用于单份预订确认的自动续期 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:client.vehicles({ make, model }) 用于获取 ID,client.getImage(…) 用于获取文件,client.createImageUrls(…) 用于获取 URL。
  • MCP:resolve_vehicle 和 create_car_image_urls 可让助手根据“中型 SUV,RAV4 或同级车型”起草车型卡片。设置方法见 MCP 页面。

常见问题

卡片能否展示租车人实际会拿到的那辆车?
不能,也不应该这样做。租车按“或同级车型”出租,而渲染图呈现的是目录中的品牌、车型和年份,而不是您停车场里的某辆车。请展示一款代表性车型,并在标签中注明“或同级车型”。柜台分配具体车辆后,如果您想改为展示该车型,可以免费将其 VIN 解码为对应的目录车辆。
为什么网格中的车辆没有按比例显示?
因为 trim 会将每张渲染图裁剪到车身边缘,而 fit=contain 会让车辆填满卡片,所以紧凑型车和全尺寸 SUV 在卡片中的填充方式相同。这正是网格保持整齐的关键。请用文字说明尺寸:级别名称、座位数和可放行李数。
级别网格的费用是多少?
一个包含 4 个车型类别、每个类别各有一张卡片图和一张主图的车队,每月需要 8 张车辆图片,每张均可按任意尺寸随意多次交付。在付费套餐有效期间将文件存储在您的 CDN 上,它们只在您获取的当月计数,并可通过 ETag 免费重新校验。如果改为提供自动续期的签名 URL,则同样的图片会在每个有页面被打开的月份各计一次。
网格应使用哪种车漆颜色?
所有车型类别使用同一种预设颜色,让卡片看起来成套统一。共有 15 种预设:white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple,默认为 silver;任意十六进制颜色值也可以使用,价格相同。租客拿到的是停车场里那辆车的颜色,而卡片从未对颜色做出承诺。
如果某个车型类别的车型不在目录中怎么办?
目录中不存在的车辆会返回 404 并附带建议:最接近的目录车辆,每个都带有其 id。您可以使用其中之一,或在公开的需求看板上申请添加该车辆。该目录是开源的。

继续阅读

客户案例 · 开发者工具如何使用永不失效的签名 URL 在邮件中嵌入汽车图片我们自己的生命周期邮件会将每位客户的首张渲染图以实时签名 URL 的形式嵌入,该 URL 会自动续期一年。下面介绍其工作原理以及如何照搬这一做法。