Use case · Data & RevOps

CRM Company Data Enrichment API

Your account table has three spellings of the same company and no way to know it. Resolve every account to one official registry number, then enrich it with the legal name, status, address and activity that number belongs to.

Published 15 September 2026

The problem

RevOps, data and CRM-admin teams inherit an account table built from imports, manual entry and whatever a sales rep typed before a demo. The same company ends up as several rows — a legal suffix here, a missing article there, a rebrand nobody updated — because nothing in the CRM enforces one identity per company. Finance carries a related version of the same problem: an invoice names a counterparty, and someone has to work out which legal entity that actually is before a credit decision or a revenue number can rely on it.

The fix is not a smarter dedupe pass over the names you already have. It is replacing the name with a key that does not vary: the country a company is registered in, plus the number that registry itself uses.

The pattern

Four endpoints, five steps.

  1. Resolve in bulk. POST /companies/lookup (see the company lookup API) takes up to 100 items per call: a name for a fuzzy match, or an exact company_number, nif, siren or vat when you already have one. This is entity resolution — turning whatever the CRM has on file into the one identifier the registry itself issues.
  2. Deduplicate by number, not by name. Take country plus company_number from the result and use that pair as the key on every account from now on. Validate the check digit before you spend a lookup on a typo, with the free company identifier validator — Spain, France, Poland and Norway all carry a real check digit; UK and Irish numbers do not, so only their format can be checked. Country-by-country rules are in EU + UK company identifiers.
  3. Enrich the record. The result from step 1 already carries the core registry fields inline — legal form, status, founding and dissolution dates, address, activity code and description, share capital, former names. Call GET /companies/{id}?country= (the id from step 1) for the fuller profile, including embedded officers where the country publishes them.
  4. Check the VAT number separately. If you invoice the account, GET /vat/{vatNumber} validates it against VIES and returns the registered name and address as a second, independent source.
  5. Keep it current. POST /monitor subscribes a company_id from every live country except France and Chile to a webhook that fires on status changes, dissolution and sanctions matches (beta). For France, the UK and Norway, re-run the batch lookup for accounts that changed, on whatever schedule fits.
curl -X POST 'https://api.prometiam.com/functions/v1/risk-api/companies/lookup' \
  -H 'Authorization: Bearer rk_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "items": [
      { "country": "ES", "name": "Acme Soluciones SL" },
      { "country": "FR", "siren": "552032534" },
      { "country": "GB", "company_number": "00445790" }
    ]
  }'

# data[] is one result per item, in request order:
# { "index": 0, "status": "found", "match_score": 92,
#   "company": { "company_number": "B12345678", "country": "ES", "status": "active", ... } }
# meta: { "items": 3, "found": 3, "not_found": 0 }

status in data[] is the lookup outcome (found / not_found / error / timeout); company.status is the registry status (active, dissolved, in_liquidation…) — do not confuse the two when you write results back.

Customer dashboard screenshot: a pasted list of companies looked up at onceCustomer dashboard screenshot: a pasted list of companies looked up at once
POST /companies/lookup without code: paste a list or upload a CSV, see which lines were found and download the table. Every line costs 1 credit, found or not. Open it in the dashboard

A real example

The script below reads a CRM export with account_name and country columns, resolves it in batches of 100, and writes back only what clears a match_score of 85 — anything lower is marked needs_review alongside its score, so a person decides instead of the script guessing on your behalf.

import csv
import requests

API_KEY = "rk_live_..."          # masked — never commit a real key
BASE = "https://api.prometiam.com/functions/v1/risk-api"
MATCH_FLOOR = 85                 # below this, a human reviews the match
BATCH_SIZE = 100

def chunks(rows, size):
    for i in range(0, len(rows), size):
        yield rows[i:i + size]

def lookup(batch):
    items = [{"country": r["country"], "name": r["account_name"]} for r in batch]
    resp = requests.post(f"{BASE}/companies/lookup",
                          headers={"Authorization": f"Bearer {API_KEY}"},
                          json={"items": items}, timeout=30)
    resp.raise_for_status()
    return resp.json()["data"]

def enrich(accounts):
    rows = []
    for batch in chunks(accounts, BATCH_SIZE):
        for account, result in zip(batch, lookup(batch)):
            company, score = result.get("company"), result.get("match_score")
            ok = result["status"] == "found" and company and (score is None or score >= MATCH_FLOOR)
            rows.append({
                "account_name": account["account_name"],
                "country": company["country"] if ok else account["country"],
                "company_number": company["company_number"] if ok else "",
                "registry_status": company["status"] if ok else "needs_review",
                "match_score": score if score is not None else "",
            })
    return rows

with open("crm_accounts.csv", newline="", encoding="utf-8") as f:
    accounts = list(csv.DictReader(f))            # columns: account_name, country

enriched = enrich(accounts)
with open("crm_accounts_enriched.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=list(enriched[0].keys()))
    writer.writeheader()
    writer.writerows(enriched)

About 40 lines end to end. Run with python -u; requests is the only dependency.

What comes back

The fields below come back on the company object embedded in POST /companies/lookup and on GET /companies/search; GET /companies/{id} returns the same fields plus embedded officers where the country publishes them.

FieldWhat it is
company_number (+ nif / siren)The registry primary key — the value to store as your dedupe key, alongside country.
company_nameRegistered legal name, always uppercase.
legal_forme.g. S.A., SARL, Ltd — null where the registry does not publish one.
statusRegistry status in that country's own vocabulary (active, dissolved, in_liquidation, bankrupt…). Values are not a shared enum across countries.
founding_date / dissolution_dateIncorporation date and, where it applies, the date the entity closed.
address, postal_code, city, provinceRegistered address, uppercase.
activity_code / activity_descriptionCNAE (ES), NAF (FR), SIC (GB/IE), PKD (PL), NACE (NO). The description is text only for ES/FR/NO.
capital_amount / capital_currencyShare capital as last published, in the registry's own currency (EUR for ES/FR, PLN for PL, NOK for NO).
previous_namesFormer registered names — sourced for the UK and Norway; the other countries typically return none.
last_seen_dateThe most recent registry publication in which Prometiam has seen this record.
Customer dashboard screenshot: a company profile with identifiers and signalsCustomer dashboard screenshot: a company profile with identifiers and signals
The same record in the dashboard: identifiers, registered office, and the insolvency, sanctions (beta) and register signals on one page. Shown for a UK company. Open it in the dashboard

Pricing fit

A 50,000-account CRM run through /companies/lookup in batches of 100 is 500 calls, but the plan meter counts every item: 50,000 requests and 50,000 credits. That one-off backfill is a month of the Scale tier (€99.99/month, 50,000 credits/month, 300 requests/minute), or a few months on Professional (€29.99/month, 10,000 credits/month, 120 requests/minute). After the backfill, a nightly refresh only has to touch accounts that actually changed — a few hundred a night for most books, roughly 6,000 to 10,000 credits a month — which fits Professional; a few thousand a night needs Scale.

A smaller list, or a first test against real accounts, fits inside the Free trial's 500 credits over 14 days, with a card required and nothing charged for 14 days.

Scope limits, stated plainly

  • Fuzzy matching returns a best guess with a score, not a certainty. Keep anything under the threshold you choose — this page uses 85 as an example — on a review queue instead of writing it back automatically. For accounts typed in by hand, a company autocomplete field lets the user pick the registry record instead of typing a free-text name.
  • No personal emails, no inferred contacts. What Prometiam returns about a company is registry data: name, status, address, activity and, where published, officers — not a lead list.
  • No estimated financials, no employee counts, no credit score of any kind. Share capital is the only money field on the company record, and only where the registry itself published it. Filed annual accounts for GB, FR, DK, SE, NO and SEC filers in the United States, for Finland (statements filed digitally with PRH, about one filer in twenty, and listed groups) and for listed companies in ES, PL, HR and BE, are a separate endpoint from the Scale plan.
  • Ireland is company-level today — no officers yet.
  • previous_names is populated for the UK and Norway; the other countries typically return none, so a rebrand elsewhere will not surface there.
  • Norway has register-change events only, from 14 Oct 2026 (no gazette), so a Norwegian account can be watched for register changes from that date.
  • Monitoring (POST /monitor) covers every live country except France and Chile. Refresh France, the UK and Norway on your own schedule.

FAQ

How do I match messy CRM company names to an official registry record?
Send up to 100 account names per call to POST /companies/lookup. Each fuzzy name match returns the best candidate with a match_score from 0 to 100; an exact company_number, nif, siren or vat resolves directly with no score needed.

What should I store as the dedupe key instead of the company name?
Country plus company_number — the identifier the registry itself assigns: NIF for Spain, SIREN for France, the Companies House number for the UK, the CRO number for Ireland, KRS for Poland, the organisation number for Norway, the Y-tunnus for Finland, the organisationsnummer for Sweden, the enterprise number for Belgium, the MBS or OIB for Croatia, the CVR number for Denmark, the registry code for Estonia, the IČO for Slovakia, the UID for Switzerland, the registration number for Latvia, Cyprus and Romania, the legal entity code for Lithuania, the corporation number for Canada, the ACN or ARBN for Australia, the corporate number for Japan, the company number for Israel, the UEN for Singapore, the NIT for Colombia, the unified business number for Taiwan, the IČO for the Czech Republic, the CNPJ for Brazil, the CUIT for Argentina, the LEI for Germany, the state-prefixed number for the United States (such as NY-4424185). Validate the check digit first with the free company identifier validator.

How many CRM accounts can I resolve in one request?
Up to 100 items per POST /companies/lookup call. Each item still counts as one request against your rate limit and costs 1 credit, so 50,000 accounts is 500 batch calls but 50,000 counted requests and 50,000 credits.

Does Prometiam return emails or phone numbers for CRM contacts?
No. Prometiam returns no personal emails and no inferred contacts — what comes back is registry data about the company itself: name, status, address, activity and, where published, officers. It is not a contact-finder product.

How do I keep enriched CRM records up to date?
Subscribe companies registered in any live country except France and Chile to POST /monitor for a webhook on status changes, dissolution and sanctions matches (beta); Mexico covers its LEI-registered entities only and Argentina has no dissolution alert. For France and Chile, re-run the batch lookup for changed accounts on a schedule — nightly is typical.

Related