PHPackages                             estin92/laravel-dvla-ves - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. estin92/laravel-dvla-ves

ActiveLibrary[API Development](/categories/api)

estin92/laravel-dvla-ves
========================

Look up UK vehicle tax, MOT, fuel type and emissions by registration number in Laravel via the official DVLA Vehicle Enquiry Service (VES) API. Returns a typed, immutable VehicleData object.

1.0.4(today)00MITPHPPHP ^8.2

Since Jun 13Pushed todayCompare

[ Source](https://github.com/estin92/laravel-dvla-ves)[ Packagist](https://packagist.org/packages/estin92/laravel-dvla-ves)[ RSS](/packages/estin92-laravel-dvla-ves/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (8)Versions (6)Used By (0)

Laravel DVLA VES
================

[](#laravel-dvla-ves)

[![Tests](https://github.com/estin92/laravel-dvla-ves/actions/workflows/tests.yml/badge.svg)](https://github.com/estin92/laravel-dvla-ves/actions/workflows/tests.yml)[![codecov](https://camo.githubusercontent.com/9b0f39c40500ee871637d16025326cba694917bc8a40bc95ff14270548ed843f/68747470733a2f2f636f6465636f762e696f2f67682f657374696e39322f6c61726176656c2d64766c612d7665732f67726170682f62616467652e737667)](https://codecov.io/gh/estin92/laravel-dvla-ves)[![Latest Version on Packagist](https://camo.githubusercontent.com/fe360652a56439586382211270c6e6988df9412afc14fc22b633e6a2ce4b19a9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f657374696e39322f6c61726176656c2d64766c612d7665732e737667)](https://packagist.org/packages/estin92/laravel-dvla-ves)[![Total Downloads](https://camo.githubusercontent.com/0ea126e8b9c98d2598d9c452c1972e004d7a49075a4178a1e6f2bf44a7741903/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f657374696e39322f6c61726176656c2d64766c612d7665732e737667)](https://packagist.org/packages/estin92/laravel-dvla-ves)[![PHP Version](https://camo.githubusercontent.com/0834841fc6debafa854a8a38f68de297d49e20050fd67895dae5312cb48f0e74/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f657374696e39322f6c61726176656c2d64766c612d7665732e737667)](https://packagist.org/packages/estin92/laravel-dvla-ves)[![License](https://camo.githubusercontent.com/d81e4ae82acebc53e0fd21ceda27dd1aaf25694fb96b8289f9282a5c062b58ad/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f657374696e39322f6c61726176656c2d64766c612d7665732e737667)](LICENSE)

**Laravel DVLA VES** is a PHP package that looks up UK vehicle details by registration number through the official DVLA Vehicle Enquiry Service (VES) API.

Pass a number plate and it returns a typed, immutable `VehicleData` object exposing tax status, MOT status and expiry, fuel type, CO2 emissions, make, colour, year of manufacture and more.

It is built for Laravel 12 and 13 on PHP 8.2+, with first-class enums, response caching, a fake driver for testing and an Artisan lookup command. This package is an independent open-source connector and is not affiliated with or endorsed by the DVLA.

Install
-------

[](#install)

```
composer require estin92/laravel-dvla-ves
```

Configure
---------

[](#configure)

```
php artisan vendor:publish --tag=dvla-ves-config
```

Set your API key in `.env`:

```
DVLA_VES_API_KEY=your-key
```

> You supply your own DVLA VES API key, obtained from the [DVLA Developer Portal](https://developer-portal.driver-vehicle-licensing.api.gov.uk/). Your use of the API is governed by DVLA's own terms of use - this package is an independent connector and is not affiliated with or endorsed by the DVLA.

Usage
-----

[](#usage)

```
use Estin92\DvlaVes\Facades\DvlaVes;

$vehicle = DvlaVes::lookup('AB12CDE');
```

`lookup()` returns a readonly `VehicleData`. Every wire field is exposed as a typed property - all nullable except `registrationNumber` (see [Why is almost every field nullable?](#why-is-almost-every-field-nullable) below):

```
// Identity & build
$vehicle->registrationNumber;            // string            "AB12CDE"
$vehicle->make;                          // ?string           "FORD"
$vehicle->colour;                        // ?string           "BLUE"
$vehicle->yearOfManufacture;             // ?int              2012
$vehicle->wheelplan;                     // ?string           "2 AXLE RIGID BODY"
$vehicle->typeApproval;                  // ?string           "M1"

// Engine & emissions
$vehicle->fuelType;                      // ?FuelType (enum)
$vehicle->engineCapacity;                // ?int              1560   (cc)
$vehicle->co2Emissions;                  // ?int              104    (g/km)
$vehicle->revenueWeight;                 // ?int              null   (kg, goods vehicles)
$vehicle->euroStatus;                    // ?string           "EURO 6"
$vehicle->realDrivingEmissions;          // ?string           "1"

// Tax
$vehicle->taxStatus;                     // ?TaxStatus (enum)
$vehicle->taxDueDate;                    // ?CarbonImmutable
$vehicle->artEndDate;                    // ?CarbonImmutable   (luxury-car VED supplement end)

// MOT
$vehicle->motStatus;                     // ?MotStatus (enum)
$vehicle->motExpiryDate;                 // ?CarbonImmutable

// Registration history
$vehicle->monthOfFirstRegistration;      // ?string "2012-03"  (or ?CarbonImmutable, see below)
$vehicle->monthOfFirstDvlaRegistration;  // ?string "2012-03"  (or ?CarbonImmutable, see below)
$vehicle->dateOfLastV5CIssued;           // ?CarbonImmutable

// Flags
$vehicle->markedForExport;               // ?bool
$vehicle->automatedVehicle;              // ?bool              (see note below)

// The untouched decoded API payload, if you need a field not surfaced above
$vehicle->rawResponse;                   // array
```

Domain helpers wrap the nullable enums/flags and always return a plain `bool`:

```
$vehicle->isPetrol();                    // fuelType === Petrol
$vehicle->isDiesel();                    // fuelType === Diesel
$vehicle->isElectric();                  // fuelType === Electricity
$vehicle->isHybrid();                    // fuelType === HybridElectric

$vehicle->isTaxed();                     // taxStatus === Taxed
$vehicle->isSorn();                      // taxStatus === Sorn
$vehicle->isTaxDue();                    // taxStatus === Untaxed
$vehicle->hasValidMot();                 // motStatus === Valid

$vehicle->isMarkedForExport();           // markedForExport ?? false
$vehicle->isAutomatedVehicle();          // automatedVehicle ?? false
$vehicle->isSubjectToAdditionalRateOfTax(); // artEndDate is in the future
```

Accessors return a typed value rather than a `bool`:

```
$vehicle->getFirstRegistrationDate();    // ?CarbonImmutable (start of month), parsed on demand
$vehicle->additionalRateOfTaxEndDate();  // ?CarbonImmutable  alias for artEndDate
```

The enums carry their own helpers and a translatable label:

```
$vehicle->fuelType?->label();            // "Petrol"        (translatable via dvla-ves::enums)
$vehicle->taxStatus?->label();           // "Taxed"
$vehicle->motStatus?->isValid();         // bool

if ($vehicle->fuelType?->isElectric()) {
    // ...
}
```

Failures throw - catch the base exception or a specific subclass:

```
use Estin92\DvlaVes\Exceptions\DvlaVesException;
use Estin92\DvlaVes\Exceptions\VehicleNotFoundException;
use Estin92\DvlaVes\Exceptions\RateLimitExceededException;

try {
    $vehicle = DvlaVes::lookup('AB12CDE');
} catch (VehicleNotFoundException $e) {
    // 404 - no vehicle for that registration
} catch (RateLimitExceededException $e) {
    $e->retryAfter;                      // ?int seconds, from the Retry-After header
} catch (DvlaVesException $e) {
    // any other DVLA VES failure (invalid registration, service unavailable, ...)
    report($e);
}
```

> The `monthOfFirstRegistration` / `monthOfFirstDvlaRegistration` fields are `"YYYY-MM"`strings by default. Either call `getFirstRegistrationDate()` for a `CarbonImmutable` or set `DVLA_VES_CAST_YEAR_MONTH_ONLY_FIELDS_TO_CARBON=true` to receive these properties as `CarbonImmutable` (start of month) directly.

Why is almost every field nullable?
-----------------------------------

[](#why-is-almost-every-field-nullable)

DVLA has been tested and found to return a different subset of fields per vehicle, omitting the ones that don't apply rather than returning them as `null`. Every property except `registrationNumber` is typed `?` and defaults to `null` when its key is absent. Across **21 real vehicle lookups**, the number of responses that contained each field was:

FieldPresent`registrationNumber`21/21`make`21/21`colour`21/21`fuelType`21/21`taxStatus`21/21`motStatus`21/21`yearOfManufacture`21/21`monthOfFirstRegistration`21/21`wheelplan`21/21`markedForExport`21/21`dateOfLastV5CIssued`21/21`engineCapacity`20/21`co2Emissions`19/21`taxDueDate`18/21`typeApproval`17/21`motExpiryDate`15/21`revenueWeight`10/21`euroStatus`4/21`artEndDate`2/21`automatedVehicle`2/21`realDrivingEmissions`1/21`monthOfFirstDvlaRegistration`1/21We have opted to keep all fields, including those that appeared in all 21, nullable too - the sample can't guarantee every vehicle returns them. No captured response contained an explicit `null`; DVLA seems instead to only ever omit attributes. Typing every field as nullable means an omitted attribute defaults to `null` rather than triggering an unnecessary fatal error during hydration. To avoid null checks, use the boolean helpers (e.g. `isElectric()`, `hasValidMot()`), which treat a missing value as `false`.

API reference
-------------

[](#api-reference)

- `DvlaVes::lookup(string $registration): VehicleData`
- `DvlaVes::isConfigured(): bool`.
- `VehicleData` - readonly DTO; full property and helper list shown under [Usage](#usage).
- Enums: `FuelType`, `TaxStatus`, `MotStatus` (each with `fromApi()` + translated `label()`).
- Exceptions: `VehicleNotFoundException`, `InvalidRegistrationException`, `RateLimitExceededException`, `ServiceUnavailableException` - all extend `DvlaVesException`.

> `VehicleData::$automatedVehicle` (`?bool`) is documented in the DVLA VES v1.2.0 OpenAPI reference (both the JSON spec and its rendered HTML), though DVLA's separate prose service-description page omits it. It is sparsely included and in practice, our testing has shown the DVLA only returns it for certain vehicles - so the raw property is nullable. Use `isAutomatedVehicle()` for a plain boolean; it returns `false` for both `false` and `null`, since a vehicle DVLA never flags is not an automated vehicle.

Caching (off by default)
------------------------

[](#caching-off-by-default)

```
DVLA_VES_CACHE_ENABLED=true
DVLA_VES_CACHE_TTL=86400
```

Caches successful lookups keyed by normalised registration.

Testing
-------

[](#testing)

```
DvlaVes::fake([
    'AB12CDE' => ['registrationNumber' => 'AB12CDE', 'make' => 'FORD'],
]);

DvlaVes::lookup('AB12CDE')->make; // "FORD"
```

Artisan
-------

[](#artisan)

```
php artisan dvla-ves:lookup AB12CDE
```

AI agent support (Laravel Boost)
--------------------------------

[](#ai-agent-support-laravel-boost)

This package ships [Laravel Boost](https://laravel.com/docs/13.x/boost) resources - an AI guideline and a `dvla-ves` skill - so coding agents (Claude Code, Cursor, etc.) get accurate context about `VehicleData`, the enums, exceptions, caching and the fake driver.

Boost surfaces them once it is made aware of this package:

- If you install or reconfigure Boost with `php artisan boost:install`, this package's guideline and skill are offered for publishing.
- In a project where Boost is already installed, scan for this newly added package and be prompted to publish its resources with:

```
php artisan boost:update --discover
```

Plain `php artisan boost:update` only refreshes resources you have already published - it will not discover this package on its own, so use `--discover` (or re-run `boost:install`) the first time. Once published, the resources are recorded in `boost.json` and stay current on every `boost:update`. To automate that, add Boost's update command to your app's `composer.json` `post-update-cmd` scripts.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

5

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9d53373a7c505b38fc229302763816d489ac26a2375da0abf23658c05c326351?d=identicon)[estin92](/maintainers/estin92)

---

Tags

phplaravelregistrationvehicleukdvlavesMOTvehicle-enquiryvehicle-taxdvla-api

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/estin92-laravel-dvla-ves/health.svg)

```
[![Health](https://phpackages.com/badges/estin92-laravel-dvla-ves/health.svg)](https://phpackages.com/packages/estin92-laravel-dvla-ves)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.5k](/packages/larastan-larastan)[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.7M64](/packages/spatie-laravel-responsecache)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k28.4M135](/packages/laravel-cashier)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M113](/packages/laravel-mcp)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k14.1M120](/packages/laravel-pulse)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
