# Check a business identifier against the registers

`POST /api/v1/entity`

- Authentication: Secret API key as a bearer token
- Billing: 1 credit when a register answers. A malformed identifier, one no register carries, an unsupported register and a register that is not installed are all free, and a 7-day repeat of the same identifier on the same account is free.
- Group: Entity

Reads a business identifier against the registers this server holds: a Legal Entity Identifier against the GLEIF Level 1 golden copy, or a UK company number against the Companies House free company data product. Both are bulk files synced locally, so no third party sits in the request path and nothing about the lookup leaves the server.

Send `identifier` with a `type` of `lei` or `company_number`. A company number needs the `country` whose register it belongs to, because company numbers are only unique inside their own register; `GB` is the register installed today. A LEI needs no country: the identifier is global by construction. A UK company number is zero-padded to eight characters before it is looked up, so `1234567` and `01234567` are the same company.

The answer gives the entity's legal name, whether it is still live (`status`, with the register's own wording in `status_detail`), the country and jurisdiction it was formed in, its category and legal form, and when it was registered. For a LEI it adds `checksum_valid` — the ISO 7064 MOD 97-10 check digit defined by ISO 17442, which catches a mistyped identifier and says nothing about whether it was ever issued — plus `registration_status` and `next_renewal_at`, because a LEI registration can lapse while the entity behind it is perfectly alive.

Where a LEI names its entry in a national register this server also holds, the two records are compared field by field. `agreements` quotes both sides of each comparison and `flags` names what differs: `name_differs_across_registers`, `status_differs_across_registers`, `dissolved_in_national_register`, `lei_registration_not_current`. Names are compared on their letters and digits with the company-form words removed, so only a real difference is reported. Where there is no counterpart, `agreements` is empty — an absent register is not a disagreement.

`sources` names every register behind the answer with the date of the copy. That matters here: the Companies House snapshot is republished monthly and can be up to five weeks behind the live register, while the GLEIF golden copy is published daily.

The product answers about organisations, not people. No registered address is stored or returned. A registered address is very often somebody's home, and an identifier alone does not say whether a company or a sole trader stands behind it, so the product holds none of them rather than some of them badly.

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `identifier` | string | yes | The Legal Entity Identifier or company number to check. At most 64 characters. |
| `type` | string | yes | Which register the identifier belongs to. One of: lei, company_number. |
| `country` | string | null | no | The ISO 3166-1 alpha-2 country of the company register. Required when `type` is `company_number`; ignored for a LEI. At most 2 characters. |

## Example request

```bash
curl -X POST https://spaw.co/api/v1/entity \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "identifier": "213800QILIUD4ROSUO03",
  "type": "lei"
}'
```

## Responses

### 200 — The entity as the registers hold it; `found: false` with a `reason` for an identifier none of them could answer.

```json
{
    "success": true,
    "data": {
        "identifier": "213800QILIUD4ROSUO03",
        "identifier_type": "lei",
        "found": true,
        "reason": null,
        "name": "ACME HOLDINGS LIMITED",
        "status": "active",
        "status_detail": "ACTIVE",
        "country": "GB",
        "jurisdiction": "GB",
        "category": "GENERAL",
        "legal_form": "H0PO",
        "registered_at": "2016-04-21",
        "record_updated_at": "2026-08-14",
        "checksum_valid": true,
        "registration_status": "ISSUED",
        "next_renewal_at": "2027-04-21",
        "other_register": "uk-companies-house",
        "other_identifier": "01234567",
        "flags": [],
        "agreements": [
            {
                "field": "name",
                "lei": "ACME HOLDINGS LIMITED",
                "national_register": "ACME HOLDINGS LIMITED",
                "result": "agree"
            },
            {
                "field": "status",
                "lei": "active",
                "national_register": "active",
                "result": "agree"
            }
        ],
        "sources": [
            {
                "dataset": "gleif-lei-level-1",
                "version": "2026-09-08"
            },
            {
                "dataset": "uk-companies-house-entities",
                "version": "2026-09-01"
            }
        ]
    },
    "meta": {
        "credits_used": 1,
        "credits_remaining": 999,
        "cache_hit": false,
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 401 — The key is missing, malformed, or revoked.

```json
{
    "success": false,
    "error": {
        "code": "UNAUTHENTICATED",
        "message": "Provide a valid API key as a bearer token.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 402 — The balance is empty. The lookup did not run.

```json
{
    "success": false,
    "error": {
        "code": "INSUFFICIENT_CREDITS",
        "message": "Your credit balance is empty.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 422 — The request body could not be validated; `error.errors` lists the fields.

```json
{
    "success": false,
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "The email field is required.",
        "errors": {
            "email": [
                "The email field is required."
            ]
        },
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 429 — Over 5 requests per second for the key. Retry after the limit resets.

```json
{
    "success": false,
    "error": {
        "code": "RATE_LIMITED",
        "message": "Too many requests. Retry after the limit resets.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

## Error codes

- `UNAUTHENTICATED` — https://spaw.co/docs/errors/UNAUTHENTICATED
- `VALIDATION_FAILED` — https://spaw.co/docs/errors/VALIDATION_FAILED
- `INSUFFICIENT_CREDITS` — https://spaw.co/docs/errors/INSUFFICIENT_CREDITS
- `RATE_LIMITED` — https://spaw.co/docs/errors/RATE_LIMITED
- `KEY_SCOPE_DENIED` — https://spaw.co/docs/errors/KEY_SCOPE_DENIED
- `EMAIL_NOT_VERIFIED` — https://spaw.co/docs/errors/EMAIL_NOT_VERIFIED

---

Canonical page: https://spaw.co/docs/api/verify-entity · OpenAPI document: https://spaw.co/openapi.json · All endpoints: https://spaw.co/docs/api
