# Driving licence

> `POST https://checksharecode.co.uk/api/check/driving-licence` checks a UK driving licence on DVLA's Check a driving licence service, with the check code the driver makes at gov.uk/view-driving-licence. Everything you send to this endpoint is here, and only that. A live check usually takes 5 to 15 seconds. A code works once, so the async endpoint is the safer choice: its result is kept for 7 days.

Base URL: `https://checksharecode.co.uk` · Auth: `authorization: Bearer <key>` · Web version: https://checksharecode.co.uk/docs/api/driving-licence

Synchronous: `POST https://checksharecode.co.uk/api/check/driving-licence`. Asynchronous: `POST https://checksharecode.co.uk/api/check/driving-licence/async`.

## What you send

### Synchronous

- `Authorization` (header, string, required): Bearer followed by your key. `rtw_test_…` keys return sandbox data. `rtw_live_…` keys run a real check. Example: `Bearer rtw_live_…`
- `licence_number` (string, required): The driving licence number: all 16 characters, e.g. MORGA753116SM9IJ, or just the last 8, e.g. 116SM9IJ. Leave off the 2-digit issue number printed after it on the photocard. DVLA is only sent the last 8. Example: `MORGA753116SM9IJ`
- `check_code` (string, required): The 8-character code the driver creates at gov.uk/view-driving-licence, e.g. Hb Rh fp 7g. Case matters; spaces are fine. It lasts 21 days and works once. Example: `Hb Rh fp 7g`

DVLA shows the driver’s record rather than a yes or no, so `outcome` is always `ACCEPTED` and the answer is in `driving_status`, `points`, `automatic_only`, `entitlements` and `flags`; see [the fields](https://checksharecode.co.uk/docs/api/check.md#driving-licence-fields). Only the last 8 characters of the licence number go to DVLA. Don’t send `share_code`, `date_of_birth`, `company_name` or the other share code fields: they are refused here with `400 INVALID_INPUT`. You don’t need `check` either. If you send it, it must be `driving_licence`.

Save the PDF if you need a record of the check. DVLA does not accept a printout as proof of what someone can drive, so it is your audit record, not their licence.

cURL:

```bash
curl https://checksharecode.co.uk/api/check/driving-licence \
  -H "authorization: Bearer $CHECKSHARECODE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "licence_number": "MORGA753116SM9IJ",
    "check_code": "Hb Rh fp 7g"
  }'
```

Node.js:

```js
const res = await fetch('https://checksharecode.co.uk/api/check/driving-licence', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.CHECKSHARECODE_API_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    licence_number: 'MORGA753116SM9IJ', // all 16 characters, or the last 8
    check_code: 'Hb Rh fp 7g', // from gov.uk/view-driving-licence, works once
  }),
});

const result = await res.json();
if (!res.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result.driving_status, result.points, result.automatic_only);
```

Python:

```python
import os
import requests

res = requests.post(
    "https://checksharecode.co.uk/api/check/driving-licence",
    headers={"authorization": f"Bearer {os.environ['CHECKSHARECODE_API_KEY']}"},
    json={
        "licence_number": "MORGA753116SM9IJ",  # all 16 characters, or the last 8
        "check_code": "Hb Rh fp 7g",  # from gov.uk/view-driving-licence, works once
    },
    timeout=60,
)
result = res.json()
if not res.ok:
    raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result["driving_status"], result["points"], result["automatic_only"])
```

200 OK:

```json
{
  "outcome": "ACCEPTED",
  "title": "Driving licence",
  "name": "MISS JANE EXAMPLE DOE",
  "driving_status": "Current full licence",
  "licence_number_last8": "192GT2BD",
  "issue_number": "12",
  "licence_valid_from": "2018-08-05",
  "licence_valid_to": "2028-08-04",
  "offences": 1,
  "points": 3,
  "has_penalties": true,
  "automatic_only": true,
  "entitlements": [
    {
      "category": "B",
      "kind": "full",
      "valid_from": "2009-07-20",
      "valid_to": "2060-01-01",
      "automatic_only": true,
      "restrictions": [
        {
          "code": "78",
          "text": "Restricted to vehicles with automatic transmission"
        }
      ],
      "description": "Vehicles up to 3,500 kg Maximum Authorised Mass (MAM) with up to 8 passenger seats and driver (with a trailer up to 750 kg)."
    },
    {
      "category": "AM",
      "kind": "full",
      "valid_from": "2009-07-20",
      "valid_to": "2060-01-01",
      "automatic_only": false,
      "restrictions": [
        {
          "code": "122",
          "text": "Valid on successful completion of Basic Moped Training Course (CBT)"
        }
      ],
      "description": "2-wheel vehicles or 3-wheel vehicles with a maximum design speed of over 25km/h and not more than 45km/h."
    },
    {
      "category": "A",
      "kind": "provisional",
      "valid_from": "2007-01-01",
      "valid_to": "2060-01-01",
      "automatic_only": false,
      "restrictions": [],
      "description": "A motorcycle of a power exceeding 35 kW or with a power to weight ratio exceeding 0.2 kW per kg."
    }
  ],
  "flags": [],
  "viewed_at": "2026-10-01T09:45:00+01:00",
  "pdf_data_url": "data:application/pdf;base64,JVBERi0…",
  "checked_at": "2026-10-01T08:45:03.000Z"
}
```

### Asynchronous

- `Authorization` (header, string, required): Bearer followed by your key. `rtw_test_…` keys return sandbox data. `rtw_live_…` keys run a real check. Example: `Bearer rtw_live_…`
- `Idempotency-Key` (header, string): Any string up to 255 characters, unique per check you mean to run. Sending the same key again returns the first check with `200`, so a retry after a network error never runs or charges a second check. Example: `applicant-4411-check-1`
- `licence_number` (string, required): The driving licence number: all 16 characters, e.g. MORGA753116SM9IJ, or just the last 8, e.g. 116SM9IJ. Leave off the 2-digit issue number printed after it on the photocard. DVLA is only sent the last 8. Example: `MORGA753116SM9IJ`
- `check_code` (string, required): The 8-character code the driver creates at gov.uk/view-driving-licence, e.g. Hb Rh fp 7g. Case matters; spaces are fine. It lasts 21 days and works once. Example: `Hb Rh fp 7g`
- `callback_url` (string): Where we post the result when the check ends. Without it we use your default URL for the mode, set on [Webhooks](https://checksharecode.co.uk/app/webhooks), and without that you poll. See [Webhooks](https://checksharecode.co.uk/docs/webhooks.md). Example: `https://api.acme.example/hooks/rtw?applicant=4411`
- `client_reference` (string): Your own id for the check, up to 200 characters, given back in every response and webhook. Use an opaque id, never the applicant’s name, share code or date of birth. Example: `applicant-4411`

The `202` has a `Location` header with the check’s URL, `/api/check/driving-licence/async/chk_…`. `GET` it with the same key, or set a callback URL and we post the result when the check ends. See [Asynchronous checks](https://checksharecode.co.uk/docs/api/check-async.md) and [Webhooks](https://checksharecode.co.uk/docs/webhooks.md).

Request:

```bash
curl https://checksharecode.co.uk/api/check/driving-licence/async \
  -H "Authorization: Bearer $CHECKSHARECODE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: applicant-4411-check-1" \
  -d '{
    "licence_number": "MORGA753116SM9IJ",
    "check_code": "Hb Rh fp 7g",
    "callback_url": "https://api.acme.example/hooks/rtw?applicant=4411",
    "client_reference": "applicant-4411"
  }'
```

202 Accepted:

```json
{
  "id": "chk_5f2a8c1e9b7d4a6f8e3c2b1a0d9f8e7c",
  "status": "queued",
  "mode": "live",
  "client_reference": "applicant-4411",
  "callback_url": "https://api.acme.example/hooks/rtw?applicant=4411",
  "attempts": 0,
  "result": null,
  "error": null,
  "result_expires_at": null,
  "result_expired": false,
  "created_at": "2026-09-28T10:00:00.000Z",
  "completed_at": null
}
```

## A check code works once

The driver makes the code at gov.uk/view-driving-licence. It is 8 letters and digits, shown in pairs like Hb Rh fp 7g, and case matters. It lasts 21 days, and DVLA lets it be used once.

- **Sent twice?** If your account sends the same licence number and code again within 21 days, we refuse it with `409 CHECK_CODE_ALREADY_USED` before it reaches DVLA, and it is not charged. `first_check` names the earlier check.
- **Every error from the check says whether the code was used.** `check_code_used` is `false` when DVLA never saw the code: send the same code again. When it is `true` or `"unknown"`, ask the driver for a new code. `401`, `402` and `429` come before the check, so the code is never sent.
- **Refused?** `422 CHECK_CODE_UNUSABLE` means DVLA did not accept the licence number and code, and does not say why. It uses 2 checks, like a record DVLA shows.
- **Keep the result.** DVLA shows the record once for each code. An [asynchronous check](https://checksharecode.co.uk/docs/api/check-async.md) keeps it for you for 7 days, so a dropped connection never loses it.

## Try it

Signed in, the web version of this page sends this request with your sandbox key and shows the answer.

## The same for every check

These are written once, for this endpoint, [right to work](https://checksharecode.co.uk/docs/api/right-to-work.md), [right to rent](https://checksharecode.co.uk/docs/api/right-to-rent.md) and [immigration status](https://checksharecode.co.uk/docs/api/immigration-status.md) alike.

- [Response fields](https://checksharecode.co.uk/docs/api/check.md#response-fields): What each check returns
- [Errors](https://checksharecode.co.uk/docs/api/check.md#errors): Every code, and what to do
- [Rate limits and quota](https://checksharecode.co.uk/docs/api/check.md#limits): One allowance for every check
- [Webhooks](https://checksharecode.co.uk/docs/webhooks.md): For asynchronous checks

See also: [POST /api/check/immigration-status](https://checksharecode.co.uk/docs/api/immigration-status.md) · [Responses, errors and limits](https://checksharecode.co.uk/docs/api/check.md)
