Skip to main content
Historically, /v2/company accepted a single catch-all datapoint called companyProfile and onboarding ran on its own /v2/onboarding endpoint. That model hid what you were actually buying (one companyProfile meant different things in different countries) and forced onboarding and verification down two separate code paths. The Data Product Model replaces companyProfile with explicit, named datapoints and folds /v2/onboarding into /v2/company as a mode. You now ask for exactly the sections you need, the billing and response shape match what you requested, and both retrieval modes share the same endpoint. Both legacy shapes still work. This guide shows you how to move over.

The datapoints

A request to POST /v2/company takes a dataPoints array. Each entry is one of the values below. Availability varies by country (see each country guide).
DatapointWhat you get
companyCore registration record: legal name, registration number, legal form, status, incorporation date, registered address, share capital, activity codes, tax IDs.
legalRepresentativesDirectors, managers, and officers with signing authority.
otherKeyPersonsNon-signing roles the register exposes separately: auditors, supervisory board members, commissaires aux comptes, etc.
establishmentsBranches and secondary locations with their own address and local registration number.
shareholdersDirect shareholders and their ownership percentages, as declared at the register.
ultimateBeneficialOwnersUBO declarations: natural persons with ultimate control, ownership chain, and control type.
availableDocumentsList of official documents the register exposes for this company. Always free. Use the returned IDs in a follow-up documents request.
graphOwnership and control network: nodes (companies, people) and edges (shareholding, subsidiaries, officer roles). Budget-capped with graphMaxBudget.
company, legalRepresentatives, otherKeyPersons, and establishments are the datapoints introduced by the Data Product Model. shareholders, ultimateBeneficialOwners, availableDocuments, and graph pre-date it and have always been callable by name.

What companyProfile means

companyProfile is a legacy alias. It stands for company + legalRepresentatives + otherKeyPersons + establishments, on a best-effort basis. Best-effort means: each of the four expanded datapoints is included only if the country has a block covering it in the mode you requested. If a datapoint has no compatible block, it is silently dropped from the expansion. You are never billed for datapoints the country cannot deliver. Concretely:
CountryVerification block coverscompanyProfile expansion in verification
FR, GB, BE, CZall 4all 4
EEcompany, legalReps, otherKeyPersons, shareholderscompany, legalReps, otherKeyPersons (establishments dropped)
DKcompany, legalReps, establishments, shareholderscompany, legalReps, establishments (otherKeyPersons dropped)
DE, IT, ES, AT, CYcompany, legalRepscompany, legalReps only
The same rule applies in onboarding mode: the expansion is filtered against the country’s fast block. For Italy this means companyProfile in onboarding mode includes otherKeyPersons (the fast block covers it) but establishments is still dropped. Billing is one block per resolved bundle. Expansion never adds an extra SKU.

Replace companyProfile

Swap companyProfile for the datapoints you actually need.
// Before
{ "dataPoints": ["companyProfile"] }

// After (equivalent on countries whose block covers all four)
{ "dataPoints": ["company", "legalRepresentatives", "otherKeyPersons", "establishments"] }

// After (when you only need the basics)
{ "dataPoints": ["company", "legalRepresentatives"] }
Pick the names you consume:
  • company alone if you only need the registration record.
  • company + legalRepresentatives for the classic KYB pair.
  • Add otherKeyPersons when your compliance checks cover auditors or board members without signing authority.
  • Add establishments when you need branch offices.
  • Add shareholders / ultimateBeneficialOwners for ownership checks.
Explicit names make your billing predictable and your response shape visible to the reader of the code.

Do not mix legacy and new names

Within one request, you must pick one vocabulary:
  • Legal: ["companyProfile"], ["companyProfile", "shareholders"], ["companyProfile", "ultimateBeneficialOwners", "availableDocuments"]
  • Legal: ["company", "legalRepresentatives"], ["company", "shareholders", "ultimateBeneficialOwners"]
  • Rejected with HTTP 400: ["companyProfile", "company"], ["companyProfile", "legalRepresentatives"], ["companyProfile", "otherKeyPersons"], ["companyProfile", "establishments"]
The rule is: companyProfile cannot appear alongside any of the four new names (company, legalRepresentatives, otherKeyPersons, establishments). Shared datapoints (shareholders, ultimateBeneficialOwners, availableDocuments, graph) can be combined with either vocabulary.

The response echoes your vocabulary

dataStatus.dataPoints uses the exact keys you sent. Send companyProfile, get companyProfile back. Send company + legalRepresentatives, get them back separately. The internal best-effort expansion is not visible in dataStatus.
// Request: { "dataPoints": ["companyProfile"] }
{
  "dataStatus": {
    "dataPoints": {
      "companyProfile": { "status": "succeeded" }
    }
  }
}

// Request: { "dataPoints": ["company", "legalRepresentatives"] }
{
  "dataStatus": {
    "dataPoints": {
      "company": { "status": "succeeded" },
      "legalRepresentatives": { "status": "succeeded" }
    }
  }
}
This is why existing integrations keep working unchanged. The response body itself contains whatever sections the country’s block produced. For a country whose block covers all four company-level datapoints, a companyProfile request returns company, legalRepresentatives, otherKeyPersons, and establishments fields in the body. Request them explicitly (new vocabulary) to make that shape obvious in your code.

Replace /v2/onboarding

/v2/onboarding is deprecated. Use /v2/company with mode: "onboarding" instead.
# Before
curl -X POST https://api.topograph.co/v2/onboarding \
  -H 'x-api-key: <key>' \
  -d '{
    "countryCode": "DE",
    "id": "HRB123456"
  }'

# After
curl -X POST https://api.topograph.co/v2/company \
  -H 'x-api-key: <key>' \
  -d '{
    "countryCode": "DE",
    "id": "HRB123456",
    "dataPoints": ["company", "legalRepresentatives"],
    "mode": "onboarding"
  }'
Two wins from the move:
  1. You pick the datapoints. The old endpoint only ever returned the equivalent of company + legalRepresentatives. On /v2/company you can add shareholders, ultimateBeneficialOwners, or anything else and still run in onboarding mode.
  2. One codebase, one response shape. Verification and onboarding share the endpoint, the DTO, and the dataStatus format. Read Verification vs Onboarding Mode for the source-selection and deadline behavior.
Onboarding mode enforces a hard 10-second deadline per datapoint and only uses fast sources. Datapoints that have no fast source fail with fast_source_unavailable. See Verification vs Onboarding Mode.

Compatibility summary

Legacy shapeStatusReplacement
dataPoints: ["companyProfile"]Works (best-effort alias for the 4 company-level datapoints)Explicit list: ["company", "legalRepresentatives", "otherKeyPersons", "establishments"]
POST /v2/onboardingDeprecatedPOST /v2/company with mode: "onboarding"
Deprecated fast: true flagWorksmode: "onboarding"
Deprecated authoritative: true flagWorksomit mode or pass mode: "verification"
New integrations should start with the new vocabulary and /v2/company. Existing integrations can stay on companyProfile and /v2/onboarding as long as needed, and migrate opportunistically.

Next steps

Company Data

Full /v2/company request and response reference.

Verification vs Onboarding Mode

Source selection, deadlines, and authoritative flag.

KYB Onboarding

End-to-end flow combining search, onboarding mode, and verification.

Pricing & Caching

How datapoints map to billable blocks and the 24-hour dedup window.