Car Image API

Customers · Wrap, tint & PPF shops

How to preview a wrap, tint or PPF on the customer's exact car

How wrap, tint and paint-protection shops use white base renders, exact hex paints and 3D models to show a job on the customer's own car before the work starts.

The problem: the customer wants to see it on their car

A wrap shop, a tint shop or a paint-protection installer sells something the customer cannot see until it is done. The portfolio shows other people's cars. The manufacturer's swatch shows the material on a flat card. What closes the sale is the customer's own 2021 M4 in the exact satin grey they are hesitating over, and no photo of that exists, because the job has not happened yet.

The same gap shows up further down the line. A film cutter wants a clean outline of the front bumper for a model year the pattern library does not cover yet. A configurator wants a turntable of the car in a base color the designer can paint over. All three need the same thing: a studio image of a specific make, model and year, in a paint they choose, with nothing behind it.

Three ways shops use the renders

The wrap, tint and film integrations on the API today do three things, sometimes all three at once:

  • A white base, then the material on top. Render the customer's model year in color=white on a transparent PNG and composite the wrap, tint or printed design over it in the browser. The PNG's alpha is the car's outline.
  • A paint by hex. For a solid-color wrap or a respray, skip the compositing: color=686c70 returns the car already in that paint, glass and wheels untouched, for the same one credit as a preset.
  • A 3D model for the turntable. POST /api/v1/3d builds a textured GLB, USDZ and FBX of the vehicle in a chosen color, once per vehicle and color. A published model comes with a two-line embed for the quote page and a USDZ the customer can open in AR on an iPhone.

One shop runs its batches through the ChatGPT connector rather than through code, which is a fourth pattern worth its own section below.

A white base render and the material on top

A wrap replaces the paint, so start from as little paint as possible. color=white gives the brightest neutral body with the studio shading intact; the straight side view shows the most flank and the least foreshortening, which is what a material preview needs. Ask for png (or webp) so the alpha channel survives, and the largest size, 1024, so the preview can be zoomed.

The base render, and the same car in a hex paint
# the customer's exact model year, in white: the base every material goes on top of
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_0ta0ejj0qad2w&view=side&color=white&w=1024&format=png" \
  -o m4-side-white.png

# a solid-color wrap or a factory paint, by hex: the API recolors the paint and leaves glass, trim and wheels alone
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_0ta0ejj0qad2w&view=side&color=686c70&w=1024&format=png" \
  -o m4-side-nardo.png

For a textured material the compositing is a few lines of canvas: the render is drawn once as the mask, the material is painted only where the car is, and the render is drawn again in multiply so its shading and reflections come back through the material.

Painting a material over the base, in the browser
// A textured material (carbon, chrome, a printed design) goes on in the browser.
// The white render is the mask: paint the material only where the car is, then let
// the render's own shading through.
const car = await loadImage(whiteBaseUrl); // transparent PNG from the API
const ctx = canvas.getContext("2d");
canvas.width = car.width;
canvas.height = car.height;

ctx.drawImage(car, 0, 0);
ctx.globalCompositeOperation = "source-atop"; // keep the car's alpha
ctx.fillStyle = ctx.createPattern(materialSwatch, "repeat");
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "multiply"; // shading, reflections and panel lines come back
ctx.drawImage(car, 0, 0);
// The whole silhouette is covered, glass and wheels included: the API ships the car's
// alpha, not per-panel masks. Good enough for a quote; a solid color should use color=<hex> instead.

Matching a paint or a wrap color by hex

Since paints became a recolor of one neutral render, color accepts any six-digit hex next to the 15 preset names: color=686c70 bare in a URL, "color": "#686c70" in JSON. Look up the manufacturer's swatch, or the customer's factory paint code, and pass it. The recolor is applied to the paint only, so glass, lights, trim and wheels stay as they are, which is exactly the difference between a wrap preview and a tinted photo. Every hex is one more cached variant and one credit, so a palette of twenty candidates for one car costs twenty credits, once.

A 3D model for the turntable and AR

When the quote page needs the car to turn, order a model. POST /api/v1/3d takes the vehicle id and a color, charges 100 credits, and builds a textured GLB, a browser build of it, a USDZ and an FBX, plus a thumbnail. The first model of a vehicle takes 6–10 minutes; every further color of that vehicle retextures the same mesh in 1–2 minutes. Ordering a vehicle and color you already hold is free (billing.already_owned: true), so a shop can key its configurator on the same white base for every job.

Order it in white and publish it at once
curl -X POST https://carimage.dev/api/v1/3d \
  -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wrap-quote-4471" \
  -d '{"vehicle": "veh_0ta0ejj0qad2w", "color": "white", "publish": true}'
202 Accepted (trimmed)
{
  "data": {
    "id": "8c1d6b8e-2b4a-4f6e-9a2e-1f3c5d7e9b01",
    "object": "3d_model",
    "status": "queued",
    "progress": 0,
    "vehicle": {
      "id": "veh_0ta0ejj0qad2w",
      "make": "bmw",
      "model": "m4",
      "year": 2021
    },
    "color": "white",
    "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,
    "credits_remaining": 24900,
    "already_owned": false
  },
  "request_id": "req_01j9y…"
}

publish: true gives the model key-free URLs on our CDN and an embed before it is even ready; the element shows progress until the files land. Set view to any of the eight camera angles for the opening shot, spin for a turntable, backdrop for the studio floor, and ar to offer the USDZ to iPhones and the GLB to Android through the browser's own AR viewer. The FBX is for the designer: open it in Blender or a wrap-design tool, paint the artwork onto the UVs, and the customer sees their own design on their own car.

The embed on the quote page
<script src="https://carimage.dev/embed/3d.js" async></script>
<car-3d model="m3d_…" view="front-3-4" spin backdrop="studio" ar alt="2021 BMW M4 in white"></car-3d>

Running a batch from ChatGPT or Claude

Not every shop has a developer, and one of the busiest integrations on the API is an operator asking an assistant for renders. Add https://carimage.dev/api/mcp as a connector (ChatGPT and Claude sign in through OAuth; the human approves the scopes on screen) and the assistant gets the same tools a program would: resolve_vehicle to turn "2014 Audi S3 cabriolet, grey, side" into exact parameters, get_car_image for one render, create_car_image_urls for up to fifty signed URLs in one call, create_3d_model for the turntable.

Two things make a batch from an assistant go well:

  • Pass an idempotency_key per batch. Assistants retry. With a key, a retried create_car_image_urls replays the first result (idempotent_replayed: true) and bills nothing.
  • Expect the render budget, not just the rate limit. Renders of variants nobody has asked for before draw on the account's daily share of generation capacity (10% on Pro). A run of a few hundred new cars in three views can reach it; the tool then answers with a retry time and reset_at, nothing is charged, and cached images keep flowing. Spread the run over an evening or two, or move to Business for a larger share.

Setup for each client, and the full tool list, are on the MCP page.

CLI, SDK and MCP

npx @meterapp/car-image
# the white base and a hex paint, straight to files
npx @meterapp/car-image get --vehicle veh_0ta0ejj0qad2w --view side --color white --width 1024 --out m4-side-white.png
npx @meterapp/car-image get --vehicle veh_0ta0ejj0qad2w --view side --color 686c70 --width 1024 --out m4-side-nardo.png

# the 3D model: published at once, polled until ready, every file downloaded
npx @meterapp/car-image 3d create --vehicle veh_0ta0ejj0qad2w --color white --publish --wait --out ./models
With the TypeScript SDK
import { CarImageClient } from "@meterapp/car-image-sdk";

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

// 100 credits the first time. Ordering the same vehicle in the same color again
// answers billing.already_owned: true and charges nothing.
const { data: model } = await client.create3dModel({ vehicle: "veh_0ta0ejj0qad2w" }, { color: "white", publish: true });
const ready = await client.wait3dModel(model.id, { onProgress: (m) => console.log(m.stage, m.progress) });

// FBX for the designer's tool of choice, USDZ for the customer's iPhone, glb_web for the page.
const fbx = await client.download3dModel(ready.id, "fbx");
const embedHtml = ready.public?.embed.html;
  • MCP: get_car_image, create_car_image_urls, create_3d_model, get_3d_model and publish_3d_model in the core toolset; every image tool takes vehicle and a hex color.
  • The 3D docs cover webhooks (one signed POST when a model settles), file sizes and every embed attribute; views & sizes covers the paint rules.

Frequently asked questions

Does a custom hex color cost more than a preset?
No. Every color is a recolor of one neutral render of that vehicle and view, so a preset, a factory code you looked up and a wrap manufacturer's swatch all cost one credit. A hex that equals a preset's swatch is that preset.
Is the render accurate enough to cut film from?
No. Renders are generated product visuals of the make, model and year, not CAD data or OEM photography: the catalog does not separate trims, and panel lines can differ between two views of the same car. Use them to show the customer their car and to quote the job; cut patterns from a template or the vehicle itself.
Can we get the car without paint, or just the windows?
The closest to a blank is color=white on a transparent PNG, and the PNG's alpha is the car's silhouette. The API does not ship per-panel or glass masks. For a solid-color wrap let color=<hex> do the work: it recolors the paint and leaves glass, trim and wheels as they are.
Which views should a tint or PPF quote use?
The straight views: side (or side-right) for the door glass and the flanks, front for the windshield strip, hood and bumper, rear for the back glass. The three-quarter views are the ones customers respond to, so use front-3-4 for the hero and the straight views next to the line items.
Do we own the 3D model?
It is licensed, like the renders: paid once per vehicle and color per account, free to order again, yours to use inside your product and keep on your servers while the plan is active. Every further color of a vehicle you already hold retextures the same mesh, and publishing gives the model key-free URLs and an embed at no cost.