Car Image API

Use cases · Leasing

Car images for leasing quotes and offers

Render every lease offer as its model year in the exact paint by hex, put it in quote PDFs through signed URLs, and embed a 3D model in the configurator.

A build guide written from the API's documented behavior; it names no customer. Published .

The problem: the offer is exact, the picture is not

A lease offer is specific: one model year, the trim the payment is quoted on, a paint from the manufacturer's current palette, a monthly figure valid until a date. The picture beside it often is not: last year's press shot, a color the car is no longer offered in, a different body. When the payment is exact and the picture is approximate, the picture is the part a customer questions.

The same offer then travels: onto a model page, into a quote PDF, into an email, and into a configurator where the customer turns the car around. Each of those needs the same vehicle in the same paint.

What the integration looks like

  • An offer per model year, keyed by that year's catalog vehicle id.
  • The manufacturer's paint as a hex, on every picture of the offer.
  • The same picture in the quote PDF and the email, through signed URLs, so no document or renderer ever holds a key.
  • A 3D model per paint for the configurator, published as a two-line embed.

Step 1: key each offer on its model year's vehicle id

GET /api/v1/vehicles?make=&model= is free and public and lists every year of a model, each with its stable vehicle id. Store the id of the offer's model year with the offer.

Every year of one model
curl -s "https://carimage.dev/api/v1/vehicles?make=honda&model=cr-v"
200 OK (trimmed)
{
  "data": {
    "make": {
      "id": 474,
      "name": "HONDA",
      "slug": "honda"
    },
    "model": {
      "name": "CR-V",
      "slug": "cr-v",
      "vehicle_type": "Passenger Car"
    },
    "vehicles": [
      "…",
      {
        "year": 2025,
        "id": "veh_exjyt8r21gmhe"
      },
      {
        "year": 2026,
        "id": "veh_b6kgpb03d73r9"
      },
      {
        "year": 2027,
        "id": "veh_8nr7res56a6jq"
      }
    ]
  }
}

When the 2027 offers go live they take the 2027 id, and the 2026 offers keep theirs: an id never changes, so a page or a quote that was built on it keeps working after the model year turns over. The catalog keys on make, model and year, so the trim the payment is quoted on (an EX-L, a Sport Touring) is text on the offer, never something the picture claims.

Step 2: the manufacturer's paint, by hex

The 15 presets cover the common colors; an offer wants the paint the car is actually offered in. Take the hex from the manufacturer's color chip and send it bare in a URL (color=1f3a5f) or with its # in JSON ("color": "#1f3a5f"). A hex costs the same 1 credit as a preset, because every paint is a recolor of one neutral render, and a hex that equals a preset's swatch is that preset, cached once.

The offer's picture
# the 2026 offer's hero: its model year, in the paint it is offered in, cropped to a 16:9 slot
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_b6kgpb03d73r9&view=front-3-4&color=1f3a5f&w=1024&h=576&fit=contain&trim=1&padding=4&format=webp" \
  -o cr-v-2026-offer.webp

Step 3: the quote PDF and the offer email, through signed URLs

A quote PDF is usually HTML rendered by a PDF service or a headless browser. Put a signed URL from POST /api/v1/image-urls in the template, never the key: the renderer loads the picture once and embeds it, so the default lifetime of 1 hour is plenty, and format: "jpg" flattens it onto white, which suits a document. The URL costs 1 credit when it is minted.

The offer email is opened whenever the customer gets to it. Mint its picture with renew: true and ttl_seconds at the 7-day maximum, and set renew_days to the days left in the offer: each further week in which the email is opened costs 1 credit, quiet weeks cost nothing, and when the offer ends the URL stops renewing and expires. On a paid plan the license covers a render inside what your product produces for customers, a quote included.

The PDF and the email, with the TypeScript SDK
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";

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

// The offer's picture: its model year, in the paint it is offered in.
const offerImage: ImageParams = {
  vehicle: offer.vehicleId, view: "front-3-4", color: offer.paintHex, // "#1f3a5f"
  width: 1024, height: 576, fit: "contain", trim: true, padding: 4,
};

// The quote PDF: rendered from HTML, the picture loaded once and embedded, on white.
const { data: [forPdf] } = await client.createImageUrls({ ...offerImage, format: "jpg" });
const pdf = await renderQuotePdf({ ...quote, vehicleImageUrl: forPdf.url });

// The offer email: loads for as long as the offer runs, then stops renewing.
const { data: [forEmail] } = await client.createImageUrls(
  { ...offerImage, width: 600, height: 338, format: "auto" },
  {
    ttlSeconds: 604800,
    renew: true,
    renewDays: Math.min(Math.max(daysUntil(offer.endsOn), 1), 365),
    idempotencyKey: `offer:${offer.id}:email`,
  }
);

Step 4: a 3D model for the configurator

POST /api/v1/3d with the offer's vehicle id, its paint and publish: true builds a textured model (GLB, USDZ, FBX and a browser build) and gives it key-free URLs with a two-line embed. It costs 100 credits ($1.00) per vehicle and color, charged once; ordering the same vehicle in the same paint again from your account is free. The first model of a vehicle takes 10–20 minutes, and every further paint reuses its mesh and takes 1–2 minutes.

One paint of the configurator
curl --fail-with-body -X POST https://carimage.dev/api/v1/3d \
  -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: configurator-cr-v-2026-1f3a5f" \
  -d '{"vehicle": "veh_b6kgpb03d73r9", "color": "#1f3a5f", "publish": true}'
202 Accepted (trimmed)
{
  "data": {
    "id": "5b0f3c2e-8a41-4d7e-9c10-2f6e1a3b4d58",
    "object": "3d_model",
    "status": "queued",
    "progress": 0,
    "vehicle": {
      "id": "veh_b6kgpb03d73r9",
      "make": "honda",
      "model": "cr-v",
      "year": 2026
    },
    "color": "#1f3a5f",
    "files": null,
    "public": {
      "id": "m3d_…",
      "url": "https://carimage.dev/api/v1/3d/public/m3d_…",
      "files": null,
      "embed": {
        "script": "https://carimage.dev/embed/3d.js",
        "html": "<script src=\"https://carimage.dev/embed/3d.js\" async></script>\n<car-3d model=\"m3d_…\"></car-3d>"
      }
    }
  },
  "billing": {
    "charged_on": "creation",
    "credits_charged": 100,
    "already_owned": false
  }
}

Poll GET /api/v1/3d/{id} (every 10–15 seconds is plenty), or pass a webhook_url with a webhook_secret and receive one signed POST when the model is ready. data.public.embed.html is the script and a <car-3d> element: paste it into the offer page. The element shows the poster at once and fetches the viewer the first time the model scrolls into view; view, spin, backdrop, ar and alt set the opening angle, the turntable, the backdrop, an AR button on phones (Quick Look through the USDZ on iOS) and the alt text. Publishing and every load are free.

A paint picker over published models
<script src="https://carimage.dev/embed/3d.js" async></script>

<select id="paint" aria-label="Paint">
  <option value="1f3a5f">Blue</option>
  <option value="white">White</option>
</select>

<!-- One published model per paint. Only the visible one is loaded: the script
     fetches a viewer the first time a model scrolls into view. -->
<car-3d data-paint="1f3a5f" model="m3d_…" view="front-3-4" spin backdrop="studio" ar
        alt="2026 Honda CR-V in the offer's blue"></car-3d>
<car-3d data-paint="white" model="m3d_…" view="front-3-4" spin backdrop="studio" ar
        alt="2026 Honda CR-V in white" hidden></car-3d>

<script>
  document.querySelector("#paint").addEventListener("change", (event) => {
    for (const model of document.querySelectorAll("car-3d[data-paint]")) {
      model.hidden = model.dataset.paint !== event.target.value;
    }
  });
</script>

A configurator in 4 paints is 4 models, 400 credits once. The wrap and tint shops on the API order 3D models the same way; their story covers the files and the formats, and the 3D docs cover webhooks, downloads and unpublishing.

CLI, SDK and MCP

  • CLI: npx @meterapp/car-image 3d create --vehicle veh_b6kgpb03d73r9 --color "#1f3a5f" --publish --wait orders a paint, waits for it and prints the embed.
  • SDK: client.vehicles({ make, model }), client.createImageUrls(…), client.create3dModel({ vehicle }, { color, publish: true }) and client.wait3dModel(id).
  • MCP: with ?toolset=all, create_3d_model, get_3d_model and publish_3d_model let an assistant order and publish a paint for a salesperson. Setup is on the MCP page.

Frequently asked questions

Does the picture show the trim the payment is quoted on?
No. The catalog keys on make, model and year, so every trim of a model year renders as the same vehicle. Put the trim in the offer's text, next to the picture, and never describe the picture as that trim or as a car in stock.
Does a manufacturer's paint cost more than a preset?
No. Every paint is a recolor of one neutral render, so a hex costs 1 credit like any of the 15 presets, and a hex that equals a preset's swatch is that preset. The hex is applied as that exact sRGB value under studio lighting, so compare the render with the color chip before the offer goes live.
What does a configurator in 4 paints cost?
400 credits, once: 100 credits for each paint of the vehicle. Ordering a vehicle and paint your account already holds again is free (billing.already_owned is true), and publishing, polling, downloads and every load of a published model are free too.
How long does a 3D model take, and what if it fails?
The first model of a vehicle takes 10–20 minutes; every further paint of the same vehicle reuses its mesh and takes 1–2 minutes. A model that fails is refunded in full, and when 3D generation is at its daily capacity the request is refused with a retry time before anything is charged.
Can a quote PDF keep the picture after the offer ends?
While your plan is active. On a paid plan the license covers a render inside what your product produces for customers, a quote or a brochure included; it ends 30 days after the plan ends, and stored copies must then be deleted. Rights beyond that need an Enterprise agreement: support@carimage.dev.

Keep reading

Customer story · Automotive aftermarketHow to preview a wrap, tint or PPF on the customer's exact carA customer wants to see the wrap on their car, not on someone else's. Shops render the exact model year in white, composite the material over it, match a paint by hex, and order a 3D model for the turntable.