Tag companies
Give companies from your CRM a tag through the API, and remove it once they leave your list.
With this guide, you keep a tag in Webmetic in line with a list from your customer relationship management (CRM) system, for example your target accounts. At the end you'll have:
- a tag such as Target account on every company in your list that Webmetic knows, including companies that haven't visited your website yet
- a script that gives new entries the tag and removes it from entries that left the list
- the domains for which Webmetic found no company
- the tag in the company list, in the profile, and as a condition for segments
Before you start
- Time: Setting up the script takes a few minutes. Every call answers right away, so there's no result to wait for.
- Account: You need a Webmetic account with at least one website. Without a website, the API answers with
400andNO_WEBSITE. With the key of a partner account, you tag companies in the partner account itself, not for your customers. - Access: You need the Visitor Intelligence key. A key for Enrichment or the Data Layer doesn't work here. You also need cURL or an environment for JavaScript or Python with
requests. - Rights: Only admins see the key under Menu > Integrations > API in the Visitor Intelligence card. The calls themselves only need the key.
- Plan: The calls work on every plan and use no credits.
If you want to try the calls without an account, use the demo key wmtc_demo, which answers with the invented companies from these docs and stores nothing. For details, see Try it without an account.
Assign a tag
Send the tag name and the domains with POST /company-tags. The tag field takes 1 to 100 characters, and domains takes 1 to 10,000 entries.
curl -X POST "https://hub.webmetic.de/company-tags" \
-H "Authorization: wmtc_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tag": "Target account", "domains": ["bergquell.example", "hagedorn.example", "gibtesnicht.example"]}'If your account doesn't have the tag yet, Webmetic creates it, even when no domain matches. The new tag gets the next color from the dashboard's color picker, and you can change the color only there.
Upper and lower case don't matter in the name, so target account matches the existing tag Target account.
Webmetic shortens each entry to the main domain. For example, https://www.bergquell.example/kontakt, info@bergquell.example, and shop.bergquell.example all become bergquell.example.
You can match companies only by domain, not by company ID, name, or the ID from your CRM.
The API looks up the domains among all companies Webmetic knows, not only among the companies that visited your website. So a company gets the tag before its first visit and carries it as soon as it visits your website. You see the tag on the company on every website in your account.
The call only adds the tag, and a company's other tags stay. If you repeat the same call, nothing changes. If a segment had assigned the tag automatically before, it then counts as assigned by hand, and the segment no longer removes it.
Read the response
The API answers with 200 and two fields:
tagged: the number of domains found. Domains whose company already had the tag count too.unknown: the entries for which Webmetic found no company or that contain no valid domain. The API returns them unchanged and in your order.
Webmetic doesn't remember unknown domains. So send these entries again with every sync, as the script below does.
The domains in the examples are invented. With your own key, entries ending in .example always land in unknown.
Remove a tag
Send the same body with DELETE /company-tags. The API answers with untagged and unknown, and untagged also counts domains whose company never had the tag.
curl -X DELETE "https://hub.webmetic.de/company-tags" \
-H "Authorization: wmtc_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tag": "Target account", "domains": ["hagedorn.example"]}'Webmetic removes the tag from the companies found, on every website in your account. It doesn't matter who assigned the tag: you in the dashboard, the CSV tag import, a segment, an integration, or the API.
The tag itself stays in your account. You can delete or rename it only in the dashboard. If there's no tag with this name, the API answers with 404 and TAG_NOT_FOUND.
If a segment that assigns this tag automatically still matches the company, it assigns the tag again at its next check. Segments check every 15 minutes during the day. In that case, a tag your script removes at night is back by the morning.
Sync the list regularly
The API can't read tags back. So your script keeps the list of the last run and compares it with the current list from your CRM:
- Remove the tag from the entries that left the list since the last run.
- Assign the tag to every entry of the current list.
- Save the current list for the next run.
A list with up to 10,000 domains fits into one call. Split bigger lists into batches of that size and send one call per second.
The API accepts one request per second per method, so POST and DELETE count separately. If you send faster, the API answers with 429 and RATE_LIMITED, and the Retry-After header tells you how long to wait. For details, see Rate limits.
You can also build the sync in n8n or Make. Any step that sends an HTTP request with a JSON body works for it.
The CSV tag import in the dashboard under Menu > Company > CSV tag import suits a one-off sync better. It finds only companies that already visited your website and doesn't remove tags.
Complete script
The script connects all the steps. Replace crm_domains with a query of your CRM that returns the website or email address of every record. Then run the script regularly, for example once a night.
The script removes the tag first and assigns it afterwards. That way, a company keeps the tag even when only the spelling of its domain changed in your CRM.
On an error, the script prints the status and the code and doesn't save the list, so the next run repeats everything.
If your CRM returns no entries, for example during an outage, the script stops instead of removing the tag everywhere.
import json
import time
from pathlib import Path
import requests
URL = "https://hub.webmetic.de/company-tags"
HEADERS = {"Authorization": "wmtc_YOUR_API_KEY"} # Visitor Intelligence key
TAG = "Target account"
BATCH = 10_000 # most domains per call
STATE = Path("tag-domains.json") # the list of the last run
def crm_domains():
# replace with the query of your CRM: website or email of every record
return ["bergquell.example", "https://www.hagedorn.example/kontakt", "kronfeld.example"]
def call(method, domains, attempts=5):
for _ in range(attempts):
res = requests.request(method, URL, headers=HEADERS, json={"tag": TAG, "domains": domains}, timeout=60)
if res.status_code == 429:
retry_after = res.headers.get("Retry-After", "")
time.sleep(int(retry_after) if retry_after.isdigit() else 1)
continue
time.sleep(1) # one call per second
if res.ok:
return res.json()
try:
detail = res.json()["detail"]
except (ValueError, KeyError, TypeError):
detail = None
if not isinstance(detail, dict):
detail = {"error": f"HTTP_{res.status_code}", "message": res.text[:200]} # for example a proxy error page
if method == "DELETE" and detail.get("error") == "TAG_NOT_FOUND":
return {"untagged": 0, "unknown": []} # the tag was deleted in the dashboard
raise SystemExit(f"{method} {res.status_code} {detail.get('error')}: {detail.get('message')}")
raise SystemExit(f"{method} still rate limited after {attempts} attempts")
current = sorted(set(crm_domains()))
if not current: # for example during a CRM outage
raise SystemExit("The CRM returned no entries, nothing changed")
previous = set(json.loads(STATE.read_text())) if STATE.exists() else set()
removed = sorted(previous - set(current))
# 1. Remove the tag from entries that left the list
for i in range(0, len(removed), BATCH):
print("untagged:", call("DELETE", removed[i:i + BATCH])["untagged"])
# 2. Tag every entry of the current list
unknown = []
for i in range(0, len(current), BATCH):
body = call("POST", current[i:i + BATCH])
print("tagged:", body["tagged"])
unknown += body["unknown"]
print("unknown:", unknown) # sent again on the next run
# 3. Keep the list for the next run
STATE.write_text(json.dumps(current))Check the result in the dashboard
No API response contains tags, neither from the visit endpoints such as /company and /company-sessions nor in webhooks, and the AI assistant doesn't know them either. So check the result in the dashboard.
In the company list, you see up to two tags per card, and Webmetic groups the rest as "+N". In a company's profile, you find all tags in the Tags section.
Segments with the condition Tags > Tag is or is not take tags from the API into account. This applies in the dashboard, in a segment's email alerts, and to the segment.entered webhook.
A tag you assign through the API doesn't fire this webhook right away, though, because Webmetic checks a company again only when its data changes. In practice, that's its next visit. For details, see Webhooks.