The goal
Turn a rough export into a usable research list
CRM data drifts. Company names change, legal suffixes appear, domains move, and duplicate names point to different businesses. This workflow enriches only deterministic matches and keeps the evidence used for each decision.
python -m venv .venv
source .venv/bin/activate
pip install requests python-dotenv
python enrich_companies.py --input sample_companies.csv
Store DEALROOM_CLIENT_ID and DEALROOM_CLIENT_SECRET in a local .env file. Keep it out of version control.
Keep your own identifiers in the input
The example starts with five columns. The account ID and owner survive the round trip, so the enriched file can return to the source system without relying on row order.
account_id,company_name,website,country,owner
CRM-001,Eleven Labs,https://elevenlabs.io,United Kingdom,Maya
CRM-006,Quantum Motion Technologies,https://quantummotion.tech,United Kingdom,Lena
A website and country make reconciliation safer. Rows can still be searched by name when the website is blank, but the fallback requires one exact normalized name in the same country.
Let strict rules accept search candidates
GET /data/search returns likely companies. The script applies stricter rules after retrieval.
| Decision | Rule | Action |
|---|---|---|
| High confidence | Exact normalized website domain | Enrich automatically |
| Medium confidence | One exact normalized name in the same country | Enrich and retain the method |
| Low confidence | Similar name and same country | Suggest a candidate for review |
| No match | No candidate passes the rules | Preserve the row with blank enrichment fields |
Why the review queue matters
The sample contains a stale Quantum Motion domain. Search finds the likely current company, but the script refuses to attach its UUID or data without a deterministic match.
Fetch detail only after the match is safe
For accepted UUIDs, GET /data/entities/{company_id} supplies company identity, funding, employee, hiring, taxonomy, and Signal fields.
search = client.get(
"/data/search",
{"q": domain or company_name, "types": "company", "limit": 8},
)
match = pick_match(source_row, rows(search))
if match["matched"]:
company = client.get(
f"/data/entities/{match['candidate']['uuid']}",
{"currency": "USD"},
)["data"]
Batch the work row by row or with bounded concurrency. The downloadable script retries rate limits and temporary server errors, refreshes an expired token once, and writes unmatched rows instead of dropping them.
Make the score easy to replace
The example score prioritizes active, well-described companies for research. Dealroom Signal remains a separate API field. Rewrite the score for your own sales, sourcing, or portfolio workflow.
Null values earn no points. That keeps missing data visible instead of silently replacing it with an optimistic assumption.
Real output
Inspect the enriched CRM rows
The saved snapshot includes the original fields, match evidence, enrichment fields, and review status.
Loading the enriched rows...
Snapshot generated from the Dealroom API. Re-run the script before importing current data.
Review matches before writing back to the CRM
- Keep the original account ID and matching evidence with every output row.
- Inspect medium-confidence and duplicate-domain matches before an automated update.
- Never attach enrichment from a low-confidence suggestion without human confirmation.
- Choose overwrite rules field by field. A verified CRM value may be newer than the returned profile.
- Use the priority score only as a queueing rule. It cannot predict an outcome or support an investment recommendation.
Complete example
Download the enrichment workflow
The files include OAuth2 authentication, bounded retries, deterministic matching, confidence flags, CSV output, optional JSON output, and an editable score.