# EU Company Contacts & VAT Extractor (`eurodata-tools/eu-company-contacts`) Actor

Turn a list of company websites into clean B2B rows: emails, phones, social profiles, checksum-validated VAT IDs, legal name, Italian PEC & REA, German Handelsregister, French SIREN/SIRET. Pay only for websites where data is found.

- **URL**: https://apify.com/eurodata-tools/eu-company-contacts.md
- **Developed by:** [EuroData Tools](https://apify.com/eurodata-tools) (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 website with data

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

## EU Company Contacts & VAT Extractor

Turn a list of company websites into **clean B2B rows**: emails, phones, social profiles, **checksum-validated VAT IDs**, legal company name and **European registry numbers**. These are the details EU law requires companies to publish on their own website: Italian P.IVA, PEC and REA, German Impressum (USt-IdNr., Handelsregister), French SIREN/SIRET, Spanish CIF, Austrian FN and UK company numbers.

**You pay only for websites where data is found. $5 per 1,000 websites.** Unreachable, blocked or empty websites are free.

### What can you use it for?

- **Enrich lead lists.** Paste the websites from Google Maps, a trade-fair exhibitor list or your CRM, and get back emails, phones and LinkedIn profiles in one table.
- **Verify suppliers and customers (KYB).** Get the VAT ID and legal name that each company publishes on its own website, already checked with the official check-digit algorithm, ready for a VIES lookup.
- **Clean B2B invoicing data.** Italian companies need the PEC address or SDI code for e-invoicing, and this Actor finds the PEC. German companies need the Handelsregister entry, and this Actor finds it too.
- **Market mapping.** Find out which companies in a list are Italian, German or French S.r.l./GmbH/SAS, and how to reach them.
- **Stay GDPR-friendly by default.** Only company mailboxes (info@, sales@, kontakt@, amministrazione@ …) are returned unless you explicitly ask for all emails.

### Example output

One row per website (real result, shortened):

```json
{
  "domain": "kartell.com",
  "status": "ok",
  "companyName": "KARTELL S.p.A.",
  "legalName": "KARTELL S.p.A.",
  "country": "IT",
  "emails": ["privacy@kartell.it"],
  "pecEmails": [],
  "phones": [],
  "vatIds": ["IT11349160157"],
  "vatIdDetails": [{ "vatId": "IT11349160157", "country": "IT", "checksumValid": true }],
  "reaNumber": "MI-2726347",
  "linkedin": "https://it.linkedin.com/company/kartell",
  "facebook": "https://www.facebook.com/Kartell",
  "instagram": "https://www.instagram.com/kartell_official",
  "x": "https://twitter.com/kartellofficial",
  "youtube": "https://www.youtube.com/user/kartellpeople",
  "tiktok": "https://www.tiktok.com/@kartell_official",
  "pagesCrawled": 5
}
```

Other real rows from the test runs:

| Domain | Legal name | VAT ID | Registry | Emails | PEC |
|---|---|---|---|---|---|
| bosch.de | — | DE811128135 | Amtsgericht Stuttgart HRB 14000 | kontakt@bosch.de | — |
| manufactum.de | Manufactum GmbH | DE315189509 | Recklinghausen HRB 7982 | info@manufactum.de | — |
| venchi.com | Venchi S.p.A. | IT05744670968 | REA MI-1862619 | customercare@venchi.com | venchiamministrazione@pec.it |
| elica.com | Elica S.p.A. | IT00096570429 | REA AN-63006 | privacy@elica.com | elicaspa@sicurezzapostale.it |
| manner.com | Manner | ATU14390602 | FN 40643w | team@manner.com | — |
| mahou.es | Mahou, S.A. | ESA28078202 | — | contacta@mahou.es | — |
| hema.nl | HEMA B.V. | NL814217412B01 | — | — | — |

### Why this Actor and not a generic contact scraper?

| | This Actor | Generic contact scrapers |
|---|---|---|
| VAT ID with check-digit validation (IT, DE, FR, ES, NL, BE, AT, PL, PT + format check for the rest of the EU, UK and CH) | ✅ | ❌ |
| Italian PEC, REA · German HRB/HRA · French SIREN/SIRET · Austrian FN · UK company number | ✅ | ❌ |
| Legal company name (S.r.l., GmbH, SAS, S.L., B.V. …) | ✅ | ❌ |
| Finds the legal-notice page automatically (Impressum, Note legali, Mentions légales, Aviso legal) | ✅ | partly |
| Decodes hidden emails (`info [at] firma [dot] de`, Cloudflare email protection) | ✅ | partly |
| GDPR mode: company mailboxes only | ✅ default | ❌ |
| One clean row per website (not one row per page) | ✅ | ❌ |
| You pay only when data is found | ✅ | ❌ (per page) |

### How to use it

1. Paste your websites into **Company websites**, one per line. A domain (`example.it`) or a full URL both work.
2. Optionally, chain it after another Actor. Put the dataset ID of a **Google Maps Scraper** run (or any dataset with a `website` field) into **Or: read websites from a dataset**.
3. Click **Start**. Download the results as Excel, CSV or JSON, or get them through the API.

### Input

| Field | Default | What it does |
|---|---|---|
| `websites` | — | Domains or URLs, one per line |
| `sourceDatasetId` / `sourceDatasetField` | — / `website` | Read websites from another Actor's dataset |
| `maxPagesPerWebsite` | 6 | Homepage + legal notice, contact, about and privacy pages |
| `emailMode` | `generic` | `generic` = company mailboxes only. `all` = also named people (only with a lawful basis) |
| `includeWebsitesWithoutData` | `true` | Also return a free row explaining why nothing was found |
| `respectRobotsTxt` | `true` | Skip pages that robots.txt disallows |
| `maxConcurrency` | 20 | Pages downloaded in parallel |
| `proxyConfiguration` | off | Turn on Apify Proxy only if many websites come back as `blocked` |

### Output fields

`website`, `domain`, `url`, `status`, `companyName`, `legalName`, `country`, `emails`, `personalEmailsHidden`, `pecEmails`, `phones`, `vatIds`, `vatIdDetails` (with `checksumValid`), `reaNumber`, `commercialRegister`, `siren`, `siret`, `ukCompanyNumber`, `address`, `linkedin`, `facebook`, `instagram`, `x`, `youtube`, `tiktok`, `contactPageUrl`, `legalPageUrl`, `pagesCrawled`, `errorMessage`, `scrapedAt`.

`status` tells you what happened:

| status | meaning | charged? |
|---|---|---|
| `ok` | data found | yes |
| `no-data` | website works but publishes no contacts or IDs | no |
| `unreachable` | domain does not exist, timeout, connection refused | no |
| `blocked` | the website blocks automated visits (try with Apify Proxy) | no |
| `js-only` | the website is built only with JavaScript and shows no content to a normal HTTP request | no |
| `parked` | the domain is parked or for sale | no |
| `robots-disallowed` | the website's robots.txt does not allow crawling | no |

### Pricing

**$5 per 1,000 websites with data** (`website-with-data` event, $0.005 each). Rows without data are free. Set **Max cost per run** in the run options and the Actor stops exactly there: you never pay more.

Example: 2,000 websites from a Google Maps search where 1,500 have data costs **$7.50**.

### Good to know

- **Accuracy of VAT IDs.** IDs from countries with a check-digit algorithm are returned only when the check passes (`checksumValid: true`). For other countries the ID is returned only when it appears next to a VAT label (`checksumValid: null`). The check proves the number is well formed. Run a VIES lookup if you need to know that it is currently active.
- **Group websites.** Big brands sometimes show the VAT ID of the local subsidiary for your country (e.g. a `.com` that redirects by location).
- **Websites built only with JavaScript** return `js-only`. Most small and mid-sized company websites work fine.
- **Legal.** The Actor reads only public pages of company websites, respects robots.txt and returns company mailboxes by default. You are responsible for how you use the data, for example under GDPR and anti-spam rules when you contact people.

### FAQ

**Can I process 100,000 websites?** Yes. Split very large lists into runs of 10,000–50,000 for easier monitoring.

**Do I need a proxy?** Usually not. Company websites rarely block. Turn on Apify Proxy if you see many `blocked` rows.

**Something is missing or wrong?** Open an issue on the Actor page with the website. Issues are usually answered within a day.

# Actor input Schema

## `websites` (type: `array`):

Domains or URLs of the companies to process, one per line (e.g. `ferrero.com` or `https://www.example.it`). You get one result row per website.

## `sourceDatasetId` (type: `string`):

ID of an Apify dataset to read websites from, for example the output of Google Maps Scraper. Combined with the list above.

## `sourceDatasetField` (type: `string`):

Name of the field that holds the website URL in each dataset item.

## `maxPagesPerWebsite` (type: `integer`):

The homepage plus the most useful sub-pages (legal notice / Impressum, contact, about us, privacy). 4–6 is enough for almost every company site.

## `emailMode` (type: `string`):

`generic` returns only company mailboxes such as info@, sales@, contatti@, kontakt@ (GDPR-friendly). `all` also returns emails that look like a named person (mario.rossi@...). Only choose `all` if you have a lawful basis to process them.

## `includeWebsitesWithoutData` (type: `boolean`):

Adds a row (with the reason, e.g. unreachable or blocked) for websites where no contact or legal data was found. These rows are free.

## `respectRobotsTxt` (type: `boolean`):

Skip pages that the website's robots.txt does not allow to crawl. Recommended.

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

How many pages are downloaded in parallel (across different websites).

## `proxyConfiguration` (type: `object`):

Usually not needed: most company websites do not block. Turn on Apify Proxy if many websites come back as `blocked`.

## Actor input object example

```json
{
  "websites": [
    "https://www.ferrero.it",
    "https://www.bosch.de",
    "https://www.esselunga.it"
  ],
  "sourceDatasetField": "website",
  "maxPagesPerWebsite": 6,
  "emailMode": "generic",
  "includeWebsitesWithoutData": true,
  "respectRobotsTxt": true,
  "maxConcurrency": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "websites": [
        "https://www.ferrero.it",
        "https://www.bosch.de",
        "https://www.esselunga.it"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("eurodata-tools/eu-company-contacts").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 = {
    "websites": [
        "https://www.ferrero.it",
        "https://www.bosch.de",
        "https://www.esselunga.it",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("eurodata-tools/eu-company-contacts").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 '{
  "websites": [
    "https://www.ferrero.it",
    "https://www.bosch.de",
    "https://www.esselunga.it"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call eurodata-tools/eu-company-contacts --silent --output-dataset

```

## MCP server setup

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

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/ynCPdoYM5pj4rdJC7/builds/iRkFUU9chXw1l8g14/openapi.json
