Car Image API

Casos de uso · Subastas

Imágenes de coches para anuncios de subastas

Publique lotes de subasta antes de que lleguen las fotos, cada uno con una imagen de estudio de su año de modelo etiquetada como render de catálogo, mediante URL firmadas que caducan con la subasta.

Una guía de implementación escrita a partir del comportamiento documentado de la API; no menciona a ningún cliente. Publicado el .

El problema: la subasta se publica antes que las fotos

Un catálogo de subasta se publica en cuanto llegan los vehículos en consignación: un VIN, un número de lote y una fecha de venta para cada vehículo. Las fotos llegan cuando el vehículo llega al patio y el fotógrafo llega al vehículo, lo que en parte de una subasta ocurre después de que se abran las pujas. Hasta entonces, cada uno de esos lotes es un recuadro gris junto a una línea de texto.

Un render de catálogo llena el hueco con el año de modelo correcto. Pero una subasta es justo donde un render nunca debe hacerse pasar por el coche: los pujadores valoran el estado, y un render no muestra ninguno, porque no representa ningún vehículo físico concreto. Todo lo que sigue se deriva de tener presentes ambas cosas a la vez.

Cómo es la integración

  • Un render por lote a partir de su VIN, que se muestra solo hasta que llegan las fotos propias del lote.
  • Un selector del catálogo para los lotes cuyo VIN no se puede decodificar.
  • Una etiqueta en la imagen: "Render de catálogo, no este lote".
  • Una URL por lote, generada una sola vez con una Idempotency-Key, vigente mientras dure la subasta.

Paso 1: del VIN del lote a un id de vehículo

GET /api/v1/vin/{vin} es gratis. Cuando el catálogo tiene el vehículo, la respuesta incluye data.vehicle.id, el id estable que debe guardar en el lote; la versión, la carrocería y el motor decodificados se quedan en el texto del lote. El catálogo abarca de 1990 a 2027, así que el stock más antiguo de una subasta se renderiza igual que el más nuevo. La guía de seguros explica una decodificación campo por campo, y la documentación de VIN trata los VIN parciales y los errores.

Decodificar en la recepción
# the lot's VIN to the vehicle id the lot stores (free); nothing printed means no catalog match
npx @meterapp/car-image vin "$LOT_VIN" --json | jq -r '.data.vehicle.id // empty'

Paso 2: ¿el VIN no se puede decodificar? Elija el vehículo en el catálogo

Algunos lotes llegan sin un VIN que se pueda decodificar: un vehículo fabricado para otro mercado, un VIN que la fuente escribió mal, una hoja de consignación con un año, una marca y un modelo y nada más. Dote al formulario de consignación de tres menús desplegables alimentados por GET /api/v1/vehicles, que es gratuito y público: ?year= lista las marcas que tienen algún modelo en ese año, y ?year=&makeId= lista los modelos de esa marca, cada uno con el id de vehículo de ese año de modelo.

Año, luego marca, luego modelo
# every make with a 2006 model year (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?year=2006"
# {"data":{"year":2006,"makes":[…,{"id":476,"name":"DODGE","slug":"dodge"},…]},…}

# that make's 2006 models, each with the id of its 2006 model year
curl -s "https://carimage.dev/api/v1/vehicles?year=2006&makeId=476"
200 OK (recortado)
{
  "data": {
    "year": 2006,
    "make_id": 476,
    "models": [
      {
        "name": "Caliber",
        "slug": "caliber",
        "vehicle_type": "Passenger Car",
        "id": "veh_294vq81yq0d3y"
      },
      {
        "name": "Charger",
        "slug": "charger",
        "vehicle_type": "Passenger Car",
        "id": "veh_96j8z47jqfd4r"
      },
      "…"
    ]
  }
}

El mismo catálogo es el paquete abierto @meterapp/vehicle-db, así que los desplegables también pueden funcionar sin conexión con la misma lista. En cualquier caso, el lote termina guardando el mismo tipo de id que un VIN decodificado, y ningún paso posterior tiene que escribir el nombre de un vehículo.

Cuando llega una hoja de consignación completa de una vez, POST /api/v1/vehicles/check lee hasta 2000 entradas por llamada, cada una tal como la escribe la hoja y con el número de lote como su ref, y responde a todas: una coincidencia con el id que debe guardar, sugerencias, un fallo o una fila no válida. No se renderiza nada ni se cobra nada; la documentación de vehículos tiene los detalles.

Paso 3: indique que la imagen es un renderizado

Ponga la etiqueta en la propia imagen, donde se posa la mirada del pujador, y en el texto alternativo, no solo en la descripción de abajo. El estado, el kilometraje, los daños, el equipamiento y la pintura del coche van en el texto del lote y, cuando existan, en las fotos. Cuando lleguen las fotos, el render se retira.

La imagen del lote hasta que lleguen sus fotos
<figure class="lot-image">
  <img src="https://carimage.dev/api/v1/delivery/eyJhbGciOi…" width="800" height="450"
       alt="Catalog render of a 2006 Dodge Charger, not a photo of lot 117">
  <span class="lot-image__badge">Catalog render, not this lot</span>
</figure>
<p>Photos of this lot are on their way. Condition, mileage and damage are in the report.</p>

Paso 4: una URL por lote, generada una sola vez

Genere la URL del lote al darlo de alta con POST /api/v1/image-urls y una Idempotency-Key formada a partir de la venta y el lote, con letras, dígitos, ., _, : y -. La URL se paga al generarse (1 crédito), así que sin clave un alta reintentada la generaría y pagaría dos veces. Con una:

  • La misma clave con el mismo cuerpo dentro de un plazo de 24 horas reproduce la primera respuesta, incluida la misma URL, con Idempotent-Replayed: true y sin un segundo cargo.
  • La misma clave con un cuerpo distinto devuelve un 422, y un reintento que se adelanta a la primera solicitud devuelve un 409 con un Retry-After. Así que fije todo el cuerpo, incluida la vigencia, al crear el lote, y guárdelo junto con el lote.
  • Guarde también la URL en el lote: la clave protege los reintentos, y la URL guardada es lo que leen las páginas durante el resto de la subasta.

Un catálogo completo importado de una vez puede generar hasta 50 lotes por llamada, con una clave por tanda; cada URL de la respuesta repite el vehículo y las opciones para los que se generó.

URL del lote 117
curl --fail-with-body -X POST https://carimage.dev/api/v1/image-urls \
  -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sale-2026-10-14:lot-117" \
  -d '{"images":[{"vehicle":"veh_96j8z47jqfd4r","view":"side","width":800,"height":450,"fit":"contain","trim":true,"padding":6,"format":"auto"}],"ttl_seconds":259200}'
# run it again: the same URL comes back with Idempotent-Replayed: true, and nothing more is charged

Paso 5: ajuste la duración de la URL a la de la subasta

ttl_seconds admite hasta 604800 (7 días); un valor mayor se limita a ese máximo. Para una subasta que cierra dentro de la semana, establézcalo en el tiempo que falta hasta el cierre. Así, la imagen provisional deja de funcionar cuando terminan las pujas y, a partir de entonces, la URL responde 403.

  • Una venta con plazo de más de 7 días: genere la URL con renew: true, ttl_seconds al máximo y renew_days cubriendo la venta. Cada semana adicional en la que se abre el lote cuesta 1 crédito, las semanas sin actividad no cuestan nada y la renovación se detiene en renews_until.
  • Mantenga max_uses en 0, el valor predeterminado: una URL ilimitada se puede almacenar en caché públicamente durante toda su vigencia, mientras que una limitada es private, no-store y envía cada carga al origen.
  • Restrinja la clave que genera las URL a los dominios de su subasta en el panel: una imagen de lote copiada en otro sitio responde 403.
Alta de vehículos con el SDK de TypeScript
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";

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

const lotImage = (vehicle: string): ImageParams => ({
  vehicle, view: "side", width: 800, height: 450, fit: "contain", trim: true, padding: 6, format: "auto",
});

// How long the lot's URL lives, decided once from the sale's close.
function lifetime(closesAt: Date) {
  const seconds = secondsUntil(closesAt);
  return seconds <= 604800
    ? { ttlSeconds: Math.max(seconds, 60) } // closes within the week
    : { ttlSeconds: 604800, renew: true, renewDays: Math.min(daysUntil(closesAt), 365) }; // a longer timed sale
}

// At intake, once per lot. The lifetime is stored with the lot, so a retry sends
// the same body and replays the first answer instead of minting a second URL.
lot.imageLifetime ??= lifetime(sale.closesAt);
const { data: [placeholder] } = await client.createImageUrls(lotImage(lot.vehicleId), {
  ...lot.imageLifetime,
  idempotencyKey: `${sale.id}:lot-${lot.number}`,
});
lot.placeholderUrl = placeholder.url; // replaced by the lot's own photos when they arrive

Planifique según el tamaño de la subasta

Cada lote es un vehículo distinto, salvo que dos lotes compartan marca, modelo y año, y cada plan limita los vehículos distintos al mes: Free 100, Pro 2500 y Business 15.000; Enterprise: sin límite. Un vehículo ya servido este mes siempre se permite, y una petición en bloque que superaría el límite se rechaza entera, antes de cobrar nada, con el plan y el límite en la respuesta. Dimensione el plan según los lotes que publique en un mes, no según los créditos. La historia de marketplace explica cómo dosificar una importación grande dentro de los límites de tasa y de renderizado.

Preguntas frecuentes

¿Puede seguir visible el render cuando lleguen las fotos del lote?
Sustitúyala. El render es una imagen provisional del año de modelo, y un postor nunca debería tener que preguntarse qué imagen corresponde al coche que se vende. Si una página la conserva como referencia, debe mantener la etiqueta.
¿Qué pasa con la URL después de la venta?
Caduca cuando termina su vigencia, o cuando una URL renovable llega al final de su renovación, y a partir de entonces responde 403. Para entonces, el lote ya tiene sus propias fotos o un resultado, y el archivo de la subasta debería mostrar las fotos. Revocar la clave que generó las URL las anula todas a la vez.
Un reintento devolvió 422. ¿Por qué?
La misma Idempotency-Key volvió con un cuerpo distinto. La causa habitual es una vigencia recalculada a partir del reloj en el momento del reintento. Fije la vigencia al crear el lote y guárdela; así el reintento envía el mismo cuerpo y repite la primera respuesta.
¿Puede el render mostrar la pintura del lote?
Sí: color acepta cualquiera de los nombres predefinidos o un hex, así que la imagen puede coincidir con la pintura de la hoja de consignación sin coste adicional. Sigue siendo el modelo en esa pintura, no el lote, así que la etiqueta se mantiene.
¿Cuánto cuesta una subasta de 400 lotes?
400 créditos: 1 crédito por lote cuando se emite su URL, y una emisión reintentada no cobra nada. Cada carga durante la vigencia de la URL es gratis. Los planes limitan los vehículos distintos al mes (Free 100, Pro 2500 y Business 15.000; Enterprise: sin límite), y los lotes que comparten marca, modelo y año cuentan una sola vez.

Siga leyendo

Caso de cliente · Marketplaces de automociónCómo poner una imagen de estudio del vehículo en cada anuncio de un marketplaceUn anuncio necesita una imagen antes de que el vendedor suba una. Los marketplaces completan todo un catálogo con un ángulo y un color, y luego mantienen la misma forma de solicitud para cada nuevo año de modelo.