Playbooks · Recipe

Clean a Domain, Then Enrich the Company: the Second Half Doesn't Exist

Normalising a messy URL into a root domain costs 0.5 credits and works well. The step you want next does not exist: company enrichment takes a LinkedIn company URL, not a domain, and the domain-based company enricher waterfall is not in the live OpenAPI. This page covers what `clean_domain` is genuinely good for, and what to do about the missing leg.

Last updated September 22, 2026

Pattern: clean_domain (messy URL in, root domain out) → dedupe or join on the clean domain. The chain stops there. To get company data you need a LinkedIn company URL, and finding one from a domain is a separate problem with no single-call answer today.

Credit math: 5,000 messy URLs through /clean_domain at 0.5 credits per call is 2,500 credits, and it bills on every 2xx because clean_domain is not a waterfall endpoint. If deduping on the clean domain collapses 5,000 rows to 3,100 unique companies, you have removed 1,900 rows of downstream spend, which on a 5-credit endpoint is 9,500 credits you do not spend. That arithmetic is the whole case for the call.

ROI math: clean_domain pays for itself as a dedupe key, not as an enrichment step. Work out your duplicate rate before committing: if a list is already clean, skip the call, because at 0.5 credits a row it is not free. Credit tiers on /pricing.

Owned internally by: Whoever owns the list hygiene step. This is a data-cleaning call, not a GTM one.

Best for: Anyone joining two lists on a domain column, deduping form fills, or normalising a scrape before it becomes a spend decision.

example
# This half works and is worth doing.
curl -s https://api.richapi.ai/api/v1/clean_domain \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{"messy_url": "https://www.Example.COM/pricing?utm_source=x"}'
# -> a clean root domain you can use as a join key.

# This half is what people expect next, and it does NOT take a domain:
curl -s https://api.richapi.ai/api/v1/enrich_company \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{"url": "https://www.linkedin.com/company/acme"}'
# ^ note the input. A LinkedIn company URL. Sending example.com here
#   does not work, and there is no public endpoint that converts one
#   to the other in a single call.

# What clean_domain is actually for — same key on both sides of a join:
curl -s https://api.richapi.ai/api/v1/find_website_by_company_name \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{"company_name": "Acme Technologies, Inc."}'
# -> a website URL, which you then pass through clean_domain so it
#    matches the domains already in your table.

Clean a domain, then enrich the company: the second half doesn't exist

Most pages about a two-step recipe describe two steps. This one describes one step that works and one that people assume exists and that doesn't, because you finding that out here is cheaper than you finding it out in a production loop at 2am.

The step that works

`/clean_domain` takes `messy_url` and gives you back a root domain. Scheme, `www.`, path, query string, casing: gone. `https://www.Example.COM/pricing?utm_source=x` becomes the thing you actually wanted. 0.5 credits per call. It bills on every 2xx, including one where your input was nonsense, because `clean_domain` is **not** a multi-provider waterfall endpoint, so there is no free-miss case here. The miss-free rule covers `email_finder`, `email_verifier` and `phone_finder` and nothing else.

The step that doesn't

The obvious next move is to feed that clean domain into `/enrich_company` and get firmographics. **It doesn't take a domain.** `/enrich_company` has one required field, `url`, and `url` means a **LinkedIn company page URL**, as in `linkedin.com/company/acme`. Pass `acme.com` and you are not enriching a company, you are making a malformed request. And the endpoint people reach for when they hear this, the domain-based `company_enricher` waterfall, **is not in the live OpenAPI**. We checked the spec while writing this page: the string appears zero times. It is not deprecated, not hidden, not behind a flag you can ask for. Do not build against it. So the honest statement, in one sentence: **there is no public RichAPI endpoint that takes a company domain and returns company data.** Not a slow one, not an expensive one. That's a product gap and we'd rather name it than write a page that chains into a 400.

What clean_domain is genuinely good for

Three jobs, and it's good at all three. **Deduping.** 5,000 scraped URLs are rarely 5,000 companies. Normalise, group, count. If that collapses to 3,100 unique domains, you just deleted 1,900 rows of downstream spend. On a 5-credit endpoint that's 9,500 credits not spent, against 2,500 credits spent normalising. The dedupe rate is the whole argument, so measure it on a sample before you run the list. **A join key.** Two lists, both with a domain column, both formatted by different people. One has `www.acme.com`, the other has `https://acme.com/`. They do not join. Run both sides through `clean_domain` and they do. This is unglamorous and it is the most common reason to call it. **Normalising what other endpoints return.** `/find_website_by_company_name` at 1 credit takes a name and gives you a website URL in whatever form that site publishes. Push it through `clean_domain` and it matches the rows you already have. Pair it with `/normalize_company` at 0.5 credits on the name side and both of your join columns are consistent. [gtm data utilities](/use-cases/gtm-data-utilities) covers that family properly.

What to do today if you have domains and want company data

You need a LinkedIn company URL. There are two routes, neither of which is a guarantee: - `/linkedin_company_search` at 0.1 credits per result. Per-result pricing, so a cost estimate without a stated result count is meaningless. Search, take the best match, and accept that "best match" is a judgment call your code now owns. - `/web_social_links` at 1 credit against the company's homepage. Plenty of sites link their LinkedIn page in the footer. Plenty don't. Both bill on any 2xx, including one that finds nothing. Then, with a LinkedIn company URL in hand, `/enrich_company` at 1 credit does what you originally wanted. That's three calls and a matching decision where you expected one call. If your rows already carry a LinkedIn company URL from wherever they came from, skip all of this and go straight to `/enrich_company`.

The failure mode: assuming the chain, shipping the chain

Here is the bug this page exists to prevent. Someone reads "clean domain" and "enrich company" on the same page, writes `enrich_company({url: cleanDomain})`, tests it against a row that happened to have a LinkedIn URL cached, and ships. Then the batch runs and every row fails identically. If you built it that way already, the tell is a uniform failure across the whole list rather than a scattered one. Scattered misses are data. Uniform failures are wiring.

Domain cleaning FAQ

**Is domain-in company enrichment coming?** We're not going to give you a date. Build with the LinkedIn URL as the key and you won't have to rewrite if it lands. **Does clean_domain validate that the domain resolves?** No. It normalises a string. It does not check DNS, and it will happily clean a domain that doesn't exist. **Is a clean_domain miss free?** No. It bills on any 2xx. Only `email_finder`, `email_verifier` and `phone_finder` are miss-free. **Can it turn "Acme Inc." into acme.com?** No, that's a name lookup, not normalisation. `/find_website_by_company_name` does that job. **What about subdomains?** It returns a root domain, which is usually what you want for a join and occasionally not. Check a sample of your weirder rows before you rely on it.

Where to go after domain cleaning

[gtm data utilities](/use-cases/gtm-data-utilities) for the rest of the cleaning endpoints and what each costs, /api/enrich-company for the LinkedIn URL requirement in the spec, [people search to email waterfall](/use-cases/people-search-to-email-waterfall) for a chain that does hold together end to end, [n8n](/integrations/n8n) if this cleaning step belongs in a workflow, and [use cases](/use-cases) for the rest. Tiers on [pricing](/pricing). **25 free credits, no card.** Enough to normalise fifty messy URLs and see your real duplicate rate.

Frequently asked.

Is domain-in company enrichment coming?
We're not going to give you a date. Build with the LinkedIn URL as the key and you won't have to rewrite if it lands.
Does clean_domain validate that the domain resolves?
No. It normalises a string. It does not check DNS, and it will happily clean a domain that doesn't exist.
Is a clean_domain miss free?
No. It bills on any 2xx. Only `email_finder`, `email_verifier` and `phone_finder` are miss-free.
Can it turn "Acme Inc." into acme.com?
No, that's a name lookup, not normalisation. `/find_website_by_company_name` does that job.
What about subdomains?
It returns a root domain, which is usually what you want for a join and occasionally not. Check a sample of your weirder rows before you rely on it.

More playbooks

Try it with 25 free credits.