Cargar y mantener el lote
Qué autos entran al lote público, qué trae cada uno y cómo se filtra.
AutomotorasSolo en los workspaces de automotoras.
GET /stock devuelve el lote público de una automotora. Son tres endpoints de lectura sobre esa misma superficie.
curl https://api.vitrinadev.com/api/v1/stock \
-H "Authorization: Bearer $VITRINA_KEY"Es la superficie que consume el sitio web de la automotora. El inventario es otra cosa y vive en /vehicles. Ahí están las patentes, los costos y los pisos de precio, detrás de permisos que un sitio web no debería tener.
Qué autos entran al lote
Un auto aparece aquí si está activo, no fue borrado, no fue fusionado con otro y no está vendido.
Lo que sorprende de esa lista es lo que no contiene: no hay ninguna condición de "publicado en un portal". El sitio propio de la automotora muestra todo su lote activo, no solo los autos que además están cruzados a Chileautos, Yapo o Mercado Libre. published_at existe como dato y sirve para ordenar, pero no filtra nada.
Los reservados sí aparecen, con status: "reservado" y su reserved_at, para que puedas pintarles la etiqueta en vez de hacerlos desaparecer. Los vendidos no aparecen, salvo que la automotora haya pedido expresamente que sus ventas recientes se queden a la vista.
El objeto
Así viene un auto, completo, tal como lo devolvió GET /stock/{id}:
{
"data": {
"id": "08eecaca-10bc-40b0-a2eb-d0915dba1cc9",
"make": "Toyota",
"model": "Corolla",
"version": "1.8 XEI CVT",
"year": 2022,
"title": null,
"price": { "amount": 13990000, "currency": "CLP" },
"odometer": { "value": 42500, "unit": "KM" },
"fuel_type": "Bencina",
"fuel_label": "Bencina",
"gear_type": "Automática",
"gear_label": "Automática",
"body_style": "Sedán",
"body_label": "Sedán",
"color": "Gris",
"doors": 4,
"displacement_cc": null,
"type": "Car",
"type_label": "Auto",
"merch_label": null,
"merch_label_es": null,
"is_featured": false,
"featured_at": null,
"listing_type": "Usado",
"status": "disponible",
"reserved_at": null,
"sold_at": null,
"sucursal": {
"id": "6812d9f0-9bed-44b5-91df-4e5b90941f6b",
"name": "Sucursal Providencia",
"address": null,
"address_street": "Av. Nueva Providencia",
"address_number": "2214",
"address_unit": null,
"comuna_code": "13123",
"comuna_name": "Providencia",
"region_code": "13"
},
"photos": [{ "url": "https://images.example.cl/corolla-2022-1.jpg", "order": 0 }],
"description": "Mantenimientos al día en concesionario.",
"tags": [],
"equipment": [],
"created_at": "2026-09-15T18:34:48.575Z",
"published_at": null
}
}Algunas cosas que no se deducen mirándolo:
Los pares *_type y *_label no son redundantes. El primero es el valor tal como lo guardó la automotora o lo entregó su feed; el segundo es la etiqueta en español lista para mostrar. Cuando coinciden es porque la automotora ya escribió el valor canónico. Si vas a renderizar, usa *_label; si vas a agrupar o comparar, usa *_type.
price.amount es un entero en la moneda de price.currency, sin decimales ni separadores. En pesos chilenos, 13990000 son 13.990.000.
sucursal viene embebida: no tienes que pedir la sucursal por separado para mostrar dónde está el auto. Trae comuna_name ya resuelto desde el código CUT.
photos viaja tal como la guardó la automotora, con el order de cada foto adentro. La API no la reordena, así que ordénala tú por order antes de elegir la portada.
Trampa
No pidas la patente: no está
El objeto público no trae registration_number ni vin, y no hay parámetro
que los agregue. Tampoco salen los precios internos, la fuente ni de quién es
el auto. Si necesitas esos datos, el recurso es /vehicles.
Filtrar
Todos los filtros son parámetros de query y se combinan con Y lógico.
| Parámetro | Qué hace |
|---|---|
brand, model | Marca y modelo. |
body | Carrocería. |
type | Tipo de unidad: Car, Truck, Motorcycle o Boat. |
gearbox, fuel_type | Transmisión y combustible. |
min_year, max_year | Rango de año. |
min_price, max_price | Rango de precio, en la moneda del lote. |
min_odometer, max_odometer | Rango de kilometraje. |
created_from, created_to | Rango de fecha de carga. |
sucursal | El uuid de una sucursal. |
status | disponible, reservado o vendido. |
featured | Solo los destacados que eligió la automotora. |
curl "https://api.vitrinadev.com/api/v1/stock?body=SUV&max_price=17000000&sort=price_asc" \
-H "Authorization: Bearer $VITRINA_KEY"Sobre un lote de tres autos, ese filtro devolvió uno: el Hyundai Tucson, a 15.200.000. El Mazda CX-5 también es SUV, pero cuesta 18.490.000.
sort acepta price_asc, price_desc, created_asc, created_desc, published_asc, published_desc y featured_desc. Sin sort, el orden es created_desc: lo último cargado primero.
gearbox, fuel_type y body aceptan tanto el código canónico como la etiqueta en español. Las dos formas devuelven las mismas filas:
curl "https://api.vitrinadev.com/api/v1/stock?gearbox=automatica" -H "Authorization: Bearer $VITRINA_KEY"
curl "https://api.vitrinadev.com/api/v1/stock?gearbox=Autom%C3%A1tica" -H "Authorization: Bearer $VITRINA_KEY"No pasa lo mismo con type, que sí es un enum cerrado y rechaza lo que no reconoce:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": { "query": [{ "path": "type", "message": "Invalid enum value. Expected 'Car' | 'Truck' | 'Motorcycle' | 'Boat', received 'Bus'", "code": "invalid_enum_value" }] },
"requestId": "7b0841cd-0345-4cb5-a2fb-3dbb4ad4285c"
}
}Trampa
status=vendido se acepta y devuelve cero filas
El filtro se aplica encima del lote público. Si la automotora no pidió
mostrar sus ventas recientes, la consulta no devuelve nada. Qué se muestra lo
decide la automotora, no quien llama, y no existe un parámetro include_sold.
Contar
Para el «12 autos disponibles» que va arriba de la grilla hay un endpoint que no trae los autos:
curl https://api.vitrinadev.com/api/v1/stock/count \
-H "Authorization: Bearer $VITRINA_KEY"{ "data": { "count": 3 } }Acepta exactamente los mismos filtros que la lista. GET /stock/count?status=disponible cuenta lo mismo que contarías paginando: sobre ese lote de tres, devolvió 2, porque el Tucson estaba reservado.
Paginar
limit y offset, no cursores.
curl "https://api.vitrinadev.com/api/v1/stock?limit=1&offset=1" \
-H "Authorization: Bearer $VITRINA_KEY"{
"data": [ { "make": "Mazda", "model": "CX-5" } ],
"meta": { "pagination": { "limit": 1, "offset": 1 } }
}meta.pagination devuelve los valores efectivos, no los que pediste. El limit por defecto es 20 y el máximo es 100:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": { "query": [{ "path": "limit", "message": "Number must be less than or equal to 100", "code": "too_big" }] },
"requestId": "278945a0-d78d-470c-ad5c-86b029724a96"
}
}No viene un total. Si necesitas saber cuántas páginas hay, pide GET /stock/count con los mismos filtros.
Una unidad
curl https://api.vitrinadev.com/api/v1/stock/08eecaca-10bc-40b0-a2eb-d0915dba1cc9 \
-H "Authorization: Bearer $VITRINA_KEY"Devuelve el mismo objeto de la lista. Un id que no existe, o uno que existe pero está fuera del lote público, responde igual:
{
"error": {
"code": "NOT_FOUND",
"message": "Vehículo no encontrado",
"requestId": "e3c36709-4fff-462d-8940-4ff9e8138ce6"
}
}Los dos casos son un 404 y no se distinguen desde afuera. Que un auto exista en el inventario de una automotora no es información pública.
Un id que ni siquiera es un uuid falla antes, con 400:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": { "params": [{ "path": "id", "message": "Invalid uuid", "code": "invalid_string" }] },
"requestId": "6c0a7f7f-3147-4b29-8ce0-a7548c03aa51"
}
}El bloque sucursal sale de Sucursales, y el contrato de los tres endpoints está en Referencia · Stock público.