# Company Enrichment – Domain to Firmographics (`pontio/company-enrichment`) Actor

Turn a company website into one profile: emails, phones, socials, tech stack, email provider and security, domain age, and the legal entity with its LEI.

- **URL**: https://apify.com/pontio/company-enrichment.md
- **Developed by:** [Gabor Molnar](https://apify.com/pontio) (community)
- **Categories:** Lead generation, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 company enricheds

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 – Domain to Firmographics

Give it a company's website and get back one profile: the emails, phone numbers and social profiles on the site, the technologies it runs and its email provider, its DNS and email-security setup (SPF, DMARC, DKIM), when the domain was registered, and, when the site states a legal name that GLEIF holds, the legal entity behind it with its LEI, legal address and registry number. Pass one domain or a list of up to 1,000 in a single run. You pay only for complete profiles.

### What you get

One dataset item per website. Here is a real one for `hetzner.com`, with some long fields shortened:

```json
{
  "domain": "hetzner.com",
  "reasonCode": "enriched",
  "websiteUrl": "https://www.hetzner.com/",
  "emails": [],
  "phones": [],
  "socials": [
    { "network": "linkedin", "url": "https://linkedin.com/company/hetzner-online" },
    { "network": "x", "url": "https://x.com/Hetzner_Online" }
  ],
  "techStack": [
    { "name": "Brevo", "category": "email-sending", "source": "dns" }
  ],
  "registry": {
    "status": "matched",
    "legalNameOnSite": "Hetzner Online GmbH",
    "company": {
      "lei": "391200XMWUW8G8MXV439",
      "legalName": "Hetzner Online GmbH",
      "entityStatus": "ACTIVE",
      "jurisdiction": "DE",
      "registrationNumber": "HRB 6089",
      "legalAddress": {
        "lines": ["Industriestr. 25"],
        "city": "Gunzenhausen",
        "region": "DE-BY",
        "postalCode": "91710",
        "country": "DE"
      },
      "...": "the full company-registry record"
    }
  },
  "dns": { "a": ["213.133.116.44"], "mx": { "found": true, "hosts": [{ "priority": 10, "host": "mail.hetzner.company" }] }, "...": "NS, TXT, CAA, SOA, DNSSEC" },
  "emailAuth": { "spf": { "found": true, "grade": "strict" }, "dmarc": { "found": true, "policy": "reject", "grade": "enforced" }, "dkim": { "found": [], "confidence": "selector-guess" } },
  "registration": { "source": "rdap", "registrar": "Hetzner Online GmbH", "createdAt": "1997-01-15T05:00:00Z", "expiresAt": "2027-01-14T05:00:00Z" },
  "pagesCrawled": ["https://www.hetzner.com/"]
}
```

### Use it when

- You have a list of company websites (from a CRM export, a lead list or a scraper) and want contacts, tech and company data for each in one pass.
- You segment leads by technology: who runs Shopify, HubSpot, Google Workspace or Microsoft 365.
- You need the legal entity behind a website: its registered name, LEI, jurisdiction and local registry number.
- An agent needs a typed company record instead of reading a homepage.

### Input

| Field | Type | Required | What it does |
| --- | --- | --- | --- |
| `domains` | array of strings | yes | Company websites as domains or URLs, up to 1,000 per run. A URL, `www.` or an email address is reduced to its domain. Repeats of the same site are looked up and charged once. |

### Output

| Field | What it holds |
| --- | --- |
| `domain` | The normalized domain. |
| `reasonCode` | What happened. See the pricing table. |
| `websiteUrl` | Where the homepage landed after redirects, or `null` if it never loaded. |
| `emails`, `phones`, `socials` | Contacts found on the homepage and up to three contact, about, team or imprint pages. Phones are in E.164 format and validated. Socials are profile links only (not share buttons or posts) for LinkedIn, X, Facebook, Instagram, YouTube, TikTok and GitHub. |
| `techStack[]` | Technologies detected, each with a `category` and a `source`: `html` (seen in the site's pages) or `dns` (seen in its MX records, SPF includes or verification records). About 80 technologies across CMS, e-commerce, frameworks, analytics, marketing, chat, payments, consent, email hosting and sending, and business apps. |
| `registry.status` | `matched`: one LEI holder carries the legal name the site states, as its legal name or one of its other registered names, ignoring case and punctuation. When several do, active entities are preferred, then those in the country the site's phone numbers or country domain point to. `no_legal_name`: the site states none we can read. `no_exact_match`: no LEI holder carries that name. `ambiguous`: several still remain after that, so none is attached. |
| `registry.legalNameOnSite` | The legal name the site states about itself, from its schema.org `legalName` or a copyright line ending in a legal form (`© 2026 Acme GmbH`). |
| `registry.company` | The GLEIF record when `status` is `matched`, otherwise `null`. Same fields as the Company Registry Search Actor: LEI, legal name, status, jurisdiction, legal form, registry number, addresses. |
| `dns` | A, AAAA, NS, MX, TXT, CNAME, CAA and SOA records, plus DNSSEC. |
| `emailAuth` | SPF, DMARC and DKIM records with a grade for each. DKIM checks a list of common selectors, so an empty `found` means none of those, not "no DKIM". |
| `registration` | Registrar, creation and expiry dates and status from RDAP. `source: "inconclusive"` means the domain's registry offers no RDAP (many country domains, `.de` among them). |
| `pagesCrawled` | The pages that were read. |

### Pricing

Pay per event, $5.00 per 1,000 complete profiles ($0.005 each), charged on the `company-enriched` event.

One charge per website whose profile came back complete: the site loaded and every lookup answered. A registry result of `no_legal_name`, `no_exact_match` or `ambiguous` still counts as an answer, and so does a `registration` of `inconclusive` for a domain whose registry offers no RDAP (a permanent fact about that registry, not an outage). Everything else is free.

| `reasonCode` | Charged |
| --- | --- |
| `enriched` (the site loaded and every lookup answered) | yes |
| `domain_not_found` (the domain does not exist in DNS) | no |
| `website_blocked` (the site answered 401, 403 or 429: a bot wall) | no |
| `website_unreachable` (no web server answered, or it failed) | no |
| `upstream_error` (DNS, RDAP or GLEIF failed or throttled us, so the profile is incomplete) | no |
| `invalid_domain` (not a registrable domain: an IP, `localhost`, a bare TLD) | no |

Free rows still carry what was found. A blocked site's row has its DNS, email security, registration and DNS-detected technologies.

If you set a maximum total charge for a run, the Actor stops as soon as that limit is reached instead of working for free. Items after that point are left out of the dataset and not charged, and the run log says how many; submit them in a new run.

### Limits

- Pages are read as served, without running JavaScript. A site that renders its contact details or loads its scripts client-side shows fewer contacts and technologies. An empty list means "not seen", not "not used".
- Only the homepage and up to three same-site contact pages are read.
- The company is attached only on an exact legal-name match, so a site that states only a brand (`© Siemens`), or whose legal name GLEIF files differently, gets no company. GLEIF only covers entities that hold an LEI, which most small private companies do not.
- Websites are processed one after another. If you set a maximum charge for the run, or the run reaches its timeout, it stops there. The items already written stay in the dataset, so you can re-run the remainder.

### Data sources

The company's own website; DNS over HTTPS (Cloudflare `1.1.1.1`); RDAP, the registries' official successor to WHOIS; and GLEIF's public LEI index (CC0). No paid data source, and nothing is stored between runs.

# Changelog

This Actor's version history is a separate document: https://apify.com/pontio/company-enrichment/changelog.md

# Actor input Schema

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

Company websites, as domains or URLs, e.g. example.com (max 1000 per run). Repeats of the same site are dropped. Charged once per company profile that came back complete.

## Actor input object example

```json
{
  "domains": [
    "hetzner.com"
  ]
}
```

# Actor output Schema

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

One item per input, in this run's default dataset.

# 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": [
        "hetzner.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pontio/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": ["hetzner.com"] }

# Run the Actor and wait for it to finish
run = client.actor("pontio/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": [
    "hetzner.com"
  ]
}' |
apify call pontio/company-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pontio/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/S4zHXA5IlI9Dj7znw/builds/JZVceaHqBAKZHe72O/openapi.json
