# Europages Scraper: B2B Suppliers & Certificates (No Login) (`cliqtomedia/europages-companies-scraper`) Actor

Find B2B suppliers, manufacturers and wholesalers on Europages by keyword, country and company type: website, address, employees, founding year, certificates, activities, keywords, VAT ID and company phone. No login.

- **URL**: https://apify.com/cliqtomedia/europages-companies-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (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 $4.50 / 1,000 companies

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

Search Europages, the European B2B directory, by product keyword, country and company type. Get one row per supplier with website, address, employees, founding year, certificates, activities, VAT ID and company phone.

Use it to build supplier and manufacturer lists for sourcing, sales or market research. The smallest run is one keyword and one country: `packaging` in `DE` returns 20 German packaging companies in up to 4 minutes. No login and no account on Europages are needed.

![Europages B2B Supplier Scraper: search by keyword, country and company type, get company profiles with certificates and VAT ID, export as JSON or CSV](https://api.apify.com/v2/key-value-stores/zo1LWl0el0mV5cmPD/records/readme-banner-20261015.png)

### Table of contents

- [What it does](#what-it-does)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing and limits](#pricing-and-limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Support](#support)

### What it does

The Actor runs a Europages search for every keyword in every country you choose. It reads the result pages (30 companies per page). Then it opens each company profile and adds certificates, activities, product categories, VAT ID and the company phone. Each company is one row in the dataset.

Data source: public search and company pages of [Europages](https://www.europages.co.uk/), read without login. This Actor is not affiliated with Europages or Visable GmbH. Company texts, logos and trademarks belong to their owners. You are responsible for using the data in line with GDPR and the [Europages terms](https://www.europages.co.uk/company/legal-notice/terms-of-use).

#### Good fit

- Find manufacturers or wholesalers of a product in one or more European countries.
- Build a supplier shortlist with website, size (employees) and founding year.
- Check which suppliers list a certificate such as ISO 9001 or ISO 14001.
- Enrich a lead list with the company VAT ID and main phone number.

#### Not a fit / limits

- **No e-mail addresses and no personal contacts.** The Actor returns only the company's main phone number. E-mail addresses in company descriptions are replaced with `[email removed]`.
- **Europages search is fuzzy.** It also returns companies that only match part of a word, or a related term. Even a nonsense word can return a few companies. Check `keywords`, `activities` and `mainActivity` if you need exact matches.
- **Up to 1,000 companies per search** (one keyword in one country) and **up to 20 searches per run**.
- Fields depend on what each company publishes. Many companies have no certificates, VAT ID or activities.
- The Actor does not promise a complete list of all companies in a country. It returns what Europages search shows.

### Quick start

1. Open the **Input** tab.
2. Add a product keyword in **Search terms** (`queries`), for example `packaging`.
3. Choose one or more **Countries** (`countries`), for example `DE`. Leave it empty to search all of Europe.
4. Keep **Max companies per search** (`maxItems`) at the prefill `20` for a first test.
5. Click **Start**.
6. Open the **Output** tab and check the **Companies** view: `name`, `country`, `city`, `website`, `companyType`.

Expected result: 20 rows of German packaging companies, each with `detailStatus` = `complete`.

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next step |
| --- | --- | --- | --- | --- |
| Find manufacturers of a product | `queries`, `countries`, `companyType` = `production` | Company | `name`, `website`, `country`, `employees` | Export to CSV or Google Sheets |
| Supplier check by certificate | `queries`, `includeDetails` = `true` | Company | `certifications`, `foundedYear`, `distributionArea` | Filter rows by `certifications` |
| Lead list for B2B sales | `queries`, `countries` | Company | `website`, `phone`, `vatId`, `city` | Import to CRM via API or integration |
| Quick market count | `queries`, `includeDetails` = `false` | Company (search result only) | `name`, `country`, `companyType` | Count rows per country |

### Input

#### Input fields

| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example | Recommendation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `queries` | Search terms | array of strings | yes | prefill `["packaging"]` | 1–20 terms, 2–100 characters each | Each term is a separate Europages search | `["led street lighting"]` | Use English product words |
| `countries` | Countries | array of country codes | no | prefill `["DE"]`; empty = all countries | 44 Europages countries (`DE`, `FR`, `IT`, `PL`, ...) | Each country is a separate search for every term | `["DE", "PL"]` | Leave empty for one Europe-wide search |
| `companyType` | Company type | string | no | `any` | `any`, `production`, `customer_specific_manufacturing`, `distribution`, `wholesaler`, `service` | Europages keeps only companies of this type | `production` | A company can have several types |
| `maxItems` | Max companies per search | integer | no | default `100`, prefill `20` | 1–1000 | Stops each search after this many companies | `200` | Start small, then raise it |
| `includeDetails` | Open company profiles | boolean | no | `true` | `true`, `false` | `true` adds certificates, activities, VAT ID, phone; `false` is faster | `false` | Turn off for quick counts |

Duplicates are removed: if two searches find the same company, it is saved and charged only once.

#### Interacting settings

| When | Input A | Input B | Effective behavior | Example | Cost/result effect |
| --- | --- | --- | --- | --- | --- |
| Several terms and countries | `queries` | `countries` | One search per term × country; at most 20 per run | 4 terms × 5 countries = 20 searches | Up to 20 × `maxItems` rows |
| No countries | `queries` | `countries` empty | One search per term over all countries | `["valves"]`, `[]` | Fewer searches, mixed countries |
| Profiles off | `includeDetails` = `false` | — | Rows come from search pages only; `detailStatus` = `listing_only` | — | Same price per row, much faster |
| Too many searches | `queries` | `countries` | Run stops with `INVALID_INPUT` before any search | 7 terms × 3 countries = 21 | Only the start fee |

#### Input examples

##### German packaging manufacturers

```json
{
  "queries": ["packaging"],
  "countries": ["DE"],
  "companyType": "production",
  "maxItems": 50
}
```

Expected behavior: one Europages search, two result pages, 50 profiles opened.
Expected output: 50 company rows with `name`, `website`, `employees`, `certifications`, `vatId`.
Limit or cost note: 50 companies plus one start fee.

##### Valve suppliers in two countries, search results only

```json
{
  "queries": ["valves"],
  "countries": ["DE", "PL"],
  "maxItems": 200,
  "includeDetails": false
}
```

Expected behavior: two searches, no profile pages.
Expected output: up to 400 rows with `detailStatus` = `listing_only`.

### Output

#### Output fields

| Field | Meaning | Type | Can be empty? | Example / enum | Notes |
| --- | --- | --- | --- | --- | --- |
| `companyId` | Europages company ID, stable across runs | string | no | `21241677` | Use it to remove duplicates |
| `name` | Company name | string | no | `RHEIN-PLAST GMBH` | |
| `url` | Company profile on Europages | string | rarely | `https://www.europages.co.uk/en/company/...` | |
| `website` | Company's own website | string | yes | `https://www.rhein-plast.de/` | |
| `country`, `city`, `street`, `zipcode` | Company address | string | yes | `DE`, `Bad Dürkheim` | `country` is ISO 3166-1 alpha-2 |
| `latitude`, `longitude` | Address coordinates | number | yes | `49.477384` | |
| `companyType` | Company types | array | can be empty | `production`, `wholesaler` | |
| `employees` | Employee range | string | yes | `50-99` | |
| `foundedYear` | Year founded | integer | yes | `1959` | |
| `certifications` | Certificate names | array | yes | `["ISO 9001:2015"]` | `null` without profile details |
| `certificationsCount` | Number of certificates | integer | yes | `2` | |
| `vatId` | VAT ID, when the company publishes it | string | yes | `DE149372181` | |
| `phone` | Company's main phone | string | yes | `+4963227980` | No personal contacts |
| `mainActivity` | Main business area | string | yes | `Packaging services` | Profile only |
| `activities` | Europages categories, up to 15 | array | can be empty | | Profile only |
| `keywords` | Business keywords, up to 50 | array | can be empty | `Bubble wrap films` | Profile only |
| `productCategories` | Product catalogue categories | array | can be empty | `Flat Pouches` | Profile only |
| `distributionArea` | Where the company sells | string | yes | `national`, `europe`, `international` | |
| `description` | Company description | string | yes | | E-mails replaced with `[email removed]` |
| `query`, `searchCountry`, `searchRank` | Which search found the company and its position | string / integer | `searchCountry` is null for all countries | `packaging`, `DE`, `1` | |
| `detailStatus` | Profile result | string | no | `complete`, `listing_only`, `detail_error` | See below |
| `detailError` | Why the profile could not be read | string | yes | | Only for `detail_error` |
| `fetchedAt` | When the row was collected | string | no | ISO 8601, UTC | |

Other fields: `uuid`, `activitiesTotal`, `productCount`, `logoUrl`, `source` (always `europages`).

`detailStatus` values:

- `complete` — search result and profile were read.
- `listing_only` — `includeDetails` was `false`; profile fields are empty.
- `detail_error` — the profile page could not be read. The row keeps the search-result fields. **This row is not charged.**

#### Output entities and surfaces

| Situation | Where | Result shape | What to do |
| --- | --- | --- | --- |
| Normal company | Dataset, view **Companies** | One row per company | Open, export or read via API |
| No companies found | Dataset empty; `RUN_SUMMARY` status `empty`, reason `NO_RESULTS` | No rows | Try a broader keyword or more countries |
| Some profiles not read | Dataset rows with `detailStatus` = `detail_error`; reason `DETAILS_MISSING` | Search-result fields only | Run again later for those companies |
| Run stopped early | `RUN_SUMMARY` status `partial` (`CHARGE_LIMIT`, `INTERNAL_LIMIT`, `SOURCE_ERROR`) | Rows saved before the stop are kept | Read `message`; use fewer searches or a lower `maxItems` |
| Invalid input | Run fails; `RUN_SUMMARY` reason `INVALID_INPUT` | No rows | Fix the input from the message |

The key-value store record `RUN_SUMMARY` has the run status and reason, counts per search and request statistics.

#### Output example

```json
{
  "companyId": "21241677",
  "name": "RHEIN-PLAST GMBH",
  "url": "https://www.europages.co.uk/en/company/rhein-plast-gmbh-21241677",
  "website": "https://www.rhein-plast.de/",
  "country": "DE",
  "city": "Bad Dürkheim",
  "street": "Altenbacher Strasse 24-26",
  "zipcode": "67098",
  "mainActivity": "Packaging services",
  "keywords": ["Anti-static film", "Bags with adhesive sealing", "Clean room packaging"],
  "productCategories": ["Flat Pouches", "Pharmaceutical Packaging"],
  "companyType": ["production"],
  "employees": "50-99",
  "foundedYear": 1959,
  "certifications": [],
  "certificationsCount": 0,
  "vatId": "DE149372181",
  "phone": "+4963227980",
  "distributionArea": "international",
  "query": "packaging",
  "searchCountry": "DE",
  "searchRank": 1,
  "detailStatus": "complete",
  "detailError": null,
  "source": "europages",
  "fetchedAt": "2026-10-15T00:13:29.645Z"
}
```

This example is trimmed and shows one company from the `packaging` / `DE` run. An empty array means the profile has no such data. `null` means the field was not available (for example, profiles were turned off).

### Pricing and limits

#### Pricing unit

You pay per event:

- **Actor start:** $0.005 per run (one event per GB of memory; the default 512 MB is one event).
- **Company:** one event for each saved company row. The price depends on your Apify plan:

| Plan | Price per company | Per 1,000 companies |
| --- | ---: | ---: |
| Free | $0.0054 | $5.40 |
| Bronze | $0.0051 | $5.10 |
| Silver | $0.0048 | $4.80 |
| Gold | $0.0045 | $4.50 |

Platform usage (compute, proxy) is included. A run that finds no companies, or a filter with no matches, costs only the start fee. Rows with `detailStatus` = `detail_error` are not charged. Duplicates are not charged twice. You can set **Max cost per run** in run options: the Actor stops when the next row would go over it (reason `CHARGE_LIMIT`).

#### Cost examples (Free plan)

| Scenario | Input scope | Rows | Start | Rows charge | Total |
| --- | --- | ---: | ---: | ---: | ---: |
| First test | `packaging` / `DE` / 20 | 20 | $0.005 | $0.108 | $0.113 |
| Normal | 1 term / 1 country / 100 | 100 | $0.005 | $0.54 | $0.545 |
| Large | 1 term / 1 country / 1,000 | 1,000 | $0.005 | $5.40 | $5.405 |
| Nothing found | any | 0 | $0.005 | $0 | $0.005 |

#### Limits and run time

- 20 searches per run, 1,000 companies per search, 20 terms per run.
- One run works for at most 1 hour. With profiles this is about 1,500 companies per run, so 20 searches × 1,000 companies do not fit in one run.
- 20 companies with profiles: about 20 seconds to 4 minutes. 1,000 companies with profiles: about 40 minutes.
- Default memory 512 MB; default timeout 3,600 seconds. For big runs keep the timeout at 3,600 seconds or more.

### API, export and integrations

- **API:** start the Actor with the same JSON input and read rows from the default dataset (`/items?view=companies`).
- **Export:** download the dataset as JSON, CSV, Excel, XML or HTML from the **Output** tab.
- **Scheduling and integrations:** use Apify schedules, webhooks, or integrations such as Google Sheets, Make and Zapier to send new rows to your tools.

#### Use with AI agents (MCP)

- MCP server with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/europages-companies-scraper`
- Smallest useful input: `{"queries": ["packaging"], "countries": ["DE"], "maxItems": 20}`
- Key output fields: `name` (company), `website` (own site), `country` (ISO code), `companyType` (manufacturer, wholesaler, ...), `certifications` (certificate names).
- Typical run: 20 companies in 20 seconds to 4 minutes, about $0.11 on the Free plan. Limits: 20 searches per run, 1,000 companies per search, about 1,500 companies with profiles per run (1 hour).

### Troubleshooting

#### The run returned companies that do not match my keyword

Symptom: some rows are about a different product.
Likely reason: Europages search is fuzzy and also matches related words.
Check: look at `keywords`, `mainActivity` and `searchRank`. The best matches come first.
Action: use a more specific keyword, a lower `maxItems`, or filter rows after export.

#### The run failed with "at most 20 per run"

Symptom: status `INVALID_INPUT`, message `queries × countries gives N searches`.
Likely reason: too many terms × countries.
Action: split the job into several runs, or leave `countries` empty for one Europe-wide search per term.

#### Some rows have `detailStatus` = `detail_error`

Symptom: certificates, activities, VAT ID are empty for a few rows.
Likely reason: the profile page did not load in time.
Action: these rows are free. Run the same search again later to fill them. If most profiles fail (10 in a row, or more than half), the run stops early with `SOURCE_ERROR` so you do not wait for free rows.

#### The run stopped with `INTERNAL_LIMIT`

Symptom: status `partial`, fewer rows than expected.
Likely reason: the run reached the 1 hour limit. The Actor stops itself after 1 hour, even with a longer timeout.
Action: split the job: use fewer searches or a lower `maxItems` per run.

### FAQ

#### Do I need a Europages account or login?

No. The Actor reads public Europages pages. You need only an Apify account.

#### Does it return e-mail addresses?

No. It returns the company's main phone number and website. It does not collect personal contacts or e-mails.

#### Why does the same keyword give a slightly different list on another day?

Europages changes its ranking and its company data. The Actor returns what Europages shows at run time. Use `companyId` to compare runs.

#### What happens if nothing is found?

The run ends with status `empty` and reason `NO_RESULTS`. You pay only the start fee.

### Support

For bugs, questions or feature requests, open an issue in the **Issues** tab of this Actor. Include the run ID and your input. Never send tokens, passwords or billing data.

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/europages-companies-scraper/changelog.md

# Actor input Schema

## `queries` (type: `array`):

Products or services to search for, in English, e.g. packaging, led street lighting or cnc machining. Each term is a separate Europages search. Up to 20 terms, each 2 to 100 characters.

## `countries` (type: `array`):

Only companies based in these countries. Each country is a separate search for every term (at most 20 term × country searches per run). Leave empty to search all countries.

## `companyType` (type: `string`):

Only companies of this type, filtered by Europages itself. A company can have several types.

## `maxItems` (type: `integer`):

Stop each search (one term in one country) after this many companies. Europages shows 30 companies per page. You pay only for companies saved.

## `includeDetails` (type: `boolean`):

Open each company profile for certificates, activities, product categories, main business area, VAT ID and company phone. Turn off for faster, search-result-only rows.

## Actor input object example

```json
{
  "queries": [
    "packaging"
  ],
  "countries": [
    "DE"
  ],
  "companyType": "any",
  "maxItems": 20,
  "includeDetails": true
}
```

# Actor output Schema

## `companies` (type: `string`):

One row per company: website, address, type, employees, founding year, certificates and activities.

## `runSummary` (type: `string`):

Run status and reason, counts per search and request statistics.

# 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 = {
    "queries": [
        "packaging"
    ],
    "countries": [
        "DE"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/europages-companies-scraper").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 = {
    "queries": ["packaging"],
    "countries": ["DE"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/europages-companies-scraper").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 '{
  "queries": [
    "packaging"
  ],
  "countries": [
    "DE"
  ],
  "maxItems": 20
}' |
apify call cliqtomedia/europages-companies-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/europages-companies-scraper"
        }
    }
}
```

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/cle6AIvtBpFmRZ61R/builds/oKAUG2hQxf3ziItuH/openapi.json
