# Company Enrichment by Domain ⚡ Emails, Socials, Hiring, Ads (`pulselab/company-enrichment-by-domain`) Actor

Turn company domains into one enriched row each: emails by role, phones, address, founding year, LinkedIn/Instagram/X/YouTube/TikTok, website platform and marketing tools, Shopify/WooCommerce product count, ATS and open jobs, optional Google Ads activity. Respects robots.txt. Pay per company.

- **URL**: https://apify.com/pulselab/company-enrichment-by-domain.md
- **Developed by:** [Pulse Lab](https://apify.com/pulselab) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 enriched companies

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 by Domain – Emails, Socials, Tech, Hiring, Ads

Give it a list of **company domains** and get back **one enriched row per company**, built from the company's own website and public APIs:

- **Contacts** – emails grouped by role (general / sales / support / press / careers / legal / personal), phones, postal address, contact page URL
- **Company facts** – name, description, language, country, founding year (from schema.org markup when the site publishes it)
- **Social profiles** – LinkedIn, Instagram, Facebook, X, YouTube, TikTok, GitHub, Pinterest
- **Tech** – website platform (Shopify, WooCommerce, WordPress, Webflow, Wix, Squarespace, HubSpot, Next.js, …) and marketing / support tools (Google Analytics, GTM, Google Ads tag, Meta Pixel, TikTok Pixel, LinkedIn Insight, HubSpot, Intercom, Zendesk, Klaviyo, Stripe, …)
- **E-commerce** – Shopify / WooCommerce detected, **number of products**, store currency
- **Hiring** – applicant tracking system (Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, Breezy, BambooHR, Personio, Teamtailor), **number of open jobs**, jobs posted in the last 30 days, latest posting date, sample job titles, job board URL
- **Google Ads** (optional) – is the company running Google ads, estimated number of ads, last seen date (Google Ads Transparency Center)

No login, no API keys. The actor reads the homepage plus a few contact / about / imprint pages, **respects `robots.txt`**, and uses only official public endpoints for jobs and store data. Blocked or unreachable websites are reported and **not charged**.

### Why this instead of a page-by-page contact scraper?

- **One price per company**, not per page – predictable cost for lists of thousands of domains.
- **Sales signals in the same row** – is the company hiring (and how fast), selling online (and how much), advertising on Google? Great for lead qualification and account prioritisation.
- **Clean, deduplicated output** – emails are validated and grouped by role, phones deduplicated, social links normalised.

### Input

| Field | Description |
|---|---|
| `domains` (required) | Company domains or website URLs, one per line. |
| `maxPagesPerDomain` | Pages read per site (homepage + contact / about / imprint). Default 5, max 10. |
| `includeHiring` | Detect ATS and count open jobs (default on, included in the company price). |
| `includeEcommerce` | Detect Shopify / WooCommerce and read the public product count (default on, included). |
| `includeAdsCheck` | Look the domain up in the Google Ads Transparency Center (default off, **charged separately**, uses a residential proxy). |
| `adsRegion` | Country for the ads check (default anywhere). |

Example:

```json
{
  "domains": ["stripe.com", "gymshark.com", "netguru.com", "https://www.hubspot.com"],
  "includeAdsCheck": true
}
```

### Output

One row per domain (`type: "company"`). Key fields:

`domain, status, website, name, description, language, address, country, foundingDate, emails[], emailsByRole{}, primaryEmail, phones[], linkedin, instagram, facebook, x, youtube, tiktok, github, pinterest, platform, platforms[], generator, tools[], ecommercePlatform, productCount, storeCurrency, ats, atsSlug, jobBoardUrl, careersUrl, openings, postedLast30Days, latestPostedAt, topJobTitles[], adsVerdict, adsEstimateLow, adsEstimateHigh, latestAdDate, adsAdvertisers[], contactPageUrl, pagesCrawled, pagesSkippedByRobots, fetchedViaProxy, note, checkedAt`

`status`: `OK` (charged), `BLOCKED` (site refuses automated access even from a residential IP – not charged), `UNREACHABLE` (DNS / TLS / network error – not charged), `INVALID_INPUT`, `ERROR`.

`adsVerdict`: `NAME_MATCH` (an advertiser whose name matches the domain runs ads for it), `SINGLE_ADVERTISER`, `MIXED`, `LOW_VOLUME`, `NO_ADS`, `NOT_CHECKED` (not charged).

Example row (real run, trimmed):

```json
{
  "domain": "stripe.com",
  "status": "OK",
  "name": "Stripe",
  "description": "Stripe is a suite of APIs powering online payment processing…",
  "emails": ["press@stripe.com", "privacy@stripe.com"],
  "emailsByRole": { "press": ["press@stripe.com"], "legal": ["privacy@stripe.com"] },
  "linkedin": "https://www.linkedin.com/company/stripe/",
  "x": "https://twitter.com/stripe",
  "youtube": "https://youtube.com/@stripe",
  "github": "https://github.com/stripe",
  "platform": "nextjs",
  "ats": "greenhouse",
  "jobBoardUrl": "https://job-boards.greenhouse.io/stripe",
  "openings": 725,
  "adsVerdict": "NAME_MATCH",
  "pagesCrawled": 5
}
```

Another: `gymshark.com` → Shopify store with **10,013 products**, Greenhouse with 10 openings, support@gymshark.com, Instagram / Facebook / X / TikTok, Google Ads `NAME_MATCH`.

### Measured results

Test list of 22 well-known companies (SaaS, DTC brands, agencies, a Korean corporation): **20 enriched, 2 blocked** (Cloudflare / Vercel bot protection even from a residential IP – reported free), ~50 seconds in total with 4 parallel workers. Across the 20 enriched rows: social profiles found for 85 %, ATS + open-job count for 75 %, at least one email for 55 %, schema.org address or founding year for 25 % (large brands often publish contact forms instead of addresses; small and mid-size businesses typically expose more).

### Pricing

Pay per event – only successful rows are charged:

- **company** – one enriched company row (`status: OK`). Includes hiring and e-commerce detection.
- **ads-check** – one Google Ads Transparency lookup (only when `includeAdsCheck` is on and the lookup succeeded).

Blocked, unreachable and invalid domains are free. Platform usage is included in the event prices.

### Limitations (please read)

- The actor only sees what the company publishes on its website and public job boards. Sites that hide contact details behind forms or JavaScript-only rendering return fewer fields.
- Some sites (Cloudflare "bot fight", Vercel rate limiting) block automated access even from residential IPs → `BLOCKED`, not charged.
- Email addresses are extracted, not verified. Role classification is based on the local part (`sales@`, `support@`, …).
- Job counts come from the public job-board API of the detected ATS; companies without a supported ATS get `ats: null`. Workday boards are found only when the careers page links to them.
- The Google Ads check reports what the Transparency Center shows for the domain; a `NO_ADS` result means no ads were found in the sample, not a guarantee.
- `robots.txt` is respected: pages a site disallows for all crawlers are skipped (`pagesSkippedByRobots`).
- A per-domain time budget of 90 s keeps slow sites from stalling a run; skipped steps are listed in `note`.

# Actor input Schema

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

One company per line: a domain (acme.com) or any page of its website. One enriched row is returned per domain.

## `maxPagesPerDomain` (type: `integer`):

How many pages to read per website (homepage + contact / about / imprint pages). 5 is enough for most sites; raise it for large corporate sites.

## `includeHiring` (type: `boolean`):

Find the applicant tracking system (Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, Breezy, BambooHR, Personio, Teamtailor) and count current openings. Included in the company price.

## `includeEcommerce` (type: `boolean`):

If the site runs Shopify or WooCommerce, read the public store endpoint for product count and currency. Included in the company price.

## `includeAdsCheck` (type: `boolean`):

Look the domain up in the Google Ads Transparency Center: is the company running Google ads, how many, last seen date. Uses a residential proxy and is charged separately per checked domain.

## `adsRegion` (type: `string`):

Only used with the Google Ads check: count ads shown in this country.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "https://www.netguru.com",
    "gymshark.com",
    "hubspot.com",
    "zapier.com"
  ],
  "maxPagesPerDomain": 5,
  "includeHiring": true,
  "includeEcommerce": true,
  "includeAdsCheck": false,
  "adsRegion": "ANYWHERE"
}
```

# Actor output Schema

## `companies` (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": [
        "apify.com",
        "https://www.netguru.com",
        "gymshark.com",
        "hubspot.com",
        "zapier.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pulselab/company-enrichment-by-domain").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": [
        "apify.com",
        "https://www.netguru.com",
        "gymshark.com",
        "hubspot.com",
        "zapier.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("pulselab/company-enrichment-by-domain").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": [
    "apify.com",
    "https://www.netguru.com",
    "gymshark.com",
    "hubspot.com",
    "zapier.com"
  ]
}' |
apify call pulselab/company-enrichment-by-domain --silent --output-dataset

```

## MCP server setup

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

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/RQPFzRTAa2cuOwTPh/builds/PReRGUNSk2e2sXjqX/openapi.json
