> ## Documentation Index
> Fetch the complete documentation index at: https://docs.topograph.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Company Data

> How to request company data from public registers via /v2/company

The `/v2/company` endpoint is the single entry point for company data, official documents, and ownership graph traversal. It accepts one company identifier, one country, and the datapoints or documents you want.

Use it for company onboarding, KYB verification, shareholder and UBO checks, subsidiary analysis, official document retrieval, and follow-up polling by `requestId`.

<Info>
  For live country coverage, data block availability, pricing, sources, identifiers, and document catalogs, see [Coverage and pricing](/essentials/coverage-and-pricing).
</Info>

## Request shape

```bash theme={null}
curl --request POST \
  --url https://api.topograph.co/v2/company \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '{
    "countryCode": "FR",
    "id": "928020932",
    "dataPoints": ["company", "legalRepresentatives", "availableDocuments"]
  }'
```

## Datapoints

Availability varies by country and mode. Use the live pricing pages or `GET /v2/pricing` for country-specific availability.

| Datapoint | What it returns |
| - | - |
| `company` | Core registration record: legal name, identifiers, legal form, status, address, activity codes, capital, and tax identifiers when available. |
| `legalRepresentatives` | Directors, managers, officers, and other people or companies with legal authority to represent the company. |
| `otherKeyPersons` | Non-signing roles such as auditors, supervisory board members, registered agents, or similar roles when the register exposes them separately. |
| `establishments` | Branches, secondary locations, business premises, or registered establishments. |
| `shareholders` | Direct shareholders and ownership details as available from the register or official filings. |
| `subsidiaries` | Companies owned by the queried company, when downstream participation data is available. |
| `ultimateBeneficialOwners` | Declared beneficial owners and control information where available. |
| `availableDocuments` | List of official documents available for the company. Use the returned document IDs in a follow-up request. |
| `graph` | Multi-level ownership traversal across companies and jurisdictions. See [Ownership graph](/essentials/ownership-graph). |

The legacy `companyProfile` datapoint is still accepted for existing integrations. New integrations should request explicit datapoints. See the [migration guide](/guides/migration-data-product-model).

## Legal Entity Identifier (LEI)

The `company` datapoint includes `company.identifiers.lei` when a company identifier uniquely matches a registration or validation reference in the GLEIF database. Existing country identifiers remain available alongside it.

Coverage varies by jurisdiction and entity. The field is absent when there is no unambiguous identifier match; absence does not prove that the company has no LEI. Lapsed and retired LEIs may be returned, while annulled LEIs are excluded.

```json theme={null}
{
  "countryCode": "FR",
  "identifiers": {
    "siren": "421100645",
    "lei": "96950066U5XAAIRCPA78"
  }
}
```

## Person identities and role entries

`entityId` identifies a person or company within a request. It links occurrences of the same entity across datapoints, such as `legalRepresentatives`, `shareholders`, and `ultimateBeneficialOwners`. It is not a permanent identifier to match people across separate requests.

**An `entityId` can appear more than once in `legalRepresentatives`.** A person holding several roles can have a separate entry for each appointment, with the same `entityId` and different role details or appointment dates.

For example, a Belgian director can also be a member of the management committee:

| Field | First entry | Second entry |
| - | - | - |
| `entityId` | `person_001` | `person_001` |
| `role.localName` | `Administrateur` | `Membre du comité de direction` |
| `role.englishTranslation` | `Director` | `Member of the management committee` |
| `role.standardized` | `Director` | `Director` |

To import one record per person, group entries by `entityId` within the request and retain all associated roles and appointment dates. Neither `entityId` alone nor the combination of `entityId` and `role.standardized` is a unique key for a role entry. Different local roles can share a standardized category. If `entityId` is absent, do not group all entries without an ID into one person.

## Choose a mode

`/v2/company` supports two modes:

* Verification mode is the default and prioritizes authoritative data for compliance.
* Prefill mode (`mode: "prefill"`) prioritizes speed for form prefill and early screening.

Both modes return the same response shape. Read [Verification vs prefill mode](/essentials/modes) before deciding which mode to use in a customer-facing flow.

```json theme={null}
{
  "countryCode": "DE",
  "id": "HRB123456",
  "dataPoints": ["company", "legalRepresentatives"],
  "mode": "prefill"
}
```

## Documents

Document retrieval is a two-step flow:

1. Request `availableDocuments`.
2. Pass selected document IDs in the `documents` array.

```json theme={null}
{
  "countryCode": "FR",
  "id": "928020932",
  "documents": ["1c932de4-4610-5506-b48d-4e62529d58e8"]
}
```

Document IDs are opaque and company-specific. Always discover them through `availableDocuments` before requesting a download. See [Document retrieval](/essentials/document-guide).

## Response lifecycle

Every request returns a `request` object with the current status of each datapoint and document.

```json theme={null}
{
  "request": {
    "companyId": "932884117",
    "countryCode": "FR",
    "requestId": "253299d1-e8d0-4268-945b-f175f98bc114",
    "dataPoints": ["company", "legalRepresentatives"],
    "dataStatus": {
      "dataPoints": {
        "company": {
          "status": "succeeded",
          "authoritative": true
        },
        "legalRepresentatives": {
          "status": "in_progress"
        }
      },
      "documents": {}
    }
  }
}
```

| Status | Meaning |
| - | - |
| `succeeded` | The datapoint or document is available. |
| `in_progress` | Retrieval is still running. Poll again or wait for a webhook. |
| `enriching` | A partial result is available while enrichment continues. |
| `failed` | Retrieval failed. Inspect `error.code` and `error.message`. |

The response may be progressive. For example, `company` can succeed before `legalRepresentatives`, documents, or ownership datapoints finish.

### Not every `failed` is an outage

These `error.code` values mean the datapoint is not something we return for this
request, rather than something that went wrong:

| `error.code` | Meaning | Example |
| - | - | - |
| `datapoint_not_applicable` | The datapoint does not apply to this entity. | `shareholders` for a sole trader. A natural person trading in their own name has no share capital, so the owner appears under `legalRepresentatives`. |
| `datapoint_not_supported` | We do not return this datapoint for this country, in any mode. | `ultimateBeneficialOwners` where the country publishes no beneficial-owner register, or where we do not yet source it. |
| `fast_source_unavailable` | The datapoint exists for this country, but not in prefill mode. Retry without `mode: "prefill"`. | `graph` requested in prefill mode. |
| `authoritative_source_unavailable` | The datapoint exists for this country, but only in prefill mode. Retry with `mode: "prefill"`. | `company` in a country served only from a non-authoritative source, requested in verification mode (the default). |

All of them carry `retryable: false`: the same request gets the same answer. Handle them the way you would handle a successful
"nothing to report" answer: don't retry, don't alert, don't block your flow waiting
for them. Filter on the code or on `retryable`. `error.message` is human-readable and
safe to show your own users. The complete list of codes, with `retryable` and the
recommended `retryAfterSeconds` for the ones you can resubmit, is in
[Reliability & Errors](/guides/reliability-and-errors#datapoint-and-document-error-codes).

To know which datapoints a country returns *before* you request them, see
[Coverage and pricing](/essentials/coverage-and-pricing) or `GET /v2/pricing`.

### An empty list is an answer

The list-shaped fields — `legalRepresentatives`, `otherKeyPersons`,
`establishments`, `shareholders`, `subsidiaries`, `ultimateBeneficialOwners` —
follow one rule, in both the API response and the webhook payload:

| Shape | Meaning |
| - | - |
| `[ … ]` | The records the register published. |
| `[]` | The datapoint **succeeded** and the register published no records. We looked; there is nothing there. |
| Key absent | The datapoint was not requested, is still running, or `failed`. Read `dataStatus.dataPoints.<name>` to tell which. |

So a datapoint whose `dataStatus` says `succeeded` always carries its list. You
never have to disambiguate "succeeded with nothing attached" from "never ran" —
`succeeded` plus a missing key cannot happen.

<Note>
  An empty list means *the register holds no such record*, which is not always
  the same as *the entity has none*. Some registers only publish part of a
  category: the Dutch KVK, for example, registers a shareholder only when a
  company has a single 100% holder, so a BV with several shareholders returns
  `shareholders: []`. Country pages document these limits — see
  [Netherlands](https://www.topograph.co/countries/nl#reading-an-empty-shareholders-list).
</Note>

## Polling

Use the `requestId` to fetch the latest result. This is free and does not create a new billable request.

```bash theme={null}
curl --request GET \
  --url https://api.topograph.co/v2/company/253299d1-e8d0-4268-945b-f175f98bc114 \
  --header 'x-api-key: <api-key>'
```

Poll every few seconds until the requested datapoints and documents are terminal (`succeeded` or `failed`). In production, use [webhooks](/guides/webhooks) when possible.

<Info>
  Some datapoints are [manually delivered](/essentials/manual-delivery) and stay `in_progress` for typically 1–3 business days. For these, webhooks are strongly recommended over polling.
</Info>

Branch on the status itself rather than enumerating the non-terminal ones. A poller
that waits for anything other than `succeeded` or `failed` will hang if a new
intermediate status is ever introduced.

## PDF extract

Any request can also be returned as a single-file PDF extract, suitable for attaching to a case file or handing to a reviewer.

```bash theme={null}
curl --request GET \
  --url https://api.topograph.co/v2/company/253299d1-e8d0-4268-945b-f175f98bc114/pdf \
  --header 'x-api-key: <api-key>' \
  --output company-extract.pdf
```

The extract renders the company profile, legal representatives, shareholders, ultimate beneficial owners, other key persons, establishments and the ownership graph — whichever of those the request asked for and received. Like polling, it is free and never billed again: it renders data the request already paid for, so you can re-download it as often as you need.

### Requesting before a request has finished

By default the extract renders whatever has arrived, exactly as `GET /v2/company/{requestId}` returns partial results while a request is still running.

The extract is built around the `company` datapoint, so it needs company data to render anything. Until `company` has arrived, the endpoint returns `409` with `extract_data_incomplete`, even without `requireComplete`. If the request settles without company data (because `company` was not requested, or it failed), it returns `409` with `extract_no_data`. Include `company` in any request you want an extract for.

A partial extract says so on its own face, so it can never be mistaken for a complete one:

* The seal reads **PARTIAL** instead of **VERIFIED**.
* A **Data completeness** section lists every requested datapoint and whether it was included, is still being retrieved, or failed — with the reason.
* The `X-Topograph-Data-Status` response header carries `complete` or `partial`, so you can branch without opening the file.

If you would rather have an error than a partial file — for an archival export that must not be a snapshot of a half-finished request — pass `requireComplete`:

```bash theme={null}
curl --request GET \
  --url 'https://api.topograph.co/v2/company/253299d1-e8d0-4268-945b-f175f98bc114/pdf?requireComplete=true' \
  --header 'x-api-key: <api-key>' \
  --output company-extract.pdf
```

That returns `409` while any requested datapoint is still running, naming what it is waiting on:

```json theme={null}
{
  "code": "extract_data_incomplete",
  "message": "The request is still running, so the extract would be partial.",
  "pendingDataPoints": ["shareholders"]
}
```

<Note>
  Only datapoints decide completeness. Documents are delivered as separate files and never appear in the extract, so a document still downloading does not hold the PDF back.
</Note>

### Seal states

| Seal | Meaning |
| - | - |
| `VERIFIED` | Every requested datapoint settled, from authoritative register sources. |
| `PARTIAL` | The request was still running when the extract was generated. |
| `UNVERIFIED` | A datapoint was served from a non-authoritative source — see [Modes](/essentials/modes). |

### Viewing in a browser

The response defaults to `Content-Disposition: attachment`. Pass `disposition=inline` to render it in place instead:

```
GET /v2/company/{requestId}/pdf?disposition=inline
```

## Pricing and budget controls

Topograph bills by data block and document. A 24-hour deduplication window prevents duplicate billing for the same block, company, and account. Request ID lookups are always free.

Use [Pricing and caching](/essentials/pricing-and-caching) for billing mechanics and [Coverage and pricing](/essentials/coverage-and-pricing) for live country-specific prices and availability.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.