Company Data Enrichment by Domain: A Clearbit Alternative
Pricing
from $2.00 / 1,000 domain registration matches
Company Data Enrichment by Domain: A Clearbit Alternative
Enrich a list of company domains with tech stack without an API key. Company enrichment for an AI agent from a website domain: get RDAP registration and TLS history, DNS, email provider, Shopify and an ATS hiring signal. A Clearbit alternative to Google Sheets or Clay, for an outbound list.
Pricing
from $2.00 / 1,000 domain registration matches
Rating
0.0
(0)
Developer
Adriana Voss
Maintained by CommunityActor stats
1
Bookmarked
1
Total users
0
Monthly active users
8 days ago
Last modified
Categories
Share
Company Domain Enrichment: Company Enrichment by Domain
Feed in a company domain and get seven independent signals back in one call: domain registration (via RDAP), DNS records and email provider (via Cloudflare DoH), detected tech stack, Shopify storefront presence, TLS certificate history (via public Certificate Transparency logs), a hiring signal detected across six ATS platforms, and an email-route/provider fingerprint. Each source is an independent public API or passive check — no login for any of the seven — and each is billed only when it actually returns data for that domain.
Who it's for
The accountable_eel catalogue sells company intelligence columns for outbound. Each actor takes a list of domains or company identifiers and returns one flat, stably-named row per input — firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and per-domain: a few tenths of a cent for a row that was actually found, and nothing for a miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists — VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No seat licence, no monthly minimum, no credit system to decode.
This one is a composite: instead of running seven separate lookups against a domain list, it runs all seven in one call and returns one row per domain, with each source's result nested under its own key and billed independently.
Why this one
- Seven signals, one call, one row per domain. You don't glue together seven actors or seven
API calls client-side —
registration,dns,techStack,shopify,tlsCertificate,hiring, andemailRouteall land in the same dataset row. - Official or primary sources, not scraped guesses. Registration comes from RDAP, DNS from Cloudflare's resolver, TLS history from public Certificate Transparency logs, and hiring data from each ATS platform's own public API — the same sources this portfolio's single-purpose actors use.
- You only pay for sources that return something. A domain with no Shopify storefront costs
nothing for the
shopifysource that run; a domain with no ATS board costs nothing forhiring. ChecksourcesFoundon each row to see exactly what was billed. columnscontrols both output and cost. Narrowing to the three sources you actually use isn't just a display filter — the unselected sources are never queried, so it's a real way to cut the per-domain price.- No proxies, no SMTP, nothing stored between runs. All seven sources are plain HTTPS calls a cloud runtime can make directly; nothing about this actor requires residential IPs or an outbound mail connection.
What you get
One dataset row per input domain. found is true if at least one of the seven sources
returned data; each source field is null when that specific source found nothing (a
non-Shopify site returns shopify: null, not an error).
Top-level fields
| Field | Type | Description |
|---|---|---|
query | string | The domain as submitted |
found | boolean | true if at least one of the seven sources returned data |
status | string | "OK" or an error status for the row |
sourcesFound | array of strings | Which sources actually returned data (and were billed) for this domain |
sourcesTried | array of strings | Which sources were attempted (the sources selected via columns, minus any that errored before returning) |
scrapedAt | ISO 8601 string | When this row was fetched |
registration — RDAP domain registration lookup
| Field | Type | Description |
|---|---|---|
domain | string | The registered domain name, uppercased as returned by RDAP |
registrar | string or null | Registrar name |
registrantOrg | string or null | Registrant organization, when not privacy-redacted |
registeredAt | ISO 8601 string or null | Original registration date |
expiresAt | ISO 8601 string or null | Current expiry date |
status | array of strings | RDAP status codes (e.g. clientTransferProhibited) |
nameservers | array of strings | Nameserver hostnames |
dns — A and MX records via Cloudflare DoH
| Field | Type | Description |
|---|---|---|
domain | string | The domain queried |
aRecords | array of strings | IPv4 addresses from A records |
mxRecords | array of strings | MX records as "<priority> <host>." |
emailProvider | string or null | "Google Workspace", "Microsoft 365", "other", or null if there are no MX records |
techStack — homepage HTML/header signature match
| Field | Type | Description |
|---|---|---|
domain | string | The domain checked |
detectedCount | number | Count of signatures matched |
detected | array of objects | Each entry is { name, category }, e.g. { "name": "Next.js", "category": "framework" } |
shopify — storefront confirmation via /products.json
| Field | Type | Description |
|---|---|---|
domain | string | The domain checked |
isShopify | boolean | Always true when this object is present (it's null at the top level otherwise) |
sampleProductCount | number | Number of products returned by the probe (up to 10) |
sampleProducts | array of objects | Up to 5 entries, each { title, vendor } |
tlsCertificate — current certificate + Certificate Transparency history
| Field | Type | Description |
|---|---|---|
domain | string | The domain checked |
currentIssuer | string or null | Issuer name of the most recent certificate |
currentNotBefore | ISO 8601 string or null | Validity start of the most recent certificate |
currentNotAfter | ISO 8601 string or null | Validity end of the most recent certificate |
currentSanCount | number | Count of subject alternative names on the current certificate |
historicalCertCount | number | Total certificates ever logged for this domain in CT logs |
hiring — ATS board detection across six platforms
| Field | Type | Description |
|---|---|---|
domain | string | The domain checked |
atsDetected | boolean | Always true when this object is present |
atsPlatform | string | The platform with the most open roles, when more than one matched |
atsPlatforms | array of strings | Every platform that had a live board (usually one: greenhouse, lever, ashby, workable, smartrecruiters, personio) |
openRoles | number | Total open roles across matched platforms |
roleCountsByDepartment | object | Role counts keyed by department name (or "Unspecified") |
hiringFor | object | Keyed by job family (sales, engineering, marketing, customer-success, executive by default, or your hiringForGroups); each value is { hiring: boolean, count: number } |
newestPostingAt | ISO 8601 string or null | Publish date of the most recent posting found |
postingsLast30d | number | Postings published in the last 30 days |
postingsPrev30d | number | Postings published in the 30 days before that |
velocity30d | number or null | postingsLast30d / postingsPrev30d, rounded to 2 decimals; null if the prior window has zero postings |
sampleTitles | array of strings | Up to 5 most recent distinct job titles |
emailRoute — MX/provider fingerprint, no SMTP
| Field | Type | Description |
|---|---|---|
domain | string | The domain checked |
hasMxRecord | boolean | Whether the domain has explicit MX records |
mxHosts | array of strings | MX hostnames (empty if mail routes via the bare A record) |
emailProvider | string or null | "Google Workspace", "Microsoft 365", "Zoho Mail", "Mailgun", "SendGrid", "Proton Mail", "other", or null |
isDisposableDomain | boolean | Checked against a community-maintained disposable-domain blocklist, refreshed each run |
Price
- Domain registration match: $4 per 1,000 domains
- DNS match: $4 per 1,000 domains
- Tech stack match: $4 per 1,000 domains
- Shopify storefront match: $4 per 1,000 domains
- TLS certificate match: $4 per 1,000 domains
- Hiring signal match: $4 per 1,000 domains
- Email route match: $4 per 1,000 domains
Plus a $0.001 (from 2 November 2026) start fee per run. Each event above is billed independently, only when it actually returns data — misses (found:false) are never charged.
This is a composite actor, so it doesn't bill per domain — it bills per source that returns data for that domain, and there are seven of them. At the FREE tier ($0.004 per source), the worst case for a single domain is all seven sources hitting: 7 × $0.004 = $0.028 for that one domain, before volume discounts bring the per-source price down to $0.002 on paid tiers. Most domains will land somewhere below that — a domain with no Shopify storefront and no ATS board only gets billed for the sources that actually found something.
Run 1,000 domains and every one hits all seven sources, and you're looking at up to $28 (plus
the one-time $0.001 (from 2 November 2026) actor start) — that's the ceiling, not the typical case, since shopify and
hiring in particular miss on most domains. Compare that ceiling to a credit-based enrichment
platform: the same 1,000 rows there run $80–$400. Use columns to drop sources you don't
need and the real number falls further, since a source that isn't selected is never queried or
billed.
How to use
- In the Apify Console. Open the actor page and click Start — the
domainsfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~company-domain-enrichment/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"domains":["stripe.com"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
- Paste a list of domains into
domains— one per line, bare (stripe.com) or full URL (https://stripe.com) both work. - Turn on
testRunfirst to check your list against just the first 5 domains before committing to a full run. - Use
columnsto pick which of the seven sources you actually want. Every source you drop is neither queried nor billed for that run — this is the main lever for controlling cost. - Read the result: one row per domain, with each selected source nested under its own key
(
row.registration,row.dns,row.techStack, and so on),nullwhen that source found nothing. ChecksourcesFoundon the row to see exactly what was billed. - If you use the
hiringsource, optionally replace the default five job families withhiringForGroupsto match the segments you actually sell into.
Input
{"domains": ["stripe.com"]}
One company domain per line. Each domain is checked against eight independent sources — you're only charged for the ones that actually return something. Accepted formats: stripe.com, https://stripe.com.
{"domains": ["stripe.com"],"testRun": false,"onlyFound": false,"maxConcurrency": 5}
domains is a list of company domains (a bare domain or a full URL both work — www.
and the path are stripped automatically). Each domain is checked against all seven
sources; one dataset row is returned per domain, and you're only charged for the
sources that actually return something for it. Turn on testRun to try your list
against just the first 5 domains before running the full batch. Optional
includeKeywords / excludeKeywords filter rows by content, maxResults caps the run
early, columns lets you pick which of the seven sources to include, and
hiringForGroups customizes the job families the hiring source reports on.
Sample output
| query | found | status | registration | dns | techStack | shopify | tlsCertificate | hiring | emailRoute | emailFinder | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|
| stripe.com | true | OK | <domain registration (rdap)> | {"domain":"stripe.com","aRecords":["198.137.150.161","198.202.176.161"],"mxRecords":["10 aspmx.l.google.com.","20 alt1.aspmx.l.google.com.","20 alt2.aspmx.l.google.com.","30 aspmx2.googlemail.com."... | {"domain":"stripe.com","detectedCount":1,"detected":[{"name":"Next.js","category":"framework"}]} | {"domain":"stripe.com","currentIssuer":"C=US, O=DigiCert\, Inc., CN=DigiCert G5 TLS RSA4096 SHA384 2021 CA1","currentNotBefore":"2026-09-24T00:00:00Z","currentNotAfter":"2026-12-17T23:59:59Z","cur... | {"domain":"stripe.com","atsDetected":true,"atsPlatform":"greenhouse","atsPlatforms":["greenhouse"],"openRoles":716,"roleCountsByDepartment":{"1185 Account Executives (EMEA)":26,"Global Operations":... | {"domain":"stripe.com","hasMxRecord":true,"mxHosts":["aspmx.l.google.com","alt1.aspmx.l.google.com","alt2.aspmx.l.google.com","aspmx2.googlemail.com","aspmx3.googlemail.com"],"emailProvider":"Googl... | {"domain":"stripe.com","mxProvider":"google","isCatchAll":null,"emailStatus":"unknown"} | 2026-10-04T06:10:09.265Z |
One row per domain, for example:
{"query": "stripe.com","found": true,"status": "OK","registration": {"domain": "STRIPE.COM","registrar": "MarkMonitor Inc.","registrantOrg": "Stripe, Inc.","registeredAt": "2010-11-24T02:36:59Z","expiresAt": "2030-11-24T02:36:59Z","status": ["clientTransferProhibited"],"nameservers": ["NS1.STRIPE.COM", "NS2.STRIPE.COM"]},"dns": {"domain": "stripe.com","aRecords": ["104.18.24.44"],"mxRecords": ["1 aspmx.l.google.com."],"emailProvider": "Google Workspace"},"techStack": {"domain": "stripe.com","detectedCount": 2,"detected": [{ "name": "React", "category": "framework" }, { "name": "Cloudflare", "category": "cdn" }]},"shopify": null,"tlsCertificate": {"domain": "stripe.com","currentIssuer": "Let's Encrypt","currentNotBefore": "2026-07-01T00:00:00Z","currentNotAfter": "2026-09-29T00:00:00Z","currentSanCount": 3,"historicalCertCount": 214},"hiring": {"domain": "stripe.com","atsDetected": true,"atsPlatform": "greenhouse","atsPlatforms": ["greenhouse"],"openRoles": 412,"roleCountsByDepartment": { "Engineering": 180, "Sales": 90, "Unspecified": 60 },"hiringFor": {"sales": { "hiring": true, "count": 90 },"engineering": { "hiring": true, "count": 180 },"marketing": { "hiring": true, "count": 12 },"customer-success": { "hiring": true, "count": 8 },"executive": { "hiring": true, "count": 5 }},"newestPostingAt": "2026-08-20T12:00:00.000Z","postingsLast30d": 34,"postingsPrev30d": 21,"velocity30d": 1.62,"sampleTitles": ["Senior Software Engineer", "Account Executive", "Product Marketing Manager"]},"emailRoute": {"domain": "stripe.com","hasMxRecord": true,"mxHosts": ["aspmx.l.google.com"],"emailProvider": "Google Workspace","isDisposableDomain": false},"sourcesFound": ["registration", "dns", "techStack", "tlsCertificate", "hiring", "emailRoute"],"sourcesTried": ["registration", "dns", "techStack", "shopify", "tlsCertificate", "hiring", "emailRoute"],"scrapedAt": "2026-08-21T10:00:00.000Z"}
Each source is null when that source specifically returned nothing (e.g. shopify is
null for a non-Shopify site) — and only sources with real data are charged. A domain
where every source misses still gets a row ("found": false), so you always get one
row per input, but nothing is billed for it.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~company-domain-enrichment/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"domains":["stripe.com"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~company-domain-enrichment/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"domains":["stripe.com"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~company-domain-enrichment/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"domains":["{{domain}}"]}, mapping the row's domain into the domains array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Company Enrichment API: Firmographics by Domain" — the agent will find and run this actor.
Tips
- Use
columnsto control cost, not just output width. If you only care about tech stack and hiring signal, select just those two — the other five sources are never queried, so you're not paying for RDAP or TLS lookups you're going to ignore anyway. - Customize
hiringForGroupsto your own segments. The default five families (sales, engineering, marketing, customer-success, executive) are a starting point — replace them with the job families that map to how you actually segment accounts, one per line as.name: keyword, keyword - Run
testRunbefore a big batch. Five domains is enough to confirm your list format and see what a typical row looks like before you commit to the full run and its full cost. - Check
sourcesFoundvs.sourcesTriedon every row, not justfound. A domain can befound: truefrom a single hit (say, justregistration) while five other sources came back empty —sourcesFoundtells you exactly what you paid for. - Don't expect
shopifyorhiringto hit on most B2B domains. They're conditional signals by nature — a services company with no e-commerce storefront and no public ATS board will legitimately returnnullfor both, at no charge.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (company-domain-enrichment) | $0.004 per source that returns data (down to $0.002 on paid tiers), $0.001 (from 2 November 2026) actor start, nothing for a miss — up to $0.028 per domain if all seven sources hit | Registration, DNS/email provider, tech stack, Shopify presence, TLS certificate history, ATS hiring signal, and email route, nested in one row per domain | Each source is a focused subset (10 tech-stack signatures, 6 ATS platforms, no SMTP verification) — built for enrichment breadth, not exhaustive single-source depth. |
| Clay | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable from Clay via its HTTP step. |
| BuiltWith | From $295/month | Historic tech data going back years, plus lead lists filtered by technology | If you need "every site that added Shopify last quarter", that's a database question and BuiltWith answers it. This answers "what is this one domain running, right now". |
| Doing it yourself | Your time + seven endpoints to integrate, rate-limit, and keep working as RDAP registries, ATS APIs, and CT log providers change their responses | The same data | This actor absorbs the maintenance: seven fetchers, retry/timeout handling, and a stable field shape that doesn't break when one upstream API changes its response format. |
Prices for third-party tools are their published list prices as of August 2026 and are not tracked here — check the vendor before relying on the comparison.
FAQ
What happens if a domain doesn't match any of the seven sources?
You still get a row (found: false), but nothing is billed — Actor.charge() only fires in the
branch where a source actually returns data, so a list full of parked domains or typos costs you
next to nothing.
Do I need API keys or proxies for any of the seven sources?
No. RDAP, Cloudflare DoH, the domain's own homepage, the six ATS platforms' public job-board
APIs, and public Certificate Transparency logs are all free and public. This actor also runs
with noProxy: true — none of the seven checks needs a residential or datacenter proxy.
Are there rate limits or a concurrency I should worry about?
maxConcurrency controls how many domains are processed in parallel (default 5) — raise it for
a faster run against a large list, or lower it if you're seeing timeouts from a slow upstream
source. There's no fixed per-run cap on domain count.
Is any of this data cached or stored between runs? No. Every field is fetched live at run time from its source — RDAP, DNS, the domain's current homepage, current CT log state, each ATS platform's current board, current MX records. Run the same domain twice on different days and you may get different results (a new TLS cert, an updated hiring count) because nothing is cached.
Is this GDPR-relevant, and is it safe to run against EU company domains?
The data returned is public registration (RDAP), public DNS, and public job postings — sources
that are, by design, published for public lookup. This actor doesn't return personal data about
individuals; registrantOrg reflects a company's registrant name when the registry doesn't
redact it. You're still responsible for how you use enriched company data downstream.
Can I run this on a schedule to monitor a list of domains?
Yes — set up an Apify schedule against this actor with the same domains input and a cadence
that fits your use case (weekly is common for hiring-signal or TLS-renewal monitoring). There's
no built-in delta tracking between runs; each run returns the current state, not what changed
since the last one.
Can an AI agent call this directly?
Yes — it's built for that. Any MCP client with the Apify MCP server can find and run this actor
by name ("Company Domain Enrichment API — RDAP, DNS, Tech Stack, TLS"), or you can call the
run-sync-get-dataset-items HTTP endpoint directly, as shown above.
What's hiringForGroups actually for?
It replaces the five default job families the hiring source reports on (sales, engineering,
marketing, customer-success, executive) with whatever segments you sell into — one line per
family as name: keyword, keyword. If you sell to procurement teams, for instance, you could add
a procurement: procurement, sourcing, supply chain line and every row's hiring.hiringFor will
report a count for it.
Related actors
- Tech Stack Lookup — the full signature catalog (30+ technologies) if this actor's focused 10-signature subset isn't enough detail on its own.
- Company Registry Unified Lookup — for company identifiers instead of domains: VAT numbers, LEI codes, and national company registries (VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH).
- Email Deliverability Check —
the standalone version of this actor's
emailRoutesource, with the same MX/provider fingerprint and disposable-domain check, run on its own.