Error Codes

Complete catalog of API error responses, status codes, and error types

This document catalogs all possible error types that clients may encounter when using the Auto.dev API Gateway.

Error Response Structure

All API errors follow a consistent JSON structure:

interface ApiError {
  status: number            // HTTP status code (400, 401, 404, 500, etc.)
  error: string             // Human-readable error description
  code: string              // Programmatic error identifier
  path: string              // API endpoint path that generated the error
  requestId: string         // Unique identifier for debugging and support
  suggestion?: string       // Helpful guidance for resolution (optional)
  message?: string          // Alternative error description (legacy/some endpoints)
  errorType?: string        // Legacy error type classification

  // Miss context (/build 404s; a /build, /vin or /specs 207 partial answer carries reason + retryable top-level, with no error block)
  reason?: string           // Machine-readable reason for the miss (see below)
  retryable?: boolean       // Whether asking again later can change the answer
  detectedMarket?: { country: string; region: string }   // VIN_NOT_IN_COVERAGE, by market
  detectedClass?: { vehicleType: string }                // VIN_NOT_IN_COVERAGE, by vehicle class

  // Plan-gating fields (402 responses)
  currentPlan?: string      // The plan the caller is on
  requiredPlan?: string     // The plan needed for this feature
  upgradeUrl?: string       // Preferred upgrade link (tier-targeted)
  upgradeLink?: string      // Alias of upgradeUrl (kept for backward compat)
  docsUrl?: string          // Link to the plans documentation
  plans?: Record<string, { price: string; rateLimit: string; features: string }>
}

Field Usage Across APIs:

  • status - HTTP status code (universal)
  • error - Primary error message (universal)
  • code - Error code identifier (universal)
  • path - Request path (universal)
  • requestId - Debug identifier (universal)
  • suggestion - Resolution guidance (taxes API)
  • message - Alternative / extended error description
  • errorType - Legacy VIN validation errors
  • reason, retryable - Why a /build lookup missed, and whether to retry (also top-level on the body, and on every 207)
  • detectedMarket, detectedClass - What put the VIN outside coverage (VIN_NOT_IN_COVERAGE)
  • currentPlan, requiredPlan, upgradeUrl, upgradeLink, docsUrl, plans - Plan-gating context (402 responses)

207 Multi-Status Is Not an Error

/build, /vin and /specs answer 207 Multi-Status when they can name the vehicle but not the whole of what the endpoint promises. The body is a partial answer, not an error: what is known, with reason and retryable top-level and no error block. A 207 is not billed and counts toward no quota. Most HTTP clients treat a 207 as ok, so branch on the status before reading the body as a 200.

endpointa 207 meansthe body holdsshape
/builda decoder names the car, no record builds itvehicle, build: null, catalog (what the matched style ships with, or null)OEM Build Data
/vina decoder names less than year, make and model, and its class or market says whyvin, vinValid, vehicle (manufacturer, type, year; make and model when named), yearAmbiguous + years when more than one year fits; no decode fieldsVIN Decode
/specsa source holds only some of the documented fieldsvehicle, specs with the held fields and null for the rest, missing[] naming the null onesSpecifications

A /build 207:

{
  "vehicle": { "vin": "1FATP8R0XT5502067", "year": 2026, "make": "Ford", "model": "Mustang", "body": "Coupe" },
  "build": null,
  "catalog": null,
  "reason": "no_build_record",
  "retryable": false
}

A /vin 207, a trailer from a small maker:

{
  "vin": "1T9P61527VP394134",
  "vinValid": true,
  "vehicle": { "vin": "1T9P61527VP394134", "year": 2027, "manufacturer": "The Metal Shop", "type": "Utility Trailers" },
  "yearAmbiguous": true,
  "years": [2027, 1997],
  "reason": "class_not_covered",
  "retryable": false
}

One reason vocabulary covers the three endpoints. On /build the same codes ride a 404 too: "if decoded" means a decoder named the year, make and model; otherwise the reason rides the 404. On /vin and /specs a reason appears only on a 207.

reasonendpointsstatusmeansretry
not_yet_available/build207 if decoded, else 404model year ahead of the catalogsyes, the catalogs catch up within the year
no_build_record/build207 if decoded, else 404no record builds this VINno
no_vin_record/specs207a covered vehicle no source holds a full record for; also the reason of each missing[] entryno
market_not_covered/build, /vin, /specs/build: 207 if decoded, else 404; /vin, /specs: 207VIN built outside North Americano
class_not_covered/build, /vin, /specs/build: 207 if decoded, else 404; /vin, /specs: 207motorcycle, trailer, bus or heavy truck; each page lists its endpoint's exact classesno
invalid_vin/build404the check digit does not compute, or the VIN is a test patternno

The full shapes are on the OEM Build Data, VIN Decode and Specifications pages.

HTTP Status Codes

400 - Bad Request

Invalid Location

Code: INVALID_LOCATION

{
  "status": 400,
  "error": "Invalid ZIP code format. ZIP code must be 5 digits (e.g., 90210) or 5+4 format (e.g., 90210-1234)",
  "code": "INVALID_LOCATION",
  "path": "/taxes/invalidzip",
  "requestId": "a1b2c3d4e5f6g7h8",
  "suggestion": "Please provide a valid 5-digit ZIP code or ZIP+4 format"
}

Invalid VIN Format

Code: INVALID_VIN_FORMAT

{
  "status": 400,
  "error": "VIN must be exactly 17 characters containing only letters and numbers (excluding I, O, Q)",
  "code": "INVALID_VIN_FORMAT",
  "path": "/vin/123Invalid",
  "requestId": "a1b2c3d4e5f6g7h8"
}

Invalid License Plate Format

Code: INVALID_PLATE_FORMAT

{
  "status": 400,
  "error": "License plate must be 2-8 alphanumeric characters",
  "code": "INVALID_PLATE_FORMAT",
  "path": "/plate/A?state=CA",
  "requestId": "b2c3d4e5f6g7h8i9"
}

Invalid State Code

Code: INVALID_STATE_CODE

{
  "status": 400,
  "error": "State must be a valid 2-letter US state code (e.g., CA, NY, TX)",
  "code": "INVALID_STATE_CODE",
  "path": "/plate/ABC123?state=XX",
  "requestId": "c3d4e5f6g7h8i9j0"
}

Invalid Parameter

Code: INVALID_PARAMETER

{
  "status": 400,
  "error": "Invalid parameter provided: make. This parameter does not exist in this endpoint.",
  "code": "INVALID_PARAMETER",
  "path": "/listings",
  "requestId": "966e8386bb38f095"
}

Invalid Request

Code: INVALID_REQUEST

{
  "status": 400,
  "error": "Invalid request format or missing required parameters",
  "code": "INVALID_REQUEST",
  "path": "/api/endpoint",
  "requestId": "d4e5f6g7h8i9j0k1"
}

401 - Unauthorized

Authentication Required

Code: UNAUTHORIZED

{
  "status": 401,
  "message": "Authentication required. Get your API key at https://auto.dev/dashboard/api-keys",
  "code": "UNAUTHORIZED",
  "path": "/vin/1HGCM82633A123456",
  "requestId": "e5f6g7h8i9j0k1l2"
}

402 - Payment Required

Returned when the caller's plan does not include the requested endpoint. The response body carries plan-gating context (currentPlan, requiredPlan, upgradeUrl, docsUrl, and a plans summary) so clients can render an actionable upgrade CTA without a second API call. Two response headers make the same context available without parsing the body:

  • X-Required-Plan — plan needed for this feature
  • X-Current-Plan — plan the caller is currently on

Feature Not Available

Code: FEATURE_NOT_AVAILABLE

{
  "status": 402,
  "error": "Payment Required",
  "code": "FEATURE_NOT_AVAILABLE",
  "message": "This feature requires the Scale plan. You are currently on the Free plan.",
  "currentPlan": "Free",
  "requiredPlan": "Scale",
  "upgradeUrl": "https://auto.dev/pricing?tier=Scale",
  "upgradeLink": "https://auto.dev/pricing?tier=Scale",
  "docsUrl": "https://docs.auto.dev/plans",
  "plans": {
    "Growth": { "price": "$299/mo", "rateLimit": "10 req/s", "features": "build, specs, price, recalls, tco, payments" },
    "Scale":  { "price": "$599/mo", "rateLimit": "50 req/s", "features": "All Growth + openrecalls, plate lookup, taxes" }
  },
  "path": "/openrecalls/1HGCM82633A123456",
  "requestId": "f6g7h8i9j0k1l2m3"
}

Payment Required (legacy)

Code: PAYMENT_REQUIRED

The original 402 shape is still returned by some legacy endpoints without plan context. New integrations should rely on FEATURE_NOT_AVAILABLE shape above.

{
  "status": 402,
  "error": "Payment Required",
  "code": "PAYMENT_REQUIRED",
  "path": "/advanced-endpoint",
  "requestId": "f6g7h8i9j0k1l2m3",
  "upgradeLink": "https://auto.dev/pricing"
}

403 - Forbidden

Forbidden Access

Code: FORBIDDEN

{
  "status": 403,
  "error": "Forbidden",
  "code": "FORBIDDEN",
  "path": "/restricted-endpoint",
  "requestId": "g7h8i9j0k1l2m3n4"
}

404 - Not Found

VIN Not Found

Code: VIN_NOT_FOUND

{
  "status": 404,
  "error": "No vehicle data found for the provided VIN",
  "code": "VIN_NOT_FOUND",
  "path": "/vin/1FTFW3LDXRFB40317",
  "requestId": "b2c3d4e5f6g7h8i9"
}

On /vin, a 404 means no decoder could name the vehicle; one named only in part, with a class or market reason, answers a 207 instead. On /specs, a 404 means no decoder could name the make; a vehicle with a make, and specifications only in part, answers a 207. Neither 404 carries a reason.

On /build, a 404 means no decoder could name the car; a car a decoder names answers a 207. The error block carries reason and retryable, and the body repeats them top-level.

{
  "status": 404,
  "error": "No build data available for this vehicle.",
  "code": "VIN_NOT_FOUND",
  "path": "/build/1FMDU32X6RUE08716",
  "requestId": "nu9cn44nt2ek5rbk",
  "reason": "no_build_record",
  "retryable": false
}

Invalid VIN

Code: INVALID_VIN

Returned by /build when a well-formed VIN is not a vehicle: its check digit does not compute, or it is a test pattern. Distinct from INVALID_VIN_FORMAT (400), which is about the characters.

{
  "status": 404,
  "error": "This VIN is not a vehicle: its check digit does not compute, or it is a test pattern.",
  "code": "INVALID_VIN",
  "path": "/build/11111111111111111",
  "requestId": "wk2t89y2ujo35ojs",
  "reason": "invalid_vin",
  "retryable": false
}

VIN Not in Coverage

Code: VIN_NOT_IN_COVERAGE

Returned by /build when the VIN is outside build coverage and no decoder could name the car: a non-North-American market (detectedMarket), or a motorcycle, trailer or heavy truck (detectedClass). When a decoder names the car, the same VIN answers a 207 with catalog: null. Retrying will not change the outcome.

{
  "status": 404,
  "error": "This VIN appears to be a non-US vehicle. auto.dev currently has full coverage for US-market vehicles only.",
  "code": "VIN_NOT_IN_COVERAGE",
  "path": "/build/6FPFXXMX2FRD07902",
  "requestId": "4k12o1tsrzyb4rai",
  "detectedMarket": { "country": "AU", "region": "Oceania" },
  "reason": "market_not_covered",
  "retryable": false
}

License Plate Not Found

Code: PLATE_NOT_FOUND

{
  "status": 404,
  "error": "No vehicle found for the provided license plate",
  "code": "PLATE_NOT_FOUND",
  "path": "/plate/NOTFOUND?state=CA",
  "requestId": "h8i9j0k1l2m3n4o5"
}

No Data Found

Code: NO_DATA_FOUND

{
  "status": 404,
  "error": "License plate found but no vehicle data available",
  "code": "NO_DATA_FOUND",
  "path": "/plate/ABC123?state=CA",
  "requestId": "i9j0k1l2m3n4o5p6"
}

Resource Not Found

Code: RESOURCE_NOT_FOUND

{
  "status": 404,
  "error": "Resource \"1FTFW3LDXRFB40317\" not found",
  "code": "RESOURCE_NOT_FOUND",
  "path": "/listings/1FTFW3LDXRFB40317",
  "requestId": "966716ac29ed8147"
}

422 - Unprocessable Entity

VIN Decode Failed

Code: VIN_DECODE_FAILED

{
  "status": 422,
  "error": "Unable to decode VIN: 1HGBH41JXMN109186",
  "code": "VIN_DECODE_FAILED",
  "path": "/vin/1HGBH41JXMN109186",
  "requestId": "j0k1l2m3n4o5p6q7"
}

Payment Calculation Error

Code: PAYMENT_CALCULATION_ERROR

{
  "status": 422,
  "error": "Cannot calculate payments with provided data",
  "code": "PAYMENT_CALCULATION_ERROR",
  "path": "/payments/1GYKPDRSXSZ102995",
  "requestId": "k1l2m3n4o5p6q7r8"
}

VIN Not Covered

Code: VIN_NOT_COVERED

Returned by /openrecalls when the VIN decodes successfully but falls outside the coverage of our recall data (typically grey-market imports, motorcycles, or non-standard formats). Distinct from a transient upstream failure — retrying will not change the outcome.

{
  "status": 422,
  "error": "VIN is not in our recall coverage. Only vehicles registered in the United States, Canada, or Mexico are supported at this time.",
  "code": "VIN_NOT_COVERED",
  "path": "/openrecalls/SC6GM1CAMX1234567",
  "requestId": "a7b8c9d0e1f2g3h4"
}

429 - Too Many Requests

Rate-limited responses carry the standard RFC 6585 + draft-ietf rate-limit headers so clients can back off deterministically:

  • Retry-After — seconds until the caller may retry
  • X-RateLimit-Limit — request budget for the window
  • X-RateLimit-Remaining — always 0 on a 429
  • X-RateLimit-Reset — Unix timestamp (seconds) when the window resets

Callers on Free that are approaching the monthly usage quota also see X-Usage-Remaining, X-Usage-Used, and X-Usage-Limit on all responses (not just 429s).

The body carries the same plan-gating context as 402 responses — upgradeLink + plans — so clients can present an upgrade CTA directly.

Rate Limit Exceeded

Code: RATE_LIMIT_EXCEEDED

{
  "status": 429,
  "error": "Rate limit exceeded for your Free plan. Upgrade to Growth ($299/mo, 10 req/s) at https://auto.dev/pricing for higher limits.",
  "code": "RATE_LIMIT_EXCEEDED",
  "path": "/vin/1HGCM82633A123456",
  "requestId": "l2m3n4o5p6q7r8s9",
  "upgradeLink": "https://auto.dev/pricing",
  "plans": {
    "Growth": { "price": "$299/mo", "rateLimit": "10 req/s", "features": "build, specs, price, recalls, tco, payments" },
    "Scale":  { "price": "$599/mo", "rateLimit": "50 req/s", "features": "All Growth + openrecalls, plate lookup, taxes" }
  }
}

500 - Internal Server Error

Internal Server Error

Code: INTERNAL_SERVER_ERROR

{
  "status": 500,
  "error": "500 - Internal Server Error",
  "code": "INTERNAL_SERVER_ERROR",
  "path": "/api/endpoint",
  "requestId": "m3n4o5p6q7r8s9t0",
  "message": "An unexpected error occurred, please try again later."
}

503 - Service Unavailable

Source Error

Code: SOURCE_ERROR

{
  "status": 503,
  "error": "External service temporarily unavailable",
  "code": "SOURCE_ERROR",
  "path": "/payments/1GYKPDRSXSZ102995",
  "requestId": "c3d4e5f6g7h8i9j0"
}

Legacy Error Formats

VIN Validation (Legacy Format)

Some endpoints may still return legacy error formats:

{
  "status": 400,
  "error": "VIN validation failed",
  "code": "VIN_VALIDATION_ERROR",
  "path": "/legacy/vin/invalid",
  "requestId": "n4o5p6q7r8s9t0u1",
  "errorType": "VALIDATION_ERROR"
}

Error Handling Best Practices

Client Implementation

  1. Always check the status code first
  2. Treat a 207 as a partial answer, not a success: on /build build is null, on /vin the decode fields are absent, on /specs the fields in missing are null; read reason
  3. Use the code field for programmatic error handling
  4. Display the error message to users (human-readable)
  5. Log the requestId for debugging and support
  6. Implement retry logic for 5xx errors with exponential backoff, and for a miss only when retryable is true

Getting Help

When contacting support, please include:

  1. Request ID from the error response
  2. Full error response JSON
  3. Request details (endpoint, parameters, headers)
  4. Expected behavior vs. actual behavior