Tutorial · NHTSA vPIC API

    NHTSA VIN Decoder API tutorial: todo lo que tu equipo necesita para integrarla esta semana

    El NHTSA vPIC API es gratis, no requiere API key y devuelve hasta 140 atributos por VIN. Este tutorial cubre los endpoints reales (DecodeVin, DecodeVinValues, DecodeVinValuesBatch), código listo para copiar en JavaScript, Python y PHP, estrategia de cache, manejo de errores y cómo encadenarlo a tu EPC o ERP en producción.

    Lectura de 12 minutos · Actualizado junio 2026 · Por el equipo de Efficiency IT Services

    Resumen ejecutivo

    • Endpoint base: https://vpic.nhtsa.dot.gov/api/vehicles/ — sin API key, sin OAuth, sin headers especiales.
    • Tres métodos clave: DecodeVin (verbose), DecodeVinValues (plano, más rápido de parsear) y DecodeVinValuesBatch (hasta 50 VINs por llamada).
    • Validá el VIN en cliente (17 chars, sin I/O/Q, dígito verificador ISO 3779) antes de gastar la llamada.
    • Cachéa por VIN con TTL ≥ 30 días: la respuesta es determinística y nunca cambia.
    • Para producción a escala, usá el endpoint batch + cola con backoff exponencial; el rate limit no está documentado pero >5 req/s sostenidas suelen recibir 429.

    Qué es vPIC y cuándo usarlo

    vPIC (Vehicle Product Information Catalog) es la API pública del National Highway Traffic Safety Administration de Estados Unidos. Es la fuente oficial de información de vehículos que los fabricantes están obligados a reportar por ley federal desde 1981. Por eso, para cualquier vehículo vendido en EE.UU. en las últimas cuatro décadas, los datos de vPIC son la referencia más confiable disponible públicamente.

    El tutorial siguiente cubre el flujo completo desde la primera llamada hasta el despliegue en producción. Si necesitás contexto estratégico sobre cuándo conviene vPIC vs una API comercial vs el EPC oficial del fabricante, leé primero la guía pillar NHTSA vs API comercial.

    Endpoints disponibles en vPIC

    vPIC expone más de 20 endpoints, pero para decodificar VINs hay tres que importan en el 95% de los casos: DecodeVin, DecodeVinValues y DecodeVinValuesBatch. Los tres aceptan format=json, format=xml o format=csv y retornan la misma información en estructuras distintas.

    • DecodeVin/{VIN}?format=json — devuelve un array de objetos {Variable, Value, ValueId, VariableId}. Más verboso, ideal para debugging.
    • DecodeVinValues/{VIN}?format=json — devuelve un único objeto plano con todas las variables como propiedades. 30% menos bytes, parse trivial.
    • DecodeVinValuesBatch?format=json — POST con hasta 50 VINs separados por ; en el body. Indispensable a partir de 1.000 VINs/día.
    • Parámetro opcional modelyear={YYYY} — útil cuando el VIN no incluye año modelo en la posición 10 (vehículos pre-1980).

    Validar el VIN antes de gastar la llamada

    Cada VIN inválido que mandás a vPIC es una llamada perdida, latencia agregada al usuario y ruido en tus logs. La validación es trivial: 17 caracteres alfanuméricos, sin las letras I, O y Q (para evitar confusión con 1 y 0), y un dígito verificador en la posición 9 calculable según ISO 3779.

    El dígito verificador usa pesos fijos por posición y un mapeo de letra → valor numérico. La suma ponderada módulo 11 debe igualar el carácter en la posición 9 (donde 10 se representa como X). Esta validación detecta el ~95% de los typos sin tocar la red.

    • Regex base: ^[A-HJ-NPR-Z0-9]{17}$
    • Pesos ISO 3779 por posición (1-17): 8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2
    • Mapeo letras: A=1, B=2, C=3, D=4, E=5, F=6, G=7, H=8, J=1, K=2, L=3, M=4, N=5, P=7, R=9, S=2, T=3, U=4, V=5, W=6, X=7, Y=8, Z=9
    • Si la suma % 11 === 10 → check digit debe ser 'X'.

    Tutorial práctico en JavaScript / Node

    Este es el patrón mínimo que usamos en producción para el AutoParts AI Agent: una función pura validateVin, una función decodeVin con timeout y abort, y un wrapper con cache en memoria (en producción, Redis con TTL de 30 días).

    El timeout de 4 segundos cubre el p99 observado de vPIC (~600ms) con holgura. Si vPIC tarda más, asumimos que está degradado y devolvemos un fallback (en nuestro caso, intentamos con la API comercial de respaldo). Nunca dejes la llamada sin timeout: una request colgada bloquea el worker.

    • Usá fetch nativo (Node 18+) o undici — evitá axios para esta llamada, no agrega valor.
    • Pasá AbortSignal.timeout(4000) para cortar después de 4s.
    • Parseá solo los ~12 campos que de verdad usás (Make, Model, ModelYear, Trim, BodyClass, EngineConfiguration, DisplacementL, FuelTypePrimary, TransmissionStyle, DriveType, PlantCountry, ErrorCode).
    • ErrorCode === '0' significa decodificación exitosa; cualquier otro código (1, 6, 7, 8...) indica decodificación parcial o VIN inválido.

    Decodificar un VIN con la NHTSA vPIC API en 6 pasos

    Flujo de producción listo para copiar: validar, llamar, parsear, cachear, registrar métricas y degradar a fallback si vPIC falla.

    1. 1

      Validar el VIN en el cliente

      Verificá longitud 17, regex sin I/O/Q y dígito verificador ISO 3779 antes de gastar la red.

    2. 2

      Buscar en cache

      Si el VIN está en Redis con TTL vigente, devolvé el JSON cacheado en <5 ms. Hit rate típico >70%.

    3. 3

      Llamar a DecodeVinValues con timeout 4s

      GET https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/{VIN}?format=json con AbortSignal.timeout(4000).

    4. 4

      Parsear y normalizar

      Quedate con los 10-15 campos que importan (Make, Model, ModelYear, Trim, EngineConfiguration, FuelTypePrimary, etc.) y normalizá strings a tu schema interno.

    5. 5

      Cachear el resultado

      Guardá en Redis con SETEX vin:{VIN} 2592000 (30 días). vPIC actualiza su catálogo ~2 veces/año; revalidá manualmente cuando publiquen release notes.

    6. 6

      Emitir métricas y aplicar fallback

      Registrá latencia y ErrorCode. Si fallan 3 llamadas seguidas o devolvés timeout, ruteá temporalmente a una API comercial (DataOne, VinAudit) y alertá al canal de operaciones.

    Tutorial en Python (requests + asyncio)

    Para integraciones que ya viven en Python (data pipelines, scripts internos del concesionario, jobs de ETL nocturnos), recomendamos httpx con asyncio antes que requests síncrono. La diferencia es brutal a partir de los 100 VINs: httpx + asyncio.gather paraleliza bien y respeta el connection pool sin que tengas que hilar threads manualmente.

    Para una sola llamada interactiva (ej. una API interna que decodifica on-demand), requests es perfectamente válido y más simple de leer. La regla práctica: <50 VINs/llamada y latencia no crítica → requests; cualquier otra cosa → httpx async.

    • pip install httpx pydantic (Pydantic v2 valida la respuesta y te da typings reales)
    • Definí un BaseModel con los 10-15 campos que te importan y usá .model_validate(response.json()).
    • Timeout recomendado: httpx.Timeout(connect=2.0, read=4.0) — vPIC connect rápido pero read variable.
    • Para batch >50 VINs, dividí en chunks y procesá con asyncio.Semaphore(10) para no saturar tu propio worker.

    Decodificar VINs en batch (>50 VINs)

    El endpoint DecodeVinValuesBatch acepta hasta 50 VINs por POST. Por encima de eso necesitás chunking en el cliente. La sintaxis del body es un único campo data con VINs separados por ; — opcionalmente cada VIN puede llevar su año modelo: 1HGBH41JXMN109186,2021;5YJSA1E26HF000337,2017.

    En nuestro AutoParts AI Agent, cuando el cliente sube un Excel con la flota completa, partimos en chunks de 50 VINs y los procesamos con backoff exponencial. Una flota de 1.200 vehículos baja de ~12 minutos (uno a uno) a ~45 segundos (24 chunks paralelos con semáforo de 4 concurrentes).

    • Body Content-Type: application/x-www-form-urlencoded
    • data=VIN1;VIN2;VIN3...;VIN50
    • Para >1.000 VINs/día: implementá cola con BullMQ (Node) o Celery (Python) + retries con jitter.
    • El batch endpoint usa el mismo rate limit que las llamadas individuales — 50 VINs = 1 request, no 50.

    Manejo de errores y rate limiting

    vPIC no documenta un rate limit explícito, pero observacionalmente devuelve HTTP 429 (Too Many Requests) cuando sostenés >5 req/s desde una sola IP. La respuesta no incluye Retry-After, así que tu lógica de retry tiene que asumirlo: empezá con 500ms y duplicá hasta 8s, con jitter aleatorio del ±20%.

    Los errores semánticos viven en el campo ErrorCode de la respuesta (HTTP 200, ¡no error de red!). Es crítico inspeccionarlo: ErrorCode === '0' es éxito; '1' significa 'check digit incorrecto pero decodificamos lo que pudimos'; '6' es 'VIN incompleto'. Tratar todos los códigos != '0' como error completo es un anti-patrón: muchas veces tenés datos útiles en una decodificación parcial.

    • HTTP 200 + ErrorCode '0' → éxito limpio
    • HTTP 200 + ErrorCode '1'/'6'/'7' → decodificación parcial, usá los campos no vacíos
    • HTTP 429 → backoff exponencial con jitter, máximo 5 intentos
    • HTTP 5xx → retry una vez, luego degradar a API comercial si tenés fallback

    Pasar a producción: cache, observabilidad y fallback

    Una vez que el tutorial funciona en tu laptop, faltan tres cosas para producción: cache, métricas y plan B. Cache porque la respuesta nunca cambia para un VIN dado, así que pagar latencia más de una vez es derroche. Métricas porque sin saber cuántas decodificaciones fallan no podés decidir si necesitás un fallback comercial. Plan B porque vPIC es 99.9% confiable, pero cuando se cae tu funnel de venta se cae con él si no tenés alternativa.

    Patrón recomendado: cache key = vin, TTL infinito (revalidate manual si NHTSA actualiza el catálogo, lo cual hacen ~2 veces/año). Métricas: latencia p50/p95/p99, tasa de éxito por ErrorCode, tasa de hits del cache. Fallback: cuando vPIC devuelve 5xx o timeout por 3 minutos consecutivos, ruteá a una API comercial (DataOne, VinAudit) y emití una alerta a Slack.

    ¿Querés ver esta API real corriendo en una conversación de WhatsApp?

    Decodificamos un VIN en vivo, lo cruzamos contra catálogo y devolvemos cotización en menos de 2 minutos.

    Preguntas Frecuentes

    Todo lo que necesitas saber antes de comenzar.