Car Image API
Documentation

Ride-share territories

Draw service zones from Geocode AI boundaries, tell which zone each driver is in, and move top-view car renders around a live map.

The fleet, live

Six cars drive across Washington, DC, split into four service zones made of its eight wards. The zone lines are Geocode AI boundaries, also made by Meter; the cars are Car Image renders in the top view, straight down from above, turned to each car's heading.

Ward 1Ward 2Ward 3Ward 4Ward 5Ward 6Ward 7Ward 82025 Toyota Camry2023 Honda Accord2018 Tesla Model 32023 Honda Odyssey2020 Toyota Camry2020 Porsche 911
Northwest0Uptown0Capitol & Waterfront0East of the River0

Ward boundaries by Geocode AI, also made by Meter. Cars are Car Image top views; each ring takes the color of the zone the car is in.

A zone is a list of boundaries

Store each service area as the ids of the city's own boundaries (wards, districts, postcodes) rather than a hand-drawn polygon. Searching and low-detail polygons are free and need no key; merging several into one shape for the map takes a Geocode AI key. Fetch the polygons once and cache them: ward lines change rarely.

bash
# free, no key: find the boundaries a zone is made of
curl "https://geocodeai.dev/v1/boundaries/search?q=Ward%203%20Washington&kind=ward"
# → results[0].id: "r16408680"

# free at low detail: the polygon itself, as a GeoJSON Feature
curl "https://geocodeai.dev/v1/boundaries/r16408680?detail=low"

# one polygon for a zone of several wards (Wards 1, 2 and 3)
curl -X POST "https://geocodeai.dev/v1/boundaries/union" \
  -H "Authorization: Bearer $GEOCODE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"include":["r16408707","r16408810","r16408680"],"detail":"low"}'

Which zone is each driver in

On your server, test the latest fix of every driver against each zone in one batch. The answer lists one true or false per point, in order.

typescript
const GEOCODE = "https://geocodeai.dev";

/** Each zone as the Geocode AI boundaries it is made of. */
const zones = {
  northwest: ["r16408707", "r16408810", "r16408680"],
  east: ["r16394083", "r16389580"],
};

/** Drivers' last GPS fixes as [lon, lat]; up to 10,000 points per call. */
export async function zoneOf(drivers: [number, number][]) {
  const zone: (string | null)[] = drivers.map(() => null);
  for (const [name, ids] of Object.entries(zones)) {
    for (const id of ids) {
      const res = await fetch(`${GEOCODE}/v1/boundaries/${id}/contains`, {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.GEOCODE_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ points: drivers }),
      });
      // { id, total, inside_count, inside: [true, false, …] }
      const { inside } = (await res.json()) as { inside: boolean[] };
      inside.forEach((hit, i) => { if (hit) zone[i] ??= name; });
    }
  }
  return zone; // null: outside every zone
}

Top-view cars as markers

Mint one signed URL per vehicle in the fleet on your server, each with view: "top", and store it with the vehicle. The page gets a plain image URL; the key never reaches the browser. Each URL counts one vehicle image when it is created and loads free until it expires; renew: true keeps it serving. Look each vehicle up once for its id (by VIN, see Dealer & VIN tools).

typescript
/** One signed URL per fleet vehicle: straight down from above, trimmed, transparent. */
export async function fleetMarkers(vehicleIds: string[]) {
  const res = await fetch("https://carimage.dev/api/v1/image-urls", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CAR_IMAGE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      images: vehicleIds.map((vehicle) => ({
        vehicle, view: "top", width: 96, height: 192, fit: "inside", trim: true, format: "webp",
      })),
      ttl_seconds: 604800,
      renew: true,
    }),
  });
  const { data } = (await res.json()) as { data: { url: string }[] };
  return data.map((image) => image.url);
}

// fleetMarkers(["veh_errc87t1jgata", "veh_94m80sn8fvcge"])

Any map library takes an image as a marker. With MapLibre GL, rotate it with the map so the car points where it drives:

typescript
import maplibregl from "maplibre-gl";

// The render's nose points up, so a marker turns by the driver's heading.
export function carMarker(map: maplibregl.Map, url: string, fix: { lon: number; lat: number; heading: number }) {
  const img = document.createElement("img");
  img.src = url; // the signed URL from your server: no key in the browser
  img.style.height = "40px";
  return new maplibregl.Marker({ element: img, rotation: fix.heading, rotationAlignment: "map" })
    .setLngLat([fix.lon, fix.lat])
    .addTo(map);
}

// On each GPS fix: marker.setLngLat([lon, lat]).setRotation(heading)

Lifetimes, renewal and billing are on Signed URLs; every Geocode AI operation and its cost is in the Geocode AI docs.