Enrich companies
Send domains from your CRM to an Enrichment list and write the company data back.
With this guide, you add data to companies from your customer relationship management (CRM) system through the API. At the end you'll have:
- an Enrichment list with one row per domain, each finished as found or not found
- industry, employee count, revenue, address, phone number, and more data as JSON or CSV
- a script that you run again for the next domains
- the same list for your team in the Enrichment tab
Before you start
- Time: Setting up the script takes a few minutes. How long the enrichment takes is described in Wait for the result.
- Account: You need a Webmetic account that can use Enrichment. If your account can't use Enrichment right now, for example after a cancellation, the API answers with
403andACCOUNT_NOT_ELIGIBLE. - Access: You need the Enrichment key, not the Visitor Intelligence key. With the wrong key, the API answers with
403andENRICHMENT_KEY_NOT_FOUND. You also need cURL or an environment for JavaScript or Python withrequests. - Rights: An admin generates the key under Menu > Integrations > API with Generate key in the Enrichment card. The calls themselves only need the key.
- Plan: You can generate the key, create lists, and add domains only during the trial, on Growth, and on Enterprise. Enrichment currently costs no credits.
If a key already exists, don't click Regenerate, because after that the old key no longer works.
Create a list
Create the list with POST /enrichment/lists. The body needs a name with 1 to 200 characters, and a description in desc is optional.
The API answers with 201 and the list object. Keep list.id, because you need this ID for every further call.
Names don't have to be unique, so every call you make creates a new list, even with the same name.
Add domains
Send the domains with POST /enrichment/lists/{list_id}/domains in the domains field. You can send 1 to 5,000 entries per request. For more, send further requests to the same list.
Webmetic shortens each entry to the domain. For example, https://www.your-domain.com/contact and info@your-domain.com both become your-domain.com, so you don't have to clean up the entries first.
The API answers with 202 and the counters added, skipped_existing, and invalid, but without row IDs. What the counters mean is described in Duplicate and invalid entries.
Webmetic then enriches the new rows in the background, without you starting anything.
Wait for the result
Call GET /enrichment/lists/{list_id} until pending_rows is 0. The list has no status of its own, and pending_rows counts every row that isn't done or miss yet.
Companies that Webmetic already knows are usually finished after about a minute, and large lists take longer. If Webmetic first has to read a website, it takes hours to a few days.
Because every row ends as done or miss, pending_rows always reaches 0. Still, give your script its own maximum wait so you don't wait for days.
Read the rows only once pending_rows is 0, because before that they can be half filled.
The status calls you make cost no credits, and webhooks don't tell you when a list is finished. For details, see Asynchronous results.
Read the rows
Call GET /enrichment/lists/{list_id}/rows with status=done. This way you only get the rows for which Webmetic found data.
Start with page=1 and raise page until isLast in the pagination block is true. You get up to 10,000 rows per page, which is also the default. For details, see Pagination.
Every row contains all fields, and unknown values are null. Ranges such as 201–500, industries, and descriptions are in German. You'll find every field in Enrichment list.
Alternatively, you download all rows at once with GET /enrichment/lists/{list_id}/export, as a CSV file with German column names. The file has no row ID, and the Domain column contains company_url.
Write the data back to your CRM
Webmetic doesn't write Enrichment data into your CRM, because the CRM integrations only transfer companies that Webmetic identifies on your website. So you write the data back with your own code.
Match the rows to your records by domain:
- Before sending, shorten each record's website to the domain and keep the mapping.
- For every row with
done, look up the records with the samedomain. - Write only fields that aren't
null, so that existing values in your CRM stay as they are. - Check rows without a matching record by hand.
If a website redirects to a domain that Webmetic already knows, domain then holds that domain, and your matching doesn't find the row. company_url, the address where the website answers, helps you check it.
Don't write back rows with miss, because their name is only a placeholder made from the domain.
Enrichment rows have no company_id. You join them with data from the daily visit export by domain as well. To do so, shorten company_url from the visit data to the domain without www..
Complete script
The script connects all the steps. Replace crm with the records from your CRM and write_to_crm (Python) or writeToCrm (JavaScript) with the call to your CRM. The cURL tab shows the individual calls.
The to_domain function shortens more roughly than Webmetic and keeps subdomains. If your CRM holds a website such as shop.your-domain.com, the row therefore ends up in the manual check, even though Webmetic found data.
The JavaScript uses await outside of functions. So run it as an ES module, for example as a file ending in .mjs.
You choose the polling interval and the maximum wait yourself. If the list isn't finished by then, the script stops and prints the list ID. Put the ID in LIST_ID, and the script keeps waiting when you run it again.
On an error, the script prints the status and the code, for example PLAN_REQUIRED. You'll find all codes in Errors.
# 1. Create the list
curl -X POST "https://hub.webmetic.de/enrichment/lists" \
-H "Authorization: wmtc_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "CRM 2026-10"}'
# 2. Add domains, up to 5,000 per request
curl -X POST "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/domains" \
-H "Authorization: wmtc_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domains": ["https://www.bergquell.example/kontakt", "info@nordlicht-logistik.example"]}'
# 3. Repeat until pending_rows is 0
curl "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID" \
-H "Authorization: wmtc_YOUR_API_KEY"
# 4. Read the found rows, raise page until isLast is true
curl "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/rows?status=done&page=1&page_size=1000" \
-H "Authorization: wmtc_YOUR_API_KEY"
# Or: every row of the list as one CSV file
curl "https://hub.webmetic.de/enrichment/lists/YOUR_LIST_ID/export" \
-H "Authorization: wmtc_YOUR_API_KEY" \
-o enrichment.csvThe domains in the example are invented. The API counts entries ending in .example as invalid.
Duplicate and invalid entries
The counters in the response and the status of the rows show what happened to your entries:
- Duplicate in one request: Webmetic creates the domain once and doesn't count the repeats. So the three counters don't always add up to the number of entries you sent.
- Already in the list:
skipped_existingcounts domains that are already in this list. Webmetic doesn't create a second row. In another list, the same domain gets its own row. - Invalid:
invalidcounts entries without a readable domain, for example a company name or an IP address. The API doesn't say which entries they were. If you need to know, shorten your entries yourself before sending, liketo_domainin the script, and check which ones contain no domain. - Several domains in one entry: Webmetic only reads the last one. Send each domain as its own entry.
- Subdomain of a platform: Webmetic shortens it to the platform's domain. So enter the company's own domain.
- Not found: With
miss, Webmetic found no usable data. - Two rows with the same domain: If two domains redirect to the same company, the list can contain two such rows.
- Enrich again:
doneandmissare final, and Webmetic doesn't update finished rows. For fresh data, create a new list.
You can delete lists and remove rows only in the dashboard. For details, see Results and export.