Company Enrichment API — Domain to Company Profile avatar

Company Enrichment API — Domain to Company Profile

Pricing

from $2.10 / 1,000 company enricheds

Go to Apify Store
Company Enrichment API — Domain to Company Profile

Company Enrichment API — Domain to Company Profile

Give it domains, URLs or email addresses and get one company profile per domain, read from the company's own website and its own DNS: name, legal name, HQ, founded year, logo, socials, role-based emails and phones, technology stack, mail/DNS/registrar/TLS and the ATS it hires through. No API key.

Pricing

from $2.10 / 1,000 company enricheds

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Send a list of domains, URLs or email addresses. Get one company profile per domain — read from the company's own website and its own DNS, never from a third party's database.

That sentence has a corollary, and it is the reason to use this Actor rather than one that sounds more confident: anything the company does not publish comes back null, never a guess. No inferred headcount, no revenue band, no guessed mailbox pattern, no name scraped off a team page. Every field that is filled carries the rule and the page URL it came from, in a fieldSources column you can audit row by row.

Five column groups on every row:

  1. Identity and firmographics — name, legal name, description, tagline, logo, HQ and every office address the site publishes, founded year, employee band, industry, languages, company type.
  2. Published contacts — role-based emails, phones, social profiles, the contact/imprint/privacy/press page URLs, contact-form presence, VAT ID, security.txt.
  3. Technology stack — 232 signatures over the pages already fetched: CMS, e-commerce platform, JS framework, analytics, ad pixels, chat widget, payment providers, CDN, hosting, security vendor.
  4. Infrastructure — mail provider from MX, DNS operator from NS, SPF and domain-verification TXT records, registrar and domain age from RDAP, TLS issuer and expiry. This is the part of the row that never fails to render: a company that publishes nothing about itself still has a mail provider, a registrar and a certificate.
  5. Hiring — the applicant tracking system the company recruits through, its board slug, and the open-role count.

$3.50 per 1,000 companies. Free-mail domains, dead domains, bot walls, robots.txt refusals, sites that published nothing nameable and domains the run never reached are all free.

At a glance

Input — this is the Store prefill; paste it and run:

{
"domains": ["stripe.com", "basecamp.com", "apify.com"],
"includeContacts": true,
"includeTechStack": true,
"includeInfrastructure": true,
"includeHiring": true,
"maxPagesPerSite": 6
}

Output — one row per domain; the fields you will use most are companyName, primaryEmail, technologyNames, emailProvider and atsPlatform (full list under Output reference). Anything that could not be fetched comes back as a free diagnostic row (ok: false, errorType, error) instead of a charge.

Price — $3.50 per 1,000 companies (+ $0.001 per run); volume tiers are SILVER $2.80 and GOLD $2.10 per 1,000, and free-mail domains, dead domains, bot walls, robots.txt refusals, sites that published nothing nameable and domains the run never reached cost nothing. Pay-per-event, no API key, no browser, limited permissions — works over the Apify MCP server (mcp.apify.com) and with agentic (x402) payments.

From codeclient.actor("insight.solutions/company-enrichment-api").call(run_input={…}) with apify-client, or POST https://api.apify.com/v2/acts/insight.solutions~company-enrichment-api/run-sync-get-dataset-items.


What you get

One row per input domain. This is apify.com from the offline test suite, trimmed to the interesting columns:

{
"ok": true,
"rowType": "company",
"input": "apify.com",
"domain": "apify.com",
"resolvedDomain": "apify.com",
"websiteUrl": "https://apify.com/",
"companyName": "Apify",
"legalName": "Apify Technologies s.r.o.",
"companyType": "Czech company",
"description": "Thousands of tools to automate your business. Get real-time web data, track competitors, generate leads, and integrate your apps and AI agents.",
"tagline": "68,948 tools for your AI",
"logoUrl": "https://apify.com/img/apify-logo/apify-symbol-200x200.svg",
"foundedYear": 2015,
"foundedYearEvidence": "jsonld.Organization.foundingDate",
"employeeCount": null,
"employeeBand": null,
"industry": "Web data and scraping",
"industrySource": "keyword-table",
"industryEvidence": "keyword: web data",
"emails": [
{ "address": "support@apify.com", "type": "role", "foundOn": "https://apify.com/" },
{ "address": "hello@apify.com", "type": "role", "foundOn": "https://apify.com/contact" }
],
"primaryEmail": "support@apify.com",
"phones": null,
"linkedinUrl": "https://linkedin.com/company/apify",
"socials": {
"linkedin": "https://linkedin.com/company/apify",
"twitter": "https://twitter.com/apify",
"github": "https://github.com/apify",
"tiktok": "https://tiktok.com/@apifytech",
"discord": "https://discord.com/invite/jyEM2PRvMU",
"g2": "https://g2.com/products/apify",
"glassdoor": "https://glassdoor.com/Overview/Working-at-Apify-EI_IE3100324.11,16.htm",
"wikipedia": "https://wikipedia.org/wiki/Apify",
"wikidata": "https://wikidata.org/wiki/Q112502991",
"facebook": null, "instagram": null, "youtube": null, "bluesky": null,
"threads": null, "reddit": null, "crunchbase": null, "producthunt": null
},
"hqAddress": "Na Příkopě 959/27, Prague, 11000, CZ",
"hqCity": "Prague",
"hqCountry": "CZ",
"country": "CZ",
"vatId": "CZ04788290",
"contactPageUrl": "https://apify.com/contact",
"careersUrl": "https://apify.com/jobs",
"docsUrl": "https://docs.apify.com/",
"technologyNames": ["Next.js", "React", "Node.js", "Google Tag Manager", "HubSpot", "Intercom", "OneTrust", "Nginx"],
"jsFramework": "Next.js",
"analytics": ["Google Tag Manager"],
"hasCookieBanner": true,
"emailProvider": "Google Workspace",
"acceptsEmail": true,
"dnsProvider": "Amazon Route 53",
"spfIncludes": ["_spf.google.com", "mailgun.org", "amazonses.com"],
"verifications": ["Google", "Zapier", "OpenAI", "Mixpanel"],
"registrar": "Amazon Registrar, Inc.",
"domainCreatedAt": "2009-06-02T17:14:10.000Z",
"domainAgeYears": 17.3,
"tlsIssuer": "Amazon — Amazon RSA 2048 M02",
"hsts": false,
"atsPlatform": "ashby",
"atsBoardSlug": "apify",
"atsBoardUrl": "https://jobs.ashbyhq.com/apify",
"openJobs": 17,
"hiringSignal": true,
"pagesFetched": [
{ "url": "https://apify.com/", "status": 200, "role": "home", "bytes": 491881 },
{ "url": "https://apify.com/contact", "status": 200, "role": "contact", "bytes": 332264 },
{ "url": "https://apify.com/about", "status": 200, "role": "about", "bytes": 482783 },
{ "url": "https://apify.com/jobs", "status": 200, "role": "careers", "bytes": 425302 }
],
"sources": ["jsonld", "opengraph", "footer", "text", "tech", "dns", "rdap", "tls", "ats", "links"],
"fieldSources": {
"companyName": { "rule": "jsonld.Organization.name", "url": "https://apify.com/" },
"legalName": { "rule": "jsonld.Organization.legalName", "url": "https://apify.com/" },
"foundedYear": { "rule": "jsonld.Organization.foundingDate", "url": "https://apify.com/" },
"emailProvider":{ "rule": "dns.MX", "url": "https://dns.google/resolve?name=apify.com&type=MX" },
"atsPlatform": { "rule": "ats.marker", "url": "https://apify.com/jobs" }
},
"confidence": 0.94,
"partial": false,
"scrapedAt": "2026-09-15T04:12:00.000Z"
}

Note employeeCount: null. Apify's home page says "more than 15,000 members" — a community, for a company of about a hundred people. A tool that filled the column from that sentence would be wrong by two orders of magnitude, so this one leaves it empty. That is the whole design in one field.


Quick start

  1. Paste your domains into Domains. A bare domain, a full URL or an email address all work.
  2. Run it.
  3. Export the dataset as JSON, CSV or Excel, or read it from the API.

The defaults are the right defaults: six pages per site, all five column groups on, five companies in parallel, the Apify datacenter proxy.


Input

FieldTypeDefaultWhat it does
domainsarray(required)One entry per company: stripe.com, https://www.stripe.com/about or sales@stripe.com. The registrable domain identifies the company, so the same company written three ways is fetched and billed once
includeContactsbooleantrueEmails, phones, socials, page URLs, VAT, security.txt
includeTechStackbooleantrue232 signatures over pages already fetched — CPU, not requests
includeInfrastructurebooleantrueDNS (MX, NS, TXT, A), RDAP, TLS
includeHiringbooleantrueATS detection is free; the open-role count costs one request
domainstringA single-entry convenience for integrations that cannot send an array
maxPagesPerSiteinteger 1–126HTML pages per company, home page included. robots.txt is outside it; security.txt gets one extra allowance
includePersonalEmailsbooleanfalseRead the Privacy section first
contactPathHintsarray[]Extra paths to prefer during discovery, e.g. ["/impressum", "/unternehmen"]
minConfidencenumber 0–10Rows below this become free diagnostics and are not charged
maxConcurrencyinteger 1–205Companies in flight; each gets its own proxy session
maxRunSecsinteger 30–3600240Wall-clock budget. Domains not reached are never charged
proxyConfigurationobject{ "useApifyProxy": true }Apify datacenter proxy

None of the include* toggles changes the price. A row is a row.


Output reference

Every row carries every column, null where it does not apply, so a mixed export of live sites, dead domains and free-mail addresses is still one rectangular table. rowType is company or diagnostic.

Identity

domain · resolvedDomain · redirectedToDifferentDomain · websiteUrl · canonicalUrl · companyName · legalName · legalNameEvidence · description · tagline · taglineSource · logoUrl · faviconUrl · companyType · copyrightYear · copyrightStartYear

resolvedDomain is not decoration. Ask for notion.so and the site answers at www.notion.com; every subsequent decision uses the resolved domain, because guessing paths on the domain you typed would 404 on every one.

copyrightStartYear is never promoted to foundedYear. Moz's footer says 2021 and Moz was founded in 2004.

Firmographics

foundedYear · foundedYearEvidence · employeeCount · employeeBand · employeesEvidence · industry · industryEvidence · industrySource

The three *Evidence columns exist because these are the three fields most worth distrusting. Read the evidence before you act on the number.

A founding year read from prose has to survive three tests: the sentence must not be about a city, a university, a church or a building (all of which get founded in years); it must either name the company or begin "We", "Founded" or "Established"; and the year must be between 1800 and today. The first live run filed one company as founded in 1979 on the strength of a sentence about the town its office is in — hence the rules, and hence the evidence column.

Contacts and location

emails · emailsCount · primaryEmail · phones · phoneEvidence · socials · linkedinUrl · contactPageUrl · contactPageConfirmed · hasContactForm · newsletterSignup · hasLoginPortal · isEcommerce · appLinks · vatId · imprintUrl · privacyUrl · termsUrl · aboutUrl · pressUrl · blogUrl · docsUrl · statusUrl · supportUrl · teamUrl · securityTxtUrl · securityContact · securityPolicyUrl · securityTxtExpires · hqAddress · hqCity · hqRegion · hqPostalCode · hqCountry · hqEvidence · officeLocations · country · languages

Every *Url column is linked, not guessed. A subdomain counts: press.stripe.com, docs.apify.com, legal.hubspot.com are all real answers this Actor finds and a same-host-only crawler misses.

A different domain does not count. When a link leaves the company's own registrable domain — basecamp.com/privacy answering at 37signals.com/policies/privacy — the URL column still holds the link that was followed and pagesFetched still records where it landed, but nothing that page says about itself becomes a fact about this company: not its name, not its description, not its structured data, and not its email addresses. A home page that redirects across a domain boundary is the opposite case and is recorded as redirectedToDifferentDomain, because there the destination is the company.

officeLocations carries every distinct postal address in the site's structured data, capped at 25. Stripe publishes 24 — and marks none of them as its headquarters, which is why hqAddress for stripe.com is null and hqEvidence reads ambiguous: 24 office locations. An HQ is Organization.address, or a location that calls itself the headquarters, or a company's single office. Otherwise there is no HQ column, because picking the first of twenty-four addresses is picking one at random.

country then falls back through its own ladder — the HQ's country, the ccTLD, the phone's calling code, og:locale, the site's own hreflang set, <html lang>, and last the legal form of the company's name — and countryEvidence names the rung that answered, so an inference never looks like a fact.

Technology

technologies · technologyNames · technologyCategories · cms · ecommercePlatform · jsFramework · hosting · analytics · advertising · cdn · security · chatWidget · paymentProviders · hasCookieBanner · externalDomains

Detection reads only pages already fetched, so it costs no extra requests. It is merged from pages on the same host as the entry page: a company's legal or docs subdomain is routinely a different stack, and stamping it on the row would be wrong.

Infrastructure

emailProvider · mxRecords · acceptsEmail · dnsProvider · nameservers · spf · spfIncludes · verifications · registrar · registrarIanaId · domainCreatedAt · domainExpiresAt · domainAgeYears · dnssec · registrationStatus · registrationNote · tlsIssuer · tlsValidTo · tlsDaysUntilExpiry · tlsSans · httpStatus · server · hsts · cdnHint

verifications is the cheapest "what does this company actually use" signal there is: a company publishes zapier-domain-verification-challenge=… only because somebody there connected Zapier to their domain.

Hiring

careersUrl · atsPlatform · atsPlatformLabel · atsBoardSlug · atsBoardUrl · openJobs · hiringSignal

Eleven platforms are detected: Greenhouse, Lever, Ashby, Workable, Recruitee, SmartRecruiters, Personio, Teamtailor, Breezy, JazzHR and Workday. Nine of them also return openJobs. Workday is named but not counted, because its list needs a POST with paging and this Actor will not hold a second protocol for one number; JazzHR publishes no public list endpoint at all.

Provenance

pagesFetched · pagesFetchedCount · sources · fieldSources · confidence · partial

confidence is

0.60 × weighted field coverage + 0.25 × best identity source + 0.15 × pages that answered
. It measures how much the company published, not whether the values are true. A field whose column group you switched off is removed from the denominator rather than counted as missing, so turning a group off never makes the same row look worse.

Diagnostic rows, all free

invalid-input · freemail · not-found · unreachable · blocked · robots-disallowed · not-html · no-data · low-confidence · timeout · budget-stopped


What fills, and what usually does not

Six real company websites were captured to build and test this Actor — stripe.com, notion.so, basecamp.com, apify.com, moz.com, hubspot.com. Here is the coverage, as numbers rather than adjectives:

FieldFills on
companyName6 / 6
description6 / 6
logoUrl6 / 6
legalName6 / 6
country6 / 6
social profiles4 / 6
HQ address3 / 6
foundedYear3 / 6
a role-based email2 / 6
a phone number1 / 6
an ATS1 / 6
employeeCount0 / 6

Three of those deserve saying out loud.

employeeCount is empty on all six. It is a headline field on several competing listings. Real company websites mostly do not publish a headcount, and the ones that publish a number usually publish a community or customer count that a careless parser turns into one. Ours returns null and tells you why.

Social recall is 4 in 6 on well-known companies. Notion and Basecamp render zero social links in their server HTML — not a parsing failure, there is genuinely nothing there. If socials are the reason you are buying, budget for about two thirds.

Phones are 1 in 6. Across six sites and thirteen captured pages there is not one tel: link and not one structured telephone value. The only number anywhere is Stripe's footer line, and it survives because it carries its own country code. See Limitations for the rule.

An HQ address fills on 3 of 6, not 4. One of the six publishes two dozen office addresses and says which is the head office for none of them. country still fills for it, and every address is still in officeLocations — but the hqAddress column stays empty rather than naming whichever one happened to be first.


What you are never charged for

  • a free-mail domain (gmail.com, qq.com, web.de — about 140 of them)
  • a domain that does not exist, or that resolves and serves nothing
  • a site behind a bot wall that revealed nothing
  • a site whose robots.txt asks us not to read its home page
  • an IP address, a bare word, or anything else that is not a domain
  • a site that answered and published nothing this Actor could name a company from
  • a row scoring below the minConfidence you set
  • domains the run never reached, because of your time budget or your spend cap
  • every run that returns nothing at all — it finishes FAILED and bills nothing, start fee included

Pricing

EventWhat it isPrice
companyOne company profile returned$0.0035 — $3.50 per 1,000
actor-startCharged once per run, and only after the first profile is in the dataset$0.001

Volume tiers: SILVER $0.0028, GOLD $0.0021 per profile.

Charging is charge-after-push, always: the row is in your dataset before the charge is recorded. ACTOR_MAX_TOTAL_CHARGE_USD is respected proactively — when the remaining budget cannot cover another profile the run stops fetching, finishes SUCCEEDED and says so in the status message.


Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

curl -X POST "https://api.apify.com/v2/acts/insight.solutions~company-enrichment-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domains":["stripe.com","basecamp.com","apify.com"],"maxPagesPerSite":6}'
# pip install apify-client
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/company-enrichment-api").call(run_input={
"domains": ["stripe.com", "sales@notion.so", "https://www.hubspot.com/"],
"maxPagesPerSite": 6,
"includeInfrastructure": True,
"minConfidence": 0.5,
"maxConcurrency": 5,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row.get("ok") and row["rowType"] == "company":
print(row["companyName"], row["country"], row["industry"],
row["emailProvider"], row["primaryEmail"], row["confidence"],
sep=" | ")
else:
print("skipped", row["input"], row["errorType"], row["error"], sep=" | ")

Set minConfidence when you would rather have a short, dense list than a long one: rows below it arrive in full as free diagnostics, so you can still see what was rejected without paying for it.


FAQ

Where does the data come from? The company's own website — its home page and up to five pages it links to — and its own DNS, RDAP registration and TLS certificate. No third-party enrichment database, no data broker, no cached profile of unknown age. If the company changed its address yesterday, this Actor reads yesterday's address.

Why is a field empty when another tool fills it? Because the company did not publish it. This Actor will not invent a headcount from an office count, a revenue band from a headcount, or an email address from a name pattern. An empty column is information; a fabricated one is a liability.

Why is there no revenue column? A private company's revenue is not something its website publishes. A band derived from a guessed employee count is a guess squared, and it would be the least reliable column in the dataset while looking like the most valuable one. It is absent on purpose.

Why no CEO or founder name? See Privacy. This Actor is company-level only, by design.

What does confidence actually measure? Coverage and source quality — how much of the profile the company published and how good the best source was. It says nothing about whether a value is correct. For that, read fieldSources and the *Evidence columns.

Can I send email addresses instead of domains? Yes. sales@acme.com becomes acme.com. The address itself is discarded and never appears in the output. A consumer mail domain gets a free diagnostic instead, because gmail.com is not a company.

How many requests does one company cost me? At the defaults: one robots.txt, up to six pages, one security.txt, four DNS lookups, one RDAP call, one TLS socket and at most one ATS board fetch. Fourteen HTTP requests worst case, and you pay one flat price whatever that comes to.

Does it re-fetch a domain I already ran? Every run is fresh. There is no cache, because a cached company profile is the thing this Actor exists as an alternative to.


Limitations

  • Phone numbers without a country code are dropped. A number is kept when it comes from a tel: link or structured data, or when the visible text carries a leading + (or 00). A national-format number like (020) 7946 0958 is not kept: without a validation library, the regex that would catch it also catches order numbers, prices, dates and postcodes, and a wrong phone number in a lead list is worse than an empty column.
  • robots.txt is honoured, and a refusal costs the whole domain. A site that disallows its home page produces a free robots-disallowed row. A disallowed sub-page is skipped silently and the profile is built from the rest.
  • JavaScript is not executed. There is no browser. A site that renders its address, its footer or its social links only in the client will give up less than one that ships them in the HTML. Most do ship them; the captures are honest about the ones that do not.
  • The employee-count and founding-year patterns are best-effort. They read prose, and prose is ambiguous. Both carry an evidence column for exactly that reason, and the founding-year rule refuses any sentence that is not visibly about the company.
  • A company with many offices and no stated headquarters gets no HQ. Schema.org has no "this one is the head office" marker that sites reliably use, so when a company lists several and labels none, hqAddress and its four sub-fields are null and hqEvidence says how many there were.
  • Structured data is only as good as the company's. Four of the six captured sites publish an Organization node; one publishes none at all. When it is absent, Open Graph and the footer carry the row, and confidence drops to say so.
  • hosting and cdnHint are hints and are labelled as such. Cloudflare running a domain's DNS does not prove Cloudflare is proxying its traffic.
  • The upstream format may change. Websites are rewritten, structured data is removed, robots.txt is tightened. When a rule stops matching, the column goes null rather than guessing — which is also how you will notice.

Privacy

This Actor is company-level only. That is a product decision, a compliance position and a selling point, and all three are deliberate.

  1. No names of individuals, ever. No CEO, no founder, no managing director, no "decision makers", no team-page roster. The structured-data walker skips Person-typed nodes entirely and passes no inheritance to their children — so when stripe.com publishes its two founders by name inside the same graph as the company's own details, neither name reaches the output. A unit test pins that against the captured page.
  2. Emails are an allowlist, not a name detector. An address survives only when its local part is a recognised business role or generic business word (info@, sales@, careers@, support@; trailing digits ignored, so sales2@ still matches). A given name anywhere in the local part beats a role word, so anna.sales@ is treated as a person's and dropped. Everything unrecognised is treated as personal and dropped. Worked example: basecamp.com publishes exactly one address, jason@basecamp.com, and this Actor returns zero emails for it. That is the correct answer.
  3. Phones are business numbers as published on the site. Team pages are fetched only for the founding-year and headcount patterns; no contact is ever read from one.
  4. No email verification of any kind. No SMTP probe, no third-party verification API, no catch-all test. acceptsEmail is derived from the presence of MX records, which is a DNS fact about a domain — not a test of anybody's mailbox.
  5. No guessed addresses. There is no emailPattern column and no firstname.lastname@ inference. An address the company did not publish does not appear.
  6. RDAP is already redacted at source. ICANN policy and the GDPR mean most registries redact the registrant entirely, and this Actor does not return a registrant field at all. The registrar's published abuse mailbox is a different thing and is not a natural person's address.
  7. robots.txt is honoured on every page, and a refusal is reported rather than worked around.
  8. You are the controller of what happens next. If you export this data, join it to personal data, or contact anyone on the strength of it, the GDPR and CCPA obligations are yours. Turning includePersonalEmails on makes that immediate and explicit.

Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

Video, audio & social

News, documents & the web

Business, finance & jobs

Apps & games