sashalenz/nhtsa-api

Laravel integration package for the NHTSA vPIC API.

Maintainers

Package info

github.com/sashalenz/nhtsa-laravel-api

pkg:composer/sashalenz/nhtsa-api

Statistics

Installs: 74

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.2.1 2026-06-09 20:03 UTC

This package is auto-updated.

Last update: 2026-06-10 07:02:16 UTC


README

A small, typed Laravel integration for the NHTSA vPIC API. It decodes VINs and WMIs into predictable DTOs, with built-in retries, optional proxying, and opt-in request-history logging.

Requirements

  • PHP 8.4 or 8.5
  • Laravel 12 or 13

Installation

composer require sashalenz/nhtsa-api

The service provider and the NhtsaApi facade are auto-discovered. Publish the config only if you need to change the defaults:

php artisan vendor:publish --tag="nhtsa-api-config"

Configuration

config/nhtsa-api.php:

Key Env Default Description
base_url NHTSA_API_BASE_URL https://vpic.nhtsa.dot.gov/api/ vPIC base URL.
timeout 10.0 Request and connection timeout, in seconds.
format NHTSA_API_FORMAT json Response format query parameter.
retry.times 3 Attempts before failing. Set 0 to disable retries.
retry.sleep 150 Delay between attempts, in milliseconds.
proxy NHTSA_API_PROXY null HTTP proxy: a URL string, a Closure resolved per request, or null.

Usage

Every call goes through the NhtsaApi facade and accepts a request DTO.

Decode a VIN — flat key/value (recommended)

decodeVinValues returns one result keyed by NHTSA variable name. It is the most convenient shape for reading individual fields:

use Sashalenz\NhtsaApi\Data\Requests\DecodeVinRequestData;
use Sashalenz\NhtsaApi\Facades\NhtsaApi;

$result = NhtsaApi::decodeVinValues(
    DecodeVinRequestData::from(['vin' => '58ADA1C18MU003613'])
)->firstResult();

$make = $result?->get('Make');         // "LEXUS"
$model = $result?->get('Model');       // "ES"
$body = $result?->get('BodyClass');    // "Sedan/Saloon"
$year = $result?->get('ModelYear');    // "2021"
$fuel = $result?->get('FuelTypePrimary');

get() accepts a default: $result->get('Trim', 'n/a'). Use ->results() to iterate every result row.

Decode a VIN — variable list

decodeVin returns the classic NHTSA variable list as VinDecodedVariableData items (variable, value, valueId, variableId):

$response = NhtsaApi::decodeVin(DecodeVinRequestData::from([
    'vin' => '5UXWX7C5*BA',
    'modelYear' => 2011, // optional, narrows ambiguous VIN patterns
]));

$make = $response->variables()->firstWhere('variable', 'Make')?->value;

Decode a WMI

use Sashalenz\NhtsaApi\Data\Requests\DecodeWmiRequestData;

$wmi = NhtsaApi::decodeWmi(DecodeWmiRequestData::from(['wmi' => '5UX']));

Façade methods

Method Returns Notes
decodeVin DecodeVinResponseData Classic variable list.
decodeVinExtended DecodeVinExtendedResponseData Adds extended / NCSA variables.
decodeVinValues VinDecodeFlatResponseData Flat key/value result.
decodeVinValuesExtended VinDecodeFlatResponseData Flat, extended.
decodeWmi DecodeWmiResponseData WMI metadata.

Service layer (request history)

NhtsaApiService wraps the client and logs every decode to the nhtsa_api_requests table — VIN, vehicle descriptor, make, model, model year, the raw decoded values, and a polymorphic link to the initiator.

use Sashalenz\NhtsaApi\Data\Requests\DecodeVinRequestData;
use Sashalenz\NhtsaApi\Services\NhtsaApiService;

$service = app(NhtsaApiService::class);

// Resolve just the make (and log the request, optionally attributed to a model).
$make = $service->determineMake(
    DecodeVinRequestData::from(['vin' => 'WP0AA2A7GL', 'modelYear' => 2016]),
    initiator: $user, // optional
);

// Full flat decode, logged.
$response = $service->decodeVinWithHistory(
    DecodeVinRequestData::from(['vin' => 'WP0AA2A7GL'])
);

// How many times this VIN has been looked up.
$count = $service->getVinRequestCount('WP0AA2A7GL');

Run the package migration to create the history table:

php artisan migrate

Error handling

A non-2xx response (after the configured retries are exhausted) raises NhtsaApiRequestException:

use Sashalenz\NhtsaApi\Exceptions\NhtsaApiRequestException;

try {
    NhtsaApi::decodeVin(DecodeVinRequestData::from(['vin' => $vin]));
} catch (NhtsaApiRequestException $e) {
    // vPIC was unavailable — fall back to manual entry, queue a retry, etc.
}

Testing

The client is built on Laravel's HTTP client, so Http::fake() intercepts every request in your tests:

use Illuminate\Support\Facades\Http;

Http::fake([
    '*/DecodeVinValues/*' => Http::response([
        'Count' => 1,
        'Message' => 'Results returned successfully',
        'Results' => [['Make' => 'LEXUS', 'Model' => 'ES']],
    ]),
]);

Run the package's own suite:

vendor/bin/pest

License

The MIT License (MIT). See LICENSE for details.