# Company Enrichment API: Company Data from a Domain (`groundtruth/company-enrichment`) Actor

Company enrichment API. Give company domains, get one record each: registrar, DNS and email provider, tech stack, open roles from its job board, and the phones, inboxes and address the company publishes on its own site. Every item links to its source. No headcount or funding data.

- **URL**: https://apify.com/groundtruth/company-enrichment.md
- **Developed by:** [Ground Truth](https://apify.com/groundtruth) (community)
- **Categories:** Lead generation, Business, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.00 / 1,000 company records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Company Enrichment API: Company Data from a Domain

Give it a list of company domains. For each one you get a single record: who registered the domain, which email provider the company uses, its website technology stack, how many roles it has open on its own job board, and the phone numbers, role inboxes (sales@, info@), postal address and contact page the company publishes on its own website. Every item links to the page or registry it came from.

**Checked every day:** this Actor is run daily with a known input and its results are compared with the original sources. Latest results: https://companyfacts.dev/status#apify-company-enrichment

### When to use it

- Enrich a CRM or lead list that only has a company domain: tech stack, hiring activity, a phone number and a contact page, in one pass.
- Find which companies on a list run Google Workspace or Microsoft 365, publish DMARC, or use a given technology.
- Spot growing companies: open roles by department, and roles opened and closed in the last 30 days, from the company's own Greenhouse, Ashby, Lever or Workable board.
- Add a published phone number, role inbox and address to a list of small local businesses, with the page each came from.
- Pre-qualify accounts before outreach: is the domain registered, how old is it, does the company still hire.

**For an AI agent:** call it when you need checkable facts about a company you only know by its domain, instead of guessing from a web search. Each part of the answer names its source, and a part with nothing to report says why.

### What it does not do

- It does not return headcount, revenue, funding or industry codes. It is built from public sources only, not from a purchased database.
- It does not find the email of a named person. Emails are never guessed from a pattern such as first.last@. Only addresses the company itself published on its own website are returned, at the company's own domain, each with its page. Addresses beyond role inboxes (which may name a person) are returned for US companies only, never for EU or UK domains.
- It reads only a company's own public pages, honoring robots.txt. It does not use LinkedIn, does not log in anywhere and does not get around bot protection. A site that blocks automated readers gives `contacts_error`.
- `hiring` needs the company to use Greenhouse, Ashby, Lever or Workable and to match its domain name. Many do not, and the record says so under `missing`.
- It takes a domain, not a company name. `stripe.com`, `https://www.stripe.com/about` and `jane@stripe.com` are all read as `stripe.com`.

### Sample output

Input:

```json
{ "domains": ["wpengine.com", "no-such-company-zq81x.com"], "include_contacts": true }
```

Output for the first domain (trimmed, run on 2026-10-30):

```json
{
  "input": "wpengine.com",
  "lookup_status": "ok",
  "domain": "wpengine.com",
  "parts_found": ["web", "tech"],
  "web": {
    "registered": true,
    "registrar": "MarkMonitor Inc.",
    "created_date": "2010-03-10",
    "expiry_date": "2027-03-10",
    "email_provider": "Google Workspace",
    "dmarc_policy": "reject",
    "verify_url": "https://rdap.verisign.com/com/v1/domain/wpengine.com"
  },
  "tech": {
    "domain": "wpengine.com",
    "cms": ["WordPress"],
    "technologies": [
      { "name": "WordPress", "categories": ["CMS", "Blogs"], "confidence": 100, "evidence": ["scripts"] },
      { "name": "WP Engine", "categories": ["PaaS", "Hosting"], "confidence": 100, "evidence": ["headers"] }
    ]
  },
  "missing": [ { "part": "hiring", "reason": "'wpengine' was not found. greenhouse has no board called 'wpengine'; ashby has no board called 'wpengine'; lever has no board called 'wpengine'; workable returned HTTP 429" } ],
  "contacts": {
    "domain": "wpengine.com",
    "us_company": true,
    "contact_page": "https://wpengine.com/contact/",
    "phones": [ { "number": "+1 512-623-8484", "source_url": "https://wpengine.com/about-us/" } ],
    "role_emails": [],
    "other_emails": [],
    "address": { "street": "500 W 2nd St", "city": "Austin", "region": "TX", "postal_code": "78701", "country": "US", "source_url": "https://wpengine.com/" },
    "pages_read": [ { "url": "https://wpengine.com/", "kind": "home" }, { "url": "https://wpengine.com/contact/", "kind": "contact" } ],
    "notes": []
  }
}
```

The second domain comes back as a record with `lookup_status: "error"` and `error: "no-such-company-zq81x.com is not a registered domain, so there is no company to look up"`. It is in the dataset, it is not charged.

**Check it yourself.** Open `web.verify_url`: it is the registry's own record for the domain, with the same registrar and dates. Open each `source_url` under `contacts`: the phone number or address is on that page. Technology names come with the evidence they were judged from (`headers`, `dns:TXT`, page markup).

**A handled error.** An entry such as `"not a domain"` or `12345` is written to the dataset with the reason (`lookup_status: "error"`) and is not charged. A company that cannot be looked up is never silently dropped.

### Input

| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
| `domains` | list of text | yes | stripe.com, wpengine.com | Company website domains. Up to 1,000 per run. Names, URLs, `www.` hosts and email addresses are reduced to the host. Unicode names are converted to punycode. Duplicates are looked up once. |
| `include_contacts` | true or false | no | true | Also read the company's own website for the phones, role inboxes, address and contact page it publishes. Turn off for the company record alone. |

### Output fields

| Field | Meaning |
|---|---|
| `input`, `domain` | What you gave (never a mailbox name) and the host looked up |
| `lookup_status` | `ok` (a company record was found) or `error` (see `error`; not charged) |
| `parts_found` | Which parts answered: `web`, `tech`, `hiring` |
| `web` | Registry (RDAP) and DNS facts: registered or not, registrar, created, updated and expiry dates, domain age, nameservers, MX hosts, email provider, SPF and DMARC policy, `verify_url`. No registrant data |
| `tech` | The website's technology stack: name, categories, confidence and the evidence it was judged from |
| `hiring` | The company's own job board: open roles, by department and place, salary ranges where posted, roles opened and closed in the last 30 days |
| `missing` | A part that was tried and had nothing to report, with the reason (for example no job board found) |
| `contacts` | `us_company`, `contact_page`, `phones`, `role_emails`, `other_emails` (US companies only), `address`, `pages_read`, `notes`. Every phone, email and the address carries `source_url`. Present only where something was found |
| `contacts_error` | Why there are no contacts: the site publishes nothing, robots.txt asks readers to stay out, or the site could not be read |
| `error` | Why a company has no record. Present only when `lookup_status` is `error` |

### Errors and what to do

| What you see | Meaning | What to do |
|---|---|---|
| `lookup_status: "error"`, "is not a registered domain" | The registry says the domain does not exist | Check the spelling, or drop the row |
| `lookup_status: "error"`, "nothing could be found" with a 502-style reason | A source was down for the whole lookup | Re-run those domains |
| `contacts_error`: "publishes no phone, email, address or contact page" | The site's pages hold none | Nothing to do; not charged |
| `contacts_error`: "robots.txt asks automated readers not to read the site" | The site opts out | Nothing to do; it is honored and not charged |
| Run fails with "rejected this Actor's API key" or "no lookups left" | A fault on the author's side, not yours | Report it on the Actor page. Nothing was charged for the unprocessed domains, which are listed in the dataset as "not looked up" |
| Entries "not looked up: the spending limit set for this run was reached" | You set a maximum cost per run | Raise it and run those domains again |

### Pricing

Pay per event, no subscription: one `company-record` event for each company that returned a record, and one `contacts-record` event for each company where contacts were found, plus Apify's small start event. You are not charged for invalid entries, duplicates, a company that failed, or a site that publishes no contacts. Current prices are on the Pricing tab.

### Sources and licensing

Our own data service, which reads registry RDAP servers, public DNS over HTTPS, a company's own public web pages (robots.txt honored), company job boards, and SEC filings. Details and the full testing record are in SOURCES.md and NETWORK-EXEMPTION.md.

### Rate it, or tell us what broke

If this Actor did the job for you, a star rating on its [Reviews](https://apify.com/groundtruth/company-enrichment/reviews) tab helps other people find it. If something did not work, please [open an issue](https://apify.com/groundtruth/company-enrichment/issues/open) instead: we read every one and fix what we can.

# Actor input Schema

## `domains` (type: `array`):

Company website domains, one per line. Plain names (stripe.com), URLs and www hosts are fine: each is reduced to the host. Up to 1,000 per run. Duplicates are looked up once.

## `include_contacts` (type: `boolean`):

Also read the company's own public website for the phone numbers, role inboxes such as sales@ and info@, postal address and contact page it publishes. Charged only for companies where something was found. Work emails beyond role inboxes are returned only for US companies, exactly as published. Turn off for the company record alone.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "https://www.wpengine.com/about-us",
    "joesstonecrab.com"
  ],
  "include_contacts": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsCsv` (type: `string`):

No description

## `runDetails` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "stripe.com",
        "wpengine.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundtruth/company-enrichment").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "domains": [
        "stripe.com",
        "wpengine.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("groundtruth/company-enrichment").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "stripe.com",
    "wpengine.com"
  ]
}' |
apify call groundtruth/company-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,groundtruth/company-enrichment"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/wTNu8xefnd7QXXv1x/builds/A8A9mJAKFiyQnb7Mr/openapi.json
