Company Enrichment API - Domain to Firmographics, Tech & Email avatar

Company Enrichment API - Domain to Firmographics, Tech & Email

Pricing

from $8.00 / 1,000 company enricheds

Go to Apify Store
Company Enrichment API - Domain to Firmographics, Tech & Email

Company Enrichment API - Domain to Firmographics, Tech & Email

One domain in, one flat JSON company profile out: company name, address, emails, phones, socials, full tech stack, the back-office SaaS a company runs read from DNS, and a graded MX/SPF/DMARC/DKIM deliverability verdict. Flat price per domain, no run minimum, no free-tier penalty. Built for agents.

Pricing

from $8.00 / 1,000 company enricheds

Rating

5.0

(3)

Developer

Santhej Kallada

Santhej Kallada

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Company Enrichment API — Domain to Firmographics, Tech & Email

Give it a domain. Get one flat JSON company profile back: company name and address, emails, phones, social profiles, the full tech stack, the back-office SaaS the company runs read straight from its DNS, and a graded MX / SPF / DMARC / DKIM deliverability verdict.

No other actor returns all six of those in one call. Today you stitch that together from a firmographics actor, a contact scraper, a tech-stack detector and a DNS/email checker — four calls, four schemas, four failure modes, four bills. This is one call, one flat row, one price.

  • One required field. {"domains":["stripe.com"]} is a complete, correct call.
  • The key you already have. A domain — not a LinkedIn company URL, not a company name you have to resolve first.
  • Flat price per domain, no run minimum. $0.008 per company enriched. A one-domain lookup costs $0.00805, not a $0.50 run floor.
  • Same price on every plan tier. Free-tier gouging is standard in this category; there is none here.
  • No API keys, no vendor accounts, no proxy setup. Every signal comes from the company's own DNS, its own website, and public unauthenticated job boards.
  • Deterministic. No LLM in the path. Same domain, same day, same JSON.

What you get per domain

Firmographics

Company name (with a company_name_source so you can weight how it was derived), legal entity name, description, logo, country, street address, city, region, postal code, VAT ID, founded year, primary language.

Contacts and socials

Role email addresses on the company's own domain, phone numbers, and up to eight social profiles — LinkedIn, X/Twitter, Facebook, Instagram, YouTube, GitHub (a strong developer-tooling buying signal), TikTok and Crunchbase.

Tech stack

A deduplicated technology list with categories, plus first-class verdicts for cms, ecommerce_platform, analytics, cdn, web_server and is_wordpress. Fingerprints are matched only against tags and script bodies, never visible page text — so a marketing page that mentions a competitor's product does not get tagged as running it.

Fingerprint definitions come from the open-source Wappalyzer technology dataset (v6.10.54), used under the MIT licence; the licence text and notice ship inside the actor image. The matching engine, the code-surface restriction that kills the false positives, and the category verdicts above are this actor's own.

Back-office SaaS from DNS — the signal a tech-stack scanner structurally cannot see

Website fingerprinting only ever observes the marketing site's frontend. Domain-verification records in DNS expose what the company bought for its back office: Microsoft 365, Atlassian, Slack, Zoom, Docusign, Miro, Adobe. Those land in paid_saas_vendors. Free-product tokens (Search Console, Meta Business, Canva, Apple) are kept separate in free_verification_tokens — calling a free tool a purchase would inflate the number, so we don't.

Graded email infrastructure, not raw records

Most actors dump raw DNS strings, or return has_spf: true and stop. This returns the interpreted verdict: the mail provider derived from MX, SPF strictness (-all hard fail vs ~all soft fail vs the +all misconfiguration), the DMARC policy (none / quarantine / reject), DKIM via provider-aware selector probing, BIMI, and a deterministic A–F email_security_grade from a published rubric — one field to filter a list on.

The grade is null in exactly one situation: a DNS lookup failed, so the evidence was never obtainable. A resolver timeout and a domain that publishes no SPF at all produce the same empty answer, and a grade of F on the first would be a confident, false claim about a real company's security posture. When the records were read, the grade is a letter; when they could not be read, it is null and the caps that would have punished the "missing" records are suppressed. null means we could not establish this, never unprotected.

email_marketing_tools decodes the SPF chain into the martech the company actually sends through: HubSpot, SendGrid, Klaviyo, Marketo, Mailchimp, Zendesk, Salesforce, Postmark, Amazon SES, Brevo, Intercom, Customer.io.

Company size — a band, with a confidence

company_size_signal is smb / mid_market / growth / enterprise, and it is null when the evidence cannot narrow the range — an unknown band beats a wrong one. A verified open-role count decides the band on its own, as a floor on implied headcount; DNS-visible SaaS vendor counts and sitemap size are only precise enough to name the bottom band, never a middle one. company_size_confidence is high only when the role count is not sitting near a band threshold.


What this actor does not return, and why

No employee_count. No revenue. No funding. No industry_code.

Companies do not publish headcount in machine-readable markup — it appeared in 0 of 14 sampled sites carrying structured company data. Every product that shows you a precise employee number for an arbitrary domain either bought it from a data broker or guessed. Guessing is how this category generates refund requests, so we ship a band and a confidence instead of a fabricated number.

If you need firmographic headcount and revenue, pair this with a LinkedIn company actor. This one tells you the truth about what it can see.


Hiring signals — read this before you rely on them

Hiring is shipped, and it is genuinely useful when it lands. It is not a headline feature, and the numbers are why.

The actor identifies the applicant tracking system a company uses and, when you enable include_open_roles, returns the open-role count, the departments hiring and a ten-role sample.

On a mixed 30-domain list of the kind a real buyer uploads — local services, DTC brands, mid-market SaaS, ecommerce — a job board was found for about 30% of domains. After the verification guard runs, usable hiring data lands on roughly 13%. The rate is much higher on tech and DTC companies and near zero on local service businesses.

The verification guard is the reason for that gap, and it is deliberate. Job-board identifiers are guessable, and a guess can land on a completely unrelated company's board — we hit exactly that live, where one company's identifier returned a board belonging to an unrelated business with a similar name. So:

  • ats_verified is true only when the board was positively tied to this company.
  • ats_source tells you exactly how: an on-page marker, the mail-sending chain, a board name match, or a job link on the company's own domain. slug_guess_unverified means we found a board and could not confirm it is theirs.
  • When ats_verified is false, open_roles_count and is_hiring are null, not a number from someone else's board.

A null you can trust beats a number you can't. include_open_roles defaults to false for that reason, and because it removes the largest download from the request path.

Fill rates quoted in this README were measured on samples of tens of domains, not thousands. Treat them as honest order-of-magnitude guidance, not guarantees.


Pricing

OutcomestatusCharged
Website and DNS both deliveredok$0.008company-enriched
Website blocked or broken, DNS layer deliveredpartial_dns_only$0.004 — half price
Domain does not resolveunreachableFree
Input could not be parsed as a domaininvalid_inputFree
Blocked site rescued via the opt-in residential retry+$0.02, only when the retry returns usable HTML
Run start$0.00005 once per run

Why blocked domains still cost something, and why it is half. When a website blocks us, DNS still answers — in probing, DNS returned answers for every domain tested, including all three that hard-blocked their website. You still get the mail provider, SPF policy, DMARC policy, DKIM, BIMI, the graded verdict and the paid-SaaS vendor list. That is still the most complete result available for a blocked domain. But there are no emails, no socials, no tech stack and only a domain-derived company name — so full price would be opportunistic. Half price is the honest answer.

No run minimum. No start-fee trap. No free-tier surcharge. Your cost is knowable before you call: len(domains) × $0.008 + $0.00005, worst case. Blocked domains cost less. Junk domains cost nothing.

Duplicates and subdomains that collapse to the same apex are deduplicated before anything is fetched, so passing stripe.com, www.stripe.com and https://stripe.com/pricing bills once.


Example input

Minimum viable call:

{ "domains": ["stripe.com"] }

Everything turned up:

{
"domains": ["stripe.com", "www.notion.so", "https://sennheiser.de/about"],
"verbosity": "full",
"max_pages": 4,
"include_tech_stack": true,
"include_open_roles": true,
"role_emails_only": true,
"residential_fallback": true,
"max_concurrency": 8
}

Example output

One row per domain, every key present on every row — null where not applicable, [] (never null) for arrays. Index record["dmarc_policy"] without a defensive check.

{
"domain": "stripe.com",
"status": "ok",
"error": null,
"final_url": "https://stripe.com/",
"fetched_at": "2026-08-14T09:21:44.118Z",
"company_name": "Stripe",
"company_name_source": "json_ld",
"legal_name": "Stripe, Inc.",
"description": "Stripe is a suite of APIs powering online payment processing and commerce solutions for internet businesses of all sizes.",
"logo_url": "https://images.stripeassets.com/fzn2n1nzq965/HTTOloNPhisV9P4hlMPNA/cacf1bb88b9fc492dfad34378d844280/Stripe_icon_-_square.svg",
"country_code": "US",
"street_address": "354 Oyster Point Boulevard",
"city": "South San Francisco",
"region": "CA",
"postal_code": "94080",
"vat_id": null,
"emails": ["press@stripe.com", "support@stripe.com"],
"phones": ["+1 888-926-2289"],
"linkedin_url": "https://www.linkedin.com/company/stripe",
"twitter_url": "https://twitter.com/stripe",
"facebook_url": null,
"instagram_url": null,
"youtube_url": "https://www.youtube.com/stripe",
"github_url": "https://github.com/stripe",
"tiktok_url": null,
"crunchbase_url": null,
"social_profile_count": 4,
"ats_vendor": "Greenhouse",
"ats_verified": true,
"ats_source": "spf_include",
"open_roles_count": null,
"is_hiring": null,
"hiring_departments": [],
"technologies": ["Cloudflare", "Contentful", "HSTS", "Next.js", "React", "Segment", "Stripe.js"],
"technology_count": 7,
"tech_categories": ["CDN", "CMS", "JavaScript Frameworks", "Payment Processor", "Analytics", "Security"],
"cms": "Contentful",
"ecommerce_platform": null,
"analytics": ["Segment"],
"cdn": "Cloudflare",
"web_server": null,
"is_wordpress": false,
"paid_saas_vendors": ["Atlassian", "Docusign", "Microsoft 365", "Slack"],
"saas_vendor_count": 4,
"mx_provider": "Google Workspace",
"spf_present": true,
"spf_policy": "-all",
"email_marketing_tools": ["Salesforce", "SendGrid"],
"dmarc_present": true,
"dmarc_policy": "reject",
"dkim_present": true,
"bimi_present": false,
"email_security_grade": "A",
"company_size_signal": "enterprise",
"company_size_confidence": "high",
"has_blog": true,
"has_pricing_page": true,
"has_security_page": true,
"used_residential": false,
"signals_found": 4,
"data_completeness": 0.81
}

A domain whose website blocks us still returns the full DNS layer (abbreviated below — the row still carries all 61 core keys, the website-derived ones empty):

{
"domain": "example-blocked.com",
"status": "partial_dns_only",
"error": "HTTP_BLOCKED",
"company_name": "Example Blocked",
"company_name_source": "domain_root",
"emails": [],
"technologies": [],
"mx_provider": "Microsoft 365",
"spf_present": true,
"spf_policy": "~all",
"dmarc_present": true,
"dmarc_policy": "quarantine",
"dkim_present": true,
"bimi_present": false,
"email_security_grade": "C",
"paid_saas_vendors": ["Microsoft 365", "Zoom"],
"signals_found": 2,
"data_completeness": 0.44
}

Core vs full fields

verbosity defaults to "core" (61 fields). Core drops the raw records that duplicate a verdict already interpreted for you — there is no point handing an agent both spf_policy: "-all" and the raw SPF string. Set "verbosity": "full" for all 85 fields when a human is reading the output or you want the raw evidence.

Core (61) — returned by default

GroupFields
Identity & statusdomain, status, error, final_url, fetched_at
Firmographicscompany_name, company_name_source, legal_name, description, logo_url, country_code, street_address, city, region, postal_code, vat_id
Contacts & socialsemails, phones, linkedin_url, twitter_url, facebook_url, instagram_url, youtube_url, github_url, tiktok_url, crunchbase_url, social_profile_count
Hiringats_vendor, ats_verified, ats_source, open_roles_count, is_hiring, hiring_departments
Tech stacktechnologies, technology_count, tech_categories, cms, ecommerce_platform, analytics, cdn, web_server, is_wordpress
SaaS from DNSpaid_saas_vendors, saas_vendor_count
Email infrastructuremx_provider, spf_present, spf_policy, email_marketing_tools, dmarc_present, dmarc_policy, dkim_present, bimi_present, email_security_grade
Signalscompany_size_signal, company_size_confidence, has_blog, has_pricing_page, has_security_page, used_residential, signals_found, data_completeness

Full adds 24 more

GroupFields
Identity & statushttp_status, input_value
Firmographicsfounded_year, primary_language, latitude, longitude
Contactscontact_page_url, about_page_url
Hiringats_board_url, open_roles_sample, open_roles_truncated
Tech stacktech_scan_complete, free_verification_tokens
Email infrastructuremx_hosts, spf_record, spf_includes, dmarc_pct, dmarc_rua, dkim_selectors_found
Diagnosticssecurity_txt_present, sitemap_url_count, page_title, canonical_url, pages_fetched

open_roles_sample is the only nested structure in the record — up to ten {title, department, location, is_remote, url} objects — which is exactly why it is full-only and the rest of the record stays strictly flat.


Judging a row without a second call

Four fields answer "should I trust this?":

  • status — four values, the top-level branch: ok, partial_dns_only, unreachable, invalid_input.
  • data_completeness — 0–1, scored against the fields that were attainable for that row given its status and your input flags, so a perfectly good record does not score 0.3 just because the company has no street address in its markup.
  • signals_found — 0–5: company, contacts + socials, tech stack, DNS/email, hiring.
  • ats_verified + ats_source — exactly how the hiring claim was derived, or that it wasn't.

error carries the machine code when something went wrong: INVALID_DOMAIN, DNS_NXDOMAIN, HTTP_BLOCKED, HTTP_ERROR, TIMEOUT, TLS_ERROR, EMPTY_SHELL.


Built for agents and pipelines

  • One required input, eight total. Nothing to configure to get a correct call.
  • Flat output — no nesting to traverse at core verbosity, so an LLM reads it straight.
  • Cost is computable before the call, which matters when an agent is under a budget.
  • Failures degrade instead of throwing: a blocked website still returns the DNS layer, a dead domain returns a clean unreachable row and costs nothing.
  • Works from n8n, Make, Zapier, MCP tool calls, or the Apify API directly.

Typical uses: enriching a raw domain list into a CRM, qualifying inbound signups from their email domain, deliverability and email-security prospecting, tech-stack-based targeting, and giving a sales agent everything it needs about an account in one tool call.


Data use, GDPR and responsible outreach

Read this before you feed the output into cold outreach.

What we collect and don't.

  • Email addresses are returned only from the company's own domain. Addresses on any other domain are dropped unconditionally — this is not a toggle. Measured on a single site, /about and /privacy pages leaked 13 unrelated third-party addresses; those never reach your output.
  • role_emails_only defaults to on, restricting output to generic role accounts (info@, sales@, support@, and similar) rather than named individuals.
  • Team, staff, people and leadership pages are never crawled. They are the highest-yield source of named natural persons, and we do not go there.
  • For .de / .at / .ch domains the statutory Impressum is read for legal entity name, address and VAT ID only. The managing director's name is deliberately not extracted, even though the page publishes it by law.
  • Only publicly published, unauthenticated pages are read. Nothing is logged into, no paywall is bypassed, and no personal data is purchased from a broker.

Your obligations. Data returned here is business contact information, but you are the controller once you use it. Under GDPR you need a lawful basis and must honour access and erasure requests. Germany's UWG §7 makes unsolicited commercial email to businesses unlawful without prior consent — unlike the US CAN-SPAM regime, opt-out is not enough there, and several other EU jurisdictions are similarly strict. Check the rules for the recipient's country, not yours. Turning role_emails_only off is available for legitimate uses such as security research and deliverability auditing; if you turn it off for cold outreach, that is your call and your liability.

We do not resell, retain or index anything this actor fetches. Output lives in your own dataset.


FAQ

What do I pass in? A domain. stripe.com, www.stripe.com, https://stripe.com/pricing and hello@stripe.com-style hosts all normalise to the same apex.

Do I need proxies or API keys? No. Both are optional-to-nonexistent: there are no vendor credentials anywhere, and residential proxy use is a single opt-in retry for blocked sites.

How fast is it? Roughly 15–17 minutes per 1,000 domains at the default memory allocation, where the container gets a quarter of a CPU core. A single-domain call returns in a few seconds. Turning off include_tech_stack is the biggest latency lever.

Why is a field null? Because the company does not publish it. The cascade for company_name bottoms out at the domain root, so that one is effectively always filled; address, VAT ID, founded year and coordinates depend on structured markup most sites do not carry. One field is null for a different reason: email_security_grade is null when a DNS lookup failed, because a grade computed on records we could not read would be a guess dressed as a finding.

Can I get more than 5,000 domains? Split them across runs. Per-run cap is 5,000.

Is the output stable? Yes — no model in the path, and the key set never changes for a given verbosity.


Tags: company enrichment, domain enrichment, firmographics, B2B data, lead enrichment, tech stack detection, technology lookup, email verification, SPF DMARC DKIM, deliverability, MX lookup, sales intelligence, prospecting, AI agents, MCP.