# GDPR Contact Enrichment API - Email Finder for Europe (`nabeelbaghoor/gdpr-contact-enrichment-api`) Actor

Enrich contacts and companies from an email, a profile URL, a website, or a name plus employer. Returns verified business emails, direct and mobile phones, job level and function, plus European company registration, VAT, activity code and headcount. Pay per result. Bring your own API key.

- **URL**: https://apify.com/nabeelbaghoor/gdpr-contact-enrichment-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$12.00 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## GDPR Contact Enrichment API - Email Finder for Europe

Enrich a list of contacts or companies from whatever you already have: an email address, a professional profile URL, a company website, or a person's name paired with their employer. Every row comes back with the verified business email, the direct and mobile phone numbers, the job title, level and function, and the company behind them, including European registration data. One flat row per contact.

### What this actor does

- **Works from four different starting points.** Addresses, profile URLs, company websites, or a name plus an employer all resolve into the same flat row shape, so lists that started in different systems end up in one table.
- **Returns European company registration data, not just firmographics.** Turn on the registry option and each company carries its registration number, establishment number, registered address, VAT number, activity code with its description, headcount and reported turnover. These are fields no email pattern guesser can produce.
- **Qualifies every address it returns.** Each address arrives with the provider's own qualification of it, and every alternative address found for the person is kept alongside the primary one, so you can apply your own sending policy rather than accepting one verdict.
- **Batches properly.** Up to 250 contacts go in one submission and are processed together, so a list of a thousand is four submissions rather than a thousand round trips. Each batch is written to the dataset as soon as it finishes, so a run stopped partway through still keeps everything it collected.
- **Waits the way the provider asks.** Submissions return a request id, not data. The actor polls with a widening gap and an overall deadline, recognises the provider's own "not ready yet" reply as a reason to keep waiting rather than a failure, and never leaves a stuck batch holding the run open.
- **Matches results back safely.** Results are paired to your inputs by position when the batch comes back whole, and by identifier when it does not, because attributing an enriched record to the wrong input is worse than reporting a miss.
- **Never charges for a miss.** Only rows carrying a resolved result are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Enrich from emails, profile URLs, company websites, or contact rows. |
| `emails` | Addresses to enrich. |
| `profileUrls` | Professional profile URLs to enrich. |
| `websites` | Company websites or domains to enrich. |
| `contacts` | Contact rows in the provider's own shape, for name plus employer. |
| `includeCompanyRegistry` | Add registration number, VAT, activity code and turnover. |
| `language` | Language for descriptive fields such as the activity description. |
| `batchSize` | Contacts per batch, up to the provider's maximum of 250. |
| `batchTimeoutMinutes` | How long to wait for one batch before giving up. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave unresolved contacts out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "customRows",
  "query": "Jane Doe at Example Labs",
  "email": "jane.doe@example.com",
  "emailQualification": "nominative@pro",
  "allEmails": [
    { "email": "jane.doe@example.com", "qualification": "nominative@pro" }
  ],
  "civility": "Ms",
  "firstName": "Jane",
  "lastName": "Doe",
  "fullName": "Jane Doe",
  "position": "Directrice Marketing",
  "jobLevel": "executive",
  "jobFunction": "marketing",
  "phone": "+33123456789",
  "mobilePhone": "+33612345678",
  "linkedin": "https://www.linkedin.com/in/janedoe",
  "organization": "Example Labs",
  "website": "example.com",
  "companyLinkedin": "https://www.linkedin.com/company/example-labs",
  "companyRegistrationNumber": "552100554",
  "companyEstablishmentNumber": "55210055400015",
  "companyRegisteredAddress": "12 Rue de Example, 75002 Paris",
  "vatNumber": "FR40552100554",
  "employees": 240,
  "activityCode": "62.01Z",
  "activityDescription": "Computer programming activities",
  "industry": "Software",
  "companyTurnover": 41200000,
  "companyResults": 3100000,
  "country": "France",
  "customFields": null,
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What makes this different from a generic email finder?

Two things. First, the identifiers it accepts: as well as an address or a profile URL, it takes a person's name paired with their employer, which is the form most exported CRM lists are already in, and it takes a company registration number. Second, what comes back with the person: the European company register data, meaning the registration number, establishment number, registered address, VAT number, activity code, headcount and reported turnover. Most finders return an address and a job title and stop there.

#### Which fields identify a contact well enough to enrich it?

Any one of these combinations works: a first and last name plus a company, a full name plus a company, a professional profile URL, or an email address. Adding more than the minimum makes the match better rather than worse, so include the website, the country or the job title when you have them. In contact rows mode you can send all of them together on the same row.

#### What does the email qualification field mean?

It is the provider's own classification of the address it found, which distinguishes a personal work address belonging to a named individual from a generic role mailbox. It matters because the two call for completely different treatment: a nominative professional address is a person you can write to, while a generic one is a shared inbox. Every alternative address the provider found is kept alongside the primary one so you can apply your own policy.

#### Is this suitable for GDPR compliant prospecting?

The provider is built around European data protection practice and processes contacts in Europe, which is why it is a common choice for teams whose legal position requires it. That said, compliance is about your own basis for processing and how you use the data, not only about where it is processed. This actor gives you the provider's output; the lawful basis for contacting the people in it remains yours, and your own agreement with the provider governs what you may do with the results.

#### Why does a run take longer than a request to a normal API?

Because this provider is asynchronous by design. A batch is submitted, the provider works through it, and the results are collected afterwards. The actor handles all of that for you: it polls with a widening gap rather than continuously, understands the provider's own "not ready yet" reply as a reason to keep waiting, and stops on a deadline you control. The practical consequence is that bigger batches are better, not worse, since the wait is paid once per batch rather than once per contact.

#### Should I change the batch size?

Usually not. The default of 250 is the provider's maximum and almost always the right choice, because a batch is processed as a unit and a larger one does not take proportionally longer. Lower it only if you want partial results written to the dataset sooner, since each batch is saved as soon as it finishes.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own API key from Dropcontact, which is the provider whose enrichment API this actor calls, created on their dashboard. Your own plan, credit balance and terms apply. Paste the key into the `apiKey` field, where it is stored as an Apify secret and sent in a dedicated request header rather than a URL parameter, so it never appears in a log line or a redirect.

#### What happens when a contact cannot be enriched?

The row is written with `found: false` and an `error` explaining that the provider had nothing for that input, and the run continues. This provider reports an unresolvable contact inside a successful batch with its fields empty rather than as an error, so the actor recognises that shape and records it as a miss instead of treating the whole batch as failed. Misses are not charged for. Set `skipNotFound` to true to leave them out of the dataset entirely.

#### How much does a run cost?

Pricing is pay per result: you are charged for each contact or company enriched into the dataset, and never for inputs the provider could not resolve, or for duplicates. Apify platform usage is included in the per-result price. Your own API provider's credit balance is separate and billed by them.

### Keyword map

GDPR email finder, contact enrichment API, B2B email finder Europe, French company data API, SIREN lookup, SIRET lookup, VAT number lookup, NAF activity code, company registry enrichment, bulk contact enrichment, mobile phone finder, direct dial lookup, CRM data enrichment, lead enrichment API, email verification, job level and function, company turnover data, headcount data, LinkedIn profile enrichment, sales prospecting data

# Actor input Schema

## `mode` (type: `string`):

Email enrichment starts from a list of addresses. Profile enrichment starts from professional profile URLs. Company enrichment starts from company websites. Contact rows lets you send the provider's own contact shape, which is the only way to pair a person's name with a specific employer or to start from a company registration number.

## `emails` (type: `array`):

Email addresses to enrich, one per line. An address pasted inside a longer string, such as a mail header or a name and address pair, is extracted before the request.

## `profileUrls` (type: `array`):

Public professional profile URLs, one per line. A bare handle is expanded to a full URL and tracking parameters are stripped, so a list copied out of a browser still works.

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

Company websites or domains, one per line. A pasted URL is reduced to the bare hostname. Company enrichment returns the registration data, the activity code and the headcount behind each one.

## `contacts` (type: `array`):

Contacts in the provider's own shape, as a list of objects. Each row needs one workable combination: a first and last name plus a company, a full name plus a company, a profile URL, or an email address. Optional keys are website, company\_linkedin, phone, job, country, num\_siren, siret and custom\_fields. Contact rows mode only.

## `includeCompanyRegistry` (type: `boolean`):

Ask the provider to resolve the company against the French business register, which adds the registration number, the establishment number, the registered address, the VAT number, the activity code and the reported turnover. Worth turning on for any list with French companies in it, and harmless otherwise.

## `language` (type: `string`):

Which language the provider should return descriptive fields in, such as the activity description on a company. Leave on the provider default unless you have a reason to change it.

## `batchSize` (type: `integer`):

How many contacts to submit in one batch. The provider allows up to 250, which is the default and almost always the right choice: a batch is processed asynchronously, so larger batches mean fewer waits rather than longer ones.

## `batchTimeoutMinutes` (type: `integer`):

How long to wait for one batch to finish before giving up on it. Raise it for very large batches on a busy account. The actor polls with a widening gap rather than continuously, so a longer timeout does not mean more requests.

## `requestsPerMinute` (type: `integer`):

Pace the run so it stays under whatever rate your plan allows. This covers both the submissions and the polls.

## `skipNotFound` (type: `boolean`):

Leave contacts the provider could not enrich out of the dataset. Off by default, because keeping the misses is what lets you see which inputs resolved and which did not. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. This is the cap on both spend and run time.

## `apiKey` (type: `string`):

Your own API key for the contact enrichment API, sent in a dedicated request header rather than a URL parameter, so it never appears in a log line or a redirect. Required for every request. Stored as a secret.

## Actor input object example

```json
{
  "mode": "emailEnrich",
  "emails": [
    "jane@stripe.com"
  ],
  "contacts": [
    {
      "first_name": "Jane",
      "last_name": "Doe",
      "company": "Stripe"
    }
  ],
  "includeCompanyRegistry": false,
  "language": "en",
  "batchSize": 250,
  "batchTimeoutMinutes": 15,
  "requestsPerMinute": 60,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

Enriched contacts and companies with their verified addresses and registration data, one row each.

# 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 = {
    "emails": [
        "jane@stripe.com"
    ],
    "contacts": [
        {
            "first_name": "Jane",
            "last_name": "Doe",
            "company": "Stripe"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/gdpr-contact-enrichment-api").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 = {
    "emails": ["jane@stripe.com"],
    "contacts": [{
            "first_name": "Jane",
            "last_name": "Doe",
            "company": "Stripe",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/gdpr-contact-enrichment-api").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 '{
  "emails": [
    "jane@stripe.com"
  ],
  "contacts": [
    {
      "first_name": "Jane",
      "last_name": "Doe",
      "company": "Stripe"
    }
  ]
}' |
apify call nabeelbaghoor/gdpr-contact-enrichment-api --silent --output-dataset

```

## MCP server setup

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

```

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/qPlIa3UjQKecIGccT/builds/1qAnY0ctkF8PxbeWY/openapi.json
