CormorantDocs
Menu

Company data

Cormorant keeps a company register: one structured record per company anyone has asked about, with every value attributed to the source that published it, when it was seen and the raw document it came from. Records are versioned, never thrown away, refreshed when they are asked for again, and kept current by official change feeds. Data comes from official registers, GLEIF and other open, legitimate sources (see Sources).

How a request is answered#

  1. You name a company by its Cormorant id (cmp_…) or an identifier (ch_uid:CHE-101.329.561, lei:…, us_cik:320193, uk_crn:…, isin:CH0038863350).
  2. Cormorant answers from the register. For each source that knows the company, data older than the source's freshness class (registry core 24 h, people 7 days, …) or your max_age is re-fetched first.
  3. A company that is not yet in the register is fetched from the sources that know the identifier's scheme and added.
  4. The answer says per source what happened (refresh): refreshed, fresh, failed (the register still answers), not_found, pending (slower than 2 s, or the source's canaries fail: it finishes in the background and the next request has the data) or skipped (the source is backing off after failures). Stale sources are refreshed at the same time, and the answer never waits more than about 2 s for them: you get the stored record with its freshness at once.

An ISIN names the company that issued the security: Cormorant asks GLEIF's ISIN-to-LEI mapping, fetches the issuer by its LEI and keeps the ISIN on the record, so the next request by that ISIN is answered by the register. An identifier no source knows is remembered as unknown for a while (ISINs 24 hours, other schemes 1 hour; refresh=true asks again), so repeating it in bulk runs costs nothing at the sources.

Every call is metered as one company request (see Costs & billing); answers from the register without any refresh count as cached.

Examples#

Find a company, then read its record:

curl
curl -s "https://api.cormorant.ch/v1/companies/search?q=UBS%20Group&country=CH" \
  -H "Authorization: Bearer $CORMORANT_API_KEY"

curl -s "https://api.cormorant.ch/v1/companies/lei:549300SZJ9VS8SGXAN81?include=ownership" \
  -H "Authorization: Bearer $CORMORANT_API_KEY"
Python
import os, httpx

api = httpx.Client(
    base_url="https://api.cormorant.ch",
    headers={"Authorization": f"Bearer {os.environ['CORMORANT_API_KEY']}"},
)
hits = api.get("/v1/companies/search", params={"q": "UBS Group", "country": "CH"}).json()
best = hits["results"][0]
record = api.get(f"/v1/companies/{best['ref']}", params={"include": "people,ownership"}).json()
print(record["names"]["legal"], record["status"]["value"])
for field, p in record["provenance"].items():
    print(field, "from", p["source"], "seen", p["observed_at"])

Match a name to a company (entity resolution):

curl
curl -s -X POST https://api.cormorant.ch/v1/companies/resolve \
  -H "Authorization: Bearer $CORMORANT_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "UBS AG", "country": "CH", "city": "Zürich"}'

match is set only when one candidate is a confident match; otherwise read candidates and their reasons. A match is put into the register right away, so it comes back with its cmp_ id and in_register: true: read the record with GET /v1/companies/{match.id}, no second lookup by ref needed. Pass "persist": false to leave the register untouched (the match may then be a live hit with id: null and a scheme:value ref).

How fast. A company already in the register is answered from it: resolve with a confident register match and search with a near-exact register hit do not ask any source (milliseconds). Otherwise the live sources are asked at the same time and waited for at most 2.5 s; a source that is slower, backing off after failures, or failing its canaries does not hold the answer up. live_sources says per source what happened (ok, failed, timeout, skipped) and how long it took, so you can tell a partial answer from a complete one. After resolve stored a new company, the other sources' data about it is fetched in the background.

Need fresher data than the freshness class? Pass max_age (seconds) or refresh=true:

curl
curl -s "https://api.cormorant.ch/v1/companies/ch_uid:CHE-101.329.561?max_age=3600" \
  -H "Authorization: Bearer $CORMORANT_API_KEY"

What changed and when — every value each source ever asserted:

curl
curl -s "https://api.cormorant.ch/v1/companies/ch_uid:CHE-101.329.561/history?field=names.legal" \
  -H "Authorization: Bearer $CORMORANT_API_KEY"

Many companies at once: a batch job (JSON or CSV in, JSON or CSV out):

curl
curl -s -X POST https://api.cormorant.ch/v1/companies/batch \
  -H "Authorization: Bearer $CORMORANT_API_KEY" -H "Content-Type: application/json" \
  -d '{"action": "get", "items": [{"ref": "lei:BFM8T61CT2L1QCEMIK50"}, {"ref": "ch_uid:CHE-116.281.710"}],
       "idempotency_key": "crm-sync-2026-10-20"}'
# then: GET /v1/jobs/{job_id}?include=items  or  GET /v1/companies/batch/{job_id}.csv

Bulk: thousands of companies#

For a list of 10,000 companies, use batch jobs, not 10,000 synchronous calls:

  1. Resolve once, with what you know best. POST /v1/companies/batch with action: "resolve" (or /batch/csv with name,country,city,isin,identifiers columns). An identifier (isin, lei, ch_uid, …) is matched exactly and costs at most one source call; a name is scored over the register and the sources of its country (always give country). Items wait up to 12 s for slow sources (completeness before speed) and confident matches are stored, so every result has company_id.
  2. Keep the cmp_ ids. Later runs use action: "get" with ref = the cmp_ id: answered from the register, refreshed only when a source's data is older than its freshness class (or your max_age).
  3. Follow changes with GET /v1/companies/changes or a watch instead of re-reading every record.

The sources' own limits set the pace of the first run for companies not yet in the register (GLEIF: 60 requests per minute; the Swiss UID register about 20), so a first run of 10,000 new companies takes hours; repeated runs are answered from the register. Identifiers no source knows are remembered as unknown for a while and answered at once when repeated.

Read more#

View as Markdown