# Company Enrichment from Domain - Socials, Contacts, Logo, Tech (`neverempty/company-enrichment-from-domain`) Actor

Give company domains, get one row per company read from its own website: name, description, address, founding year, LinkedIn/X/Facebook and other official profiles, contact email and phone, logo, tech stack, careers/pricing/blog/docs/status pages. Every value has its source and a confidence.

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

## Pricing

from $1.40 / 1,000 company returneds

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 from Domain - Socials, Contacts, Logo, Tech

**This Actor returns only what a company says on its own website.** It does not return revenue or funding amounts, and it does not look anything up in outside databases, LinkedIn or search engines. A field the site does not state stays `null`; nothing is estimated.

Give it a list of company domains (the rows of a CRM or lead list where all you have is the domain) and get **one row per company**:

| Group | Columns | Read from |
| --- | --- | --- |
| Name | `companyName`, `legalName` | JSON-LD `Organization`, `og:site_name`, the copyright line, the page title |
| Description | `description` | meta description, `og:description`, JSON-LD (home page only) |
| Industry hints | `schemaTypes`, `keywords` | the JSON-LD `@type` and meta keywords the site itself declares; no classification is guessed |
| Address | `addressRaw`, `street`, `city`, `region`, `postalCode`, `country` | JSON-LD `address` |
| Founded, size | `foundingYear`, `employeesMin`, `employeesMax` | JSON-LD `foundingDate`, `numberOfEmployees` (only when the site declares them) |
| Official profiles | `linkedinUrl`, `xUrl`, `facebookUrl`, `instagramUrl`, `youtubeUrl`, `githubUrl`, `tiktokUrl` | JSON-LD `sameAs`, header and footer links |
| Contact | `email`, `phone` | JSON-LD, `mailto:` and `tel:` links |
| Logo | `logoUrl` | JSON-LD logo, header image, touch icon |
| Technologies | `technologies` (up to 10, with category) | home page HTML, scripts and response headers |
| Company pages | `careersUrl`, `pricingUrl`, `blogUrl`, `docsUrl`, `statusUrl` | home page links |
| Language | `language` | `<html lang>` |

Every value comes with **where it was read and how sure the reading is**:

```json
"provenance": {
  "companyName": { "source": "json-ld Organization.name", "page": "https://www.zotter.at/", "confidence": "high" },
  "xUrl": { "source": "footer link", "page": "https://example.com/", "confidence": "medium", "candidates": 2, "alternatives": ["https://x.com/other"] }
},
"lowConfidenceFields": ["phone"]
```

### How it reads a site

- For each company the home page is read, plus up to 3 more pages of the same site found in its links (about, contact, legal notice): **at most 4 pages per company**. Each site's `robots.txt` is read first and followed.
- If you give a page address (an article, a product page), the site's **home page** is read instead, so the description is the company's and not one article's.
- No browser is used, so text that only appears after JavaScript runs is not seen. No proxies are used; a site that answers 403 or shows a check page is not retried through another route.

#### What is done about the usual mix-ups

- **Share buttons** (`twitter.com/intent/tweet`, `facebook.com/sharer`, `linkedin.com/shareArticle`), posts, videos, personal LinkedIn profiles and repositories are not accounts and are never returned.
- **Other companies' accounts** (customers, partners, embedded posts): a profile linked once in the header or footer, or listed in the company's own JSON-LD, is `high`. If a network has several different accounts, the one whose handle matches the company name is returned as `medium`; if none matches, the first is returned as `low` with the others in `alternatives`. A profile found only in the page body is `low` unless its handle matches the name.
- **JSON-LD of someone else**: `parentOrganization`, `brand`, `seller`, review and article authors are skipped; an organization whose `url` points to another site is not used.
- **Several addresses or phone numbers**: the first is returned as `low`, with the count.
- **Email**: only role addresses (info@, contact@, sales@ …) on the company's own domain (or a free-mail domain) and addresses the site declares in JSON-LD are returned. A role address on another organization's domain (a regulator or ombudsman named in a legal notice) is not returned. Addresses that look like a person's are not returned. A special-purpose address (press@, legal@, jobs@) is returned as `low` when it is the only one.

### Measured on 40 real companies

Run in production on 2026-10-06 (Apify datacenter connection, no proxy): 12 SaaS, 8 manufacturers and retailers, 8 European, 4 Japanese, 8 small businesses. 34 of 40 home pages could be read; 4 sites refused the reader (HTTP 403/429), 1 disallows robots, 1 could not be reached. Those 6 rows were free.

"Read" is the number of the 34 readable sites where the field came back. "Right" is from a field-by-field check of 20 of them: each value was compared with the links and JSON-LD on the page it was read from and with the company's known official name and accounts (not every link was opened in a browser).

| Field | Read (of 34) | Right (of those checked) | How it is returned |
| --- | --- | --- | --- |
| companyName | 32 | 18 of 20 | the 2 misses were site-edition names ("Brompton UK"); such names are now `low` |
| description | 33 | 20 of 20 | `high` |
| address | 8 | 7 of 8 | always `low` (below 90%; one site declares a regional office in its JSON-LD) |
| foundingYear | 7 | 7 of 7 as the site states | `high` |
| employees | 0 | not measured | none of the 40 sites declares it |
| social profiles | 26 sites, 117 links (109 `high`) | 85 of 85 `high` links, no wrong one found | several candidates are `medium` or `low` |
| email | 12 | 6 of 9 were a general contact | the 3 others were press@ / legal@, now `low`; a role address on another organization's domain is no longer returned |
| phone | 4 | 3 of 3 with a single number | several numbers are `low` |
| careers / pricing / blog / docs / status | 21 / 17 / 16 / 15 / 7 | 58 of 58 links, no wrong one found | `high` when address and link text agree, else `medium` |
| logo | 34 | not checked by eye | confidence from the source (JSON-LD logo `high`, favicon `low`) |
| technologies | 33 | not checked by eye | open fingerprint rules |
| language | 34 | as the site declares | `high` |

Small sites and large retailers often state little in a machine-readable way; expect names, descriptions, profiles and pages far more often than addresses, founding years or employee counts.

### Price

You pay per company row returned: `company-returned`, for a company whose name plus at least 3 more field groups were read. **A site that could not be read, was refused, or stated too little is a free row that says why** (and still carries whatever was read). No start fee.

### Input

```json
{ "domains": ["stripe.com", "www.hetzner.com", "https://www.zotter.at/"] }
```

1 to 500 domains per run. The same domain given twice is read and charged once.

### Output example (shortened)

```json
{
  "status": "ok",
  "input": "www.zotter.at",
  "domain": "zotter.at",
  "homepageUrl": "https://www.zotter.at/",
  "companyName": "Zotter Schokolade GmbH",
  "description": "500 verführerische Schokoladensorten im Online Shop …",
  "city": "Riegersburg",
  "country": "AT",
  "foundingYear": 1999,
  "linkedinUrl": "https://www.linkedin.com/company/zotter-schokolade",
  "xUrl": null,
  "email": "schokolade@zotter.at",
  "phone": "+43-3152-5554",
  "technologies": [{ "name": "TYPO3 CMS", "category": "CMS" }],
  "careersUrl": "https://www.zotter.at/das-ist-zotter/jobs",
  "language": "de",
  "fieldsFound": 11,
  "lowConfidenceFields": ["addressRaw", "street", "city", "region", "postalCode", "country"],
  "pagesRead": ["https://www.zotter.at/", "https://www.zotter.at/kontakt", "https://www.zotter.at/impressum"],
  "note": "Not stated on the pages read (left empty, nothing is guessed): employees. …"
}
```

Free rows have `status` `too-little-data`, `blocked`, `robots-disallowed`, `robots-unreachable`, `login-required`, `not-found`, `unreachable`, `unreadable`, `not-html`, `bad-input` or `budget-reached`, and a `note` with the reason.

### Limits

- Values are what the site states; they are not checked against a register. Contact data is business contact data published by the company; you are responsible for using it lawfully.
- Pages built entirely by JavaScript return little (`too-little-data`, free).
- Related Actors by the same author: [website-contacts](https://apify.com/neverempty/website-contacts) for every email and phone on a site, [website-technology-detector](https://apify.com/neverempty/website-technology-detector) for the full technology list.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

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

Company websites to read, one per line (1 to 500 per run): a domain (stripe.com) or any URL of the site. If you give a page address (an article, a product), the site's home page is read instead. For each company the home page and up to 3 more pages of the same site (about, contact, legal notice) are read, following the site's robots.txt. Only the site you give is read: no outside database, no LinkedIn, no search engine. The same domain given twice is read and charged once. If this is empty, the example site www.hetzner.com is read and nothing is charged.

## `maxConcurrency` (type: `integer`):

How many company sites are read in parallel (1 to 8).

## `requestTimeoutSecs` (type: `integer`):

How long to wait for one page to answer (5 to 60 seconds). HTTP 429, 500, 502, 503 and 504 are asked again up to two more times.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "www.hetzner.com",
    "www.zotter.at"
  ],
  "maxConcurrency": 4,
  "requestTimeoutSecs": 20
}
```

# Actor output Schema

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

One row per company domain: name, description, industry hints the site states, address, founding year, employees, official social profiles, contact email and phone, logo, main technologies, careers/pricing/blog/docs/status pages and language, with the source page and a confidence for every value (provenance). A site that could not be read, or where too little could be read, comes back as a free row that says why.

# 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",
        "www.hetzner.com",
        "www.zotter.at"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/company-enrichment-from-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": [
        "stripe.com",
        "www.hetzner.com",
        "www.zotter.at",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/company-enrichment-from-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/ZziXaPM6jhIqrXJho/builds/JIIGRCtvPlnMGgSsj/openapi.json
