# Check a share code

> `POST https://checksharecode.co.uk/api/check` runs the employer check on gov.uk's View a job applicant's right to work details service and returns the result as JSON. A live check takes a few seconds. Nothing is stored: the photo and PDF are only in this response.

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

## Headers

- `Authorization` (string, required): Bearer followed by your key. `rtw_test_…` keys return sandbox data. `rtw_live_…` keys run a real check. Example: `Bearer rtw_live_…`

## Body

- `share_code` (string, required): The 9-character code the applicant got from gov.uk, letters and numbers. Spaces and lower case are fine: we remove spaces and upper-case it. Example: `AB1CD2EF3`
- `date_of_birth` (string, required): The applicant's date of birth as YYYY-MM-DD. They must be at least 16. Example: `1990-01-01`
- `company_name` (string, required): Your organisation's name, 1 to 200 characters. gov.uk records it against the check. Example: `Acme Ltd`

cURL:

```bash
curl https://checksharecode.co.uk/api/check \
  -H "authorization: Bearer $CHECKSHARECODE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "share_code": "AB1CD2EF3",
    "date_of_birth": "1990-01-01",
    "company_name": "Acme Ltd"
  }'
```

Node.js:

```js
const res = await fetch('https://checksharecode.co.uk/api/check', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.CHECKSHARECODE_API_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    share_code: 'AB1CD2EF3',
    date_of_birth: '1990-01-01', // YYYY-MM-DD
    company_name: 'Acme Ltd',
  }),
});

const result = await res.json();
if (!res.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result.outcome); // "ACCEPTED" or "REJECTED"
```

Python:

```python
import os
import requests

res = requests.post(
    "https://checksharecode.co.uk/api/check",
    headers={"authorization": f"Bearer {os.environ['CHECKSHARECODE_API_KEY']}"},
    json={
        "share_code": "AB1CD2EF3",
        "date_of_birth": "1990-01-01",  # YYYY-MM-DD
        "company_name": "Acme Ltd",
    },
    timeout=60,
)
result = res.json()
if not res.ok:
    raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result["outcome"])  # "ACCEPTED" or "REJECTED"
```

200 OK:

```json
{
  "outcome": "ACCEPTED",
  "title": "Right to work",
  "name": "JANE EXAMPLE DOE",
  "date_of_birth": "1990-01-01",
  "nationality": "British",
  "permission_type": "Indefinite leave to remain",
  "start_date": null,
  "expiry_date": null,
  "conditions": [],
  "restrictions": [],
  "reference": "WE-EXAMPLE-12",
  "share_code": "AB1CD2EF3",
  "photo_data_url": "data:image/jpeg;base64,/9j/4AAQ…",
  "pdf_data_url": "data:application/pdf;base64,JVBERi0…",
  "checked_at": "2026-09-27T10:14:03.000Z"
}
```

## Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| outcome | "ACCEPTED" \| "REJECTED" | Whether gov.uk says this person has the right to work. |
| title | "Right to work" \| "Right to rent" | Which kind of share code it was. |
| name | string | Applicant name as gov.uk holds it. |
| permission_type | string \| null | For example Skilled Worker visa, or Indefinite leave to remain. |
| start_date, expiry_date | date \| null | YYYY-MM-DD. `null` when gov.uk shows none. Diary a follow-up check before the expiry date. |
| conditions, restrictions | string[] | Work conditions and restrictions, one sentence each. Empty array when there are none. |
| photo_data_url | string \| null | Applicant photo as a JPEG data URL. Compare it with the person. |
| pdf_data_url | string \| null | The gov.uk PDF as a data URL. Keep it as your record. |
| reference | string \| null | gov.uk reference for your audit trail. |
| checked_at | date-time | When the check finished, ISO 8601 UTC. |

Live responses also carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` for your monthly checks, and every response has an `X-Request-Id`.

## Errors

Errors come back as `{ "error": { "code", "message" } }`. Switch on `code`. `message` is written for a person and says what to do next. `QUOTA_EXCEEDED` and `PAYMENT_REQUIRED` also carry an `upgrade_url`.

| code | HTTP | What to do |
| --- | --- | --- |
| INVALID_INPUT | 400 | Fix the field named in `message`. Don't retry as is. |
| UNAUTHENTICATED / INVALID_KEY | 401 | Check the Authorization header and key. |
| QUOTA_EXCEEDED / PAYMENT_REQUIRED | 402 | Plan limit reached. Send the user to `upgrade_url`. |
| NOT_FOUND | 404 | gov.uk doesn't recognise the share code. Ask the applicant to check it. |
| DOB_MISMATCH | 404 | Code found, date of birth doesn't match. Ask the applicant to check it. |
| RATE_LIMITED | 429 | Too many requests. Back off and retry. |
| GOVUK_UNEXPECTED | 502 | gov.uk returned a page we didn't expect. Retry later. |
| BUSY | 503 | All check slots in use. Retry after the `Retry-After` seconds. |
| TIMEOUT | 504 | gov.uk was too slow. Safe to retry. |
| INTERNAL | 500 | Our fault. Retry, then contact us with `X-Request-Id`. |

## Rate limits and quota

| Plan | Live checks a month | After that | Requests/second per key |
| --- | --- | --- | --- |
| Free | 10 | Refused until the 1st | 5 |
| Pro | 100 | £0.25 per check | 20 |
| Scale | 500 | £0.10 per check | 50 |

Sandbox calls and errors never count. The OpenAPI 3.1 contract is at [/openapi.json](https://checksharecode.co.uk/openapi.json).

See also: [Quickstart](https://checksharecode.co.uk/docs/quickstart.md) · [Build with an agent](https://checksharecode.co.uk/docs/agents.md)
