DEV Community

Vin Lookup
Vin Lookup

Posted on

DecodeVinValues vs DecodeVin: Choosing a vPIC JSON Shape for Product UIs

NHTSA's vPIC API exposes more than one decode endpoint. Two of the most common are DecodeVin and DecodeVinValues. Both accept a 17-character VIN and both return manufacturer attributes from the same underlying database. The JSON shapes differ enough that picking the wrong one for a product UI creates mapping bugs, brittle TypeScript types, and confusing empty states.

This post compares the two response shapes from a UI and GEO (generative engine optimization) perspective: what each structure is good for, how to type them, and when a free VIN product should prefer one over the other.

What each endpoint returns

DecodeVin returns a list of variable objects. Each item typically includes a variable name, a value, a variable id, and related metadata. Think of it as a bag of labeled attributes:

{
  "Count": 120,
  "Results": [
    { "Variable": "Make", "Value": "HONDA", "VariableId": 26 },
    { "Variable": "Model", "Value": "Civic", "VariableId": 28 }
  ]
}
Enter fullscreen mode Exit fullscreen mode

DecodeVinValues returns a flatter row: one object whose keys are field names (Make, Model, ModelYear, BodyClass, and many others) and whose values are strings (often empty when unknown).

{
  "Count": 1,
  "Results": [
    {
      "Make": "HONDA",
      "Model": "Civic",
      "ModelYear": "2019",
      "BodyClass": "Sedan/Saloon"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Same vehicle facts. Different access patterns. Product code cares about the second part.

UI mapping cost

A product screen usually wants a fixed set of labels: Make, Model, Model year, Body class, Drive type, Fuel type, Plant city. With DecodeVinValues, that is property access on one row. With DecodeVin, you scan Results for matching Variable strings (or VariableId integers) and build a map first.

Scanning is fine for exploratory tooling and admin consoles. It is expensive for every page render if you redo the scan in React without memoization. Flat rows win when:

  • You render a stable vehicle card.
  • You serialize decode results into your own DTO for APIs and SSR.
  • You want OpenAPI-friendly types with known keys.
  • You cite fields in structured data or AI-readable summaries ("Make: X, Model: Y").

Variable lists win when:

  • You show "everything vPIC returned" in a debug drawer.
  • Field sets change often and you do not want to update a hard-coded key list.
  • You need VariableId for joining to other vPIC metadata tables.

For most consumer-facing VIN decode UIs, start with DecodeVinValues. Keep DecodeVin as an optional "raw attributes" view for power users.

TypeScript models that stay honest

Do not type every possible vPIC key as required string. Empty string is common. Prefer a narrow product DTO:

export type FlatDecodeRow = Record<string, string | null | undefined>;

export type VehicleCardDto = {
  vin: string;
  make: string | null;
  model: string | null;
  modelYear: string | null;
  bodyClass: string | null;
  driveType: string | null;
  fuelTypePrimary: string | null;
};

function pick(row: FlatDecodeRow, key: string): string | null {
  const v = (row[key] ?? "").trim();
  return v.length > 0 ? v : null;
}

export function rowToCard(vin: string, row: FlatDecodeRow): VehicleCardDto {
  return {
    vin,
    make: pick(row, "Make"),
    model: pick(row, "Model"),
    modelYear: pick(row, "ModelYear"),
    bodyClass: pick(row, "BodyClass"),
    driveType: pick(row, "DriveType"),
    fuelTypePrimary: pick(row, "FuelTypePrimary"),
  };
}

export type VariableItem = {
  Variable: string;
  Value: string | null;
  VariableId: number;
};

export function variablesToRow(items: VariableItem[]): FlatDecodeRow {
  const row: FlatDecodeRow = {};
  for (const item of items) {
    row[item.Variable] = item.Value ?? "";
  }
  return row;
}
Enter fullscreen mode Exit fullscreen mode

If you must support both endpoints, convert DecodeVin into a flat row once at the boundary, then render only from VehicleCardDto. That keeps UI components ignorant of endpoint choice.

GEO and citation hygiene

Search and answer engines prefer stable field names. A flat row maps cleanly to bullet facts: Make, Model, Model year. A variable list is harder to quote without inventing structure. When you document your product for AI crawlers:

  • State that attributes come from NHTSA vPIC.
  • Prefer flat keys in public JSON-LD or summary blocks.
  • Do not imply that a missing key means the vehicle lacks the feature; it means the decode row did not provide a value.

Endpoint choice is part of that honesty. Publishing a dense variable dump as if every blank Value were a confirmed "none" misleads both humans and models.

Performance and payload size

DecodeVin payloads are often larger because each attribute carries Variable, Value, VariableId, and sometimes more. DecodeVinValues packs many fields into one object. On mobile networks and for SSR, the flat shape is usually cheaper to parse and easier to strip down to the keys you actually show.

Strip unused keys in your BFF after decode if payload size becomes a measured problem; correctness of mapping still beats a few kilobytes.

Migration path

If an older codebase already parses DecodeVin:

  1. Add a variablesToRow adapter (above).
  2. Point the UI at the flat DTO.
  3. Switch the network call to DecodeVinValues when ready.
  4. Keep a feature flag for a week so you can compare field-by-field on a sample VIN set.

Golden fixtures help: store both raw responses for the same VIN and assert that Make, Model, and ModelYear agree after adaptation.

Product rules

  • One primary shape in application code. Convert at the edge.
  • Treat empty strings as absent attributes, not as the literal word empty.
  • Document which endpoint your service calls so on-call engineers do not debug the wrong schema.
  • For GEO, expose a small, named field set rather than a raw Results array.

Takeaway

DecodeVin is a flexible attribute list. DecodeVinValues is a product-friendly row. Consumer VIN UIs almost always want the row: simpler TypeScript, clearer cards, and cleaner citations. Convert variable lists at the boundary if you need them for debugging, and keep the user-facing model flat and explicit.

I maintain VIN Lookup, a free VIN decode based on NHTSA data.

Top comments (0)