# Europages B2B Scraper (`automation-lab/europages-b2b-company-directory-scraper`) Actor

Extract public Europages supplier company records, descriptions, locations, employee ranges and postal addresses for repeatable B2B sourcing research.

- **URL**: https://apify.com/automation-lab/europages-b2b-company-directory-scraper.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.44 / 1,000 item extracteds

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

## Europages B2B Scraper

Refresh supplier shortlists with public **Europages B2B** company records from the English directory. Search by supplier keyword, supply listing URLs, or revisit specific company profiles. Export names, stable profile URLs, countries, localities, employee-size ranges, descriptions and postal addresses when published.

### Who is it for?

Built for procurement researchers, sourcing analysts and data teams making repeatable company-directory snapshots. This is not an email, phone or VAT enrichment service.

### Why use this Actor?

- Collect structured supplier records without manually copying directory cards.
- Combine keywords and known company URLs in one bounded run.
- Deduplicate companies across those sources before saving or charging.
- Keep source URLs and timestamps for audit and downstream comparisons.
- Refresh company-page values optionally, without a separate detail charge.

Listing pages already expose many useful fields, including available descriptions and addresses. Keep detail fetching off unless you need profile-page refreshes. Missing fields are returned as null, not guessed.

### Getting started

1. Enter a supplier keyword such as `coffee`.
2. Set `maxItems` to a small useful shortlist, such as 5.
3. Leave company details off for a quick directory snapshot.
4. Run and open the default dataset.
5. Export JSON, CSV, Excel or another Apify-supported dataset format.

```json
{
  "searchTerms": ["coffee"],
  "maxItems": 5,
  "maxPagesPerSearch": 3,
  "includeCompanyDetails": false
}
```

### Inputs and matching behavior

| Input | Default | Behavior |
|---|---|---|
| `searchTerms` | None | Up to 20 separate English supplier-keyword searches |
| `startUrls` | None | Up to 20 public English directory listing/company URLs |
| `maxItems` | 50 | Global unique-company cap; 1–1000 |
| `maxPagesPerSearch` | 3 | Listing pages per source; 1–20 |
| `includeCompanyDetails` | false | Fetch each accepted company's public profile before saving |

Supply at least one keyword or URL. Keywords and URLs are additive, not competing modes. Sources run in input order; earlier sources can fill the global limit. Repeated URLs and company IDs are deduplicated within the run, not across runs.

Keywords are lowercased and whitespace is normalized into directory URL slugs. Ampersands become `and`; punctuation separators become hyphens. Only English letters, numbers, spaces, apostrophes, hyphens and ampersands are supported, up to 100 characters per keyword. Multiple terms mean separate searches (ANY source), not an ALL-words filter.

Europages controls search relevance: this is not exact phrase matching or whole-field equality. A supplier may match through its products rather than its name. Explicitly sponsored-only cards whose published title/description/category/product relevance flags are all zero are excluded. If those flags are unavailable, normal source results remain eligible. No arbitrary client-side text matcher is imposed.

### Supported URLs and pagination

Use HTTPS `www.europages.co.uk` URLs without query strings, fragments or credentials. URL objects accept only `url` and optional `method: "GET"`; custom headers, payload and user data are rejected, even if an MCP client displays additional request-list fields:

- Listings: `https://www.europages.co.uk/companies/coffee.html`
- Subsequent listings: `https://www.europages.co.uk/companies/pg-2/coffee.html`
- Legacy company profiles: `/COMPANY-NAME/COMPANY-ID.html`
- Current profiles: `/en/company/company-slug`

Supplied URLs are not filtered by `searchTerms`. A direct company URL fetches that profile even when `includeCompanyDetails` is false; the setting only changes listing-to-profile traversal.

Pagination starts from the supplied listing page. `maxPagesPerSearch` counts pages fetched from that point, independently for each distinct source. `maxItems` always applies globally after deduplication and relevance exclusions. Source exhaustion, billing limits or timeout may yield fewer rows. Zero and unlimited limits are unsupported; no setting guarantees complete Europages coverage.

### Extracted company data

| Field | Meaning |
|---|---|
| `companyId` | Source company identifier, when available |
| `name` | Public company display name |
| `profileUrl` | Stable public company profile URL |
| `country` | Source country code |
| `locality` | City/locality |
| `employeeSize` | Source employee range, not a verified headcount |
| `description` | Public description with HTML removed |
| `streetAddress` | Street or combined address lines |
| `postalCode` | Source postal code |
| `sourceUrl` | Listing or direct profile that produced the row |
| `searchTerm` | Input keyword, or null for supplied URLs |
| `pageNumber` | Page sequence within the source, starting at one |
| `detailsFetched` | Whether this row includes a company-page fetch |
| `scrapedAt` | UTC extraction timestamp |

No guaranteed phone, email, website, named-contact or VAT fields are emitted. Source-provided addresses and company claims can be incomplete or stale.

### Output example

This shortened record reflects the shape of a current coffee-directory result:

```json
{
  "companyId": "22264404",
  "name": "PRUVASYA Pet Products, Coffee and Hazelnut Export",
  "profileUrl": "https://www.europages.co.uk/SEZAI-OKAN/00000005549063-001.html",
  "country": "TR",
  "locality": "Ordu",
  "employeeSize": "5-9",
  "description": "My commercial life started in 2013 in the shoe industry...",
  "postalCode": "52400",
  "sourceUrl": "https://www.europages.co.uk/companies/coffee.html",
  "searchTerm": "coffee",
  "pageNumber": 1,
  "detailsFetched": false,
  "scrapedAt": "2026-10-21T06:05:19.774Z"
}
```

The actual record includes the full description and street address when published. An omitted field in this abbreviated example is not an extra paid feature.

### How much does it cost to extract Europages companies?

Pay per event: a **$0.001 start fee** plus one company-record event per saved unique company. No separate charge applies to optional company-page fetching. Duplicate, excluded and empty results do not incur company-record events. A valid run returning no companies can still incur the start fee; invalid input is rejected before charging.

BRONZE company-record price: **$0.0024 per company**. At this price, 5 companies cost $0.013, 25 cost $0.061, and 100 cost $0.241 including the start fee. These examples describe Actor event charges, not customer receipts or other services' prices.

| Store spend tier | Per saved company |
|---|---:|
| FREE | $0.00276 |
| BRONZE | $0.0024 |
| SILVER | $0.001872 |
| GOLD | $0.00144 |
| PLATINUM | $0.00144 |
| DIAMOND | $0.00144 |

These tiers follow qualifying aggregate monthly Apify Store spend, not a private per-Actor volume schedule. The $0.001 start fee is the same at every tier; consult the active pricing panel for current charges.

Use Apify's maximum charge setting to bound spending. If that limit prevents the next company-record charge, collection stops without saving that uncharged company. A timeout or failed upstream request can leave partial previously saved results; it is not a successful complete snapshot.

### Integrations and recurring shortlists

- Export CSV into procurement spreadsheets for manual qualification.
- Send JSON to a warehouse or CRM staging table keyed by `companyId`, falling back to `profileUrl`.
- Schedule repeated runs using Apify schedules, then compare timestamps/records downstream.
- Use webhooks or Make/n8n to process successful runs and flag failures separately.

Each run creates a fresh default dataset. This Actor does not maintain a cross-run watchlist, diff engine, history service or notification rule. Scheduling and change comparison are external workflow steps.

### API usage

Start one asynchronous run and retain the returned run ID:

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/automation-lab~europages-b2b-company-directory-scraper/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"searchTerms":["coffee"],"maxItems":5}'
```

JavaScript with `apify-client`:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/europages-b2b-company-directory-scraper')
  .call({ searchTerms: ['coffee'], maxItems: 5 });
if (run.status !== 'SUCCEEDED') throw new Error(`Run ${run.id}: ${run.status}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 5 });
console.log(items);
```

Python with `apify-client`:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/europages-b2b-company-directory-scraper').call(
    run_input={'searchTerms': ['coffee'], 'maxItems': 5})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(f"Run {run['id']}: {run['status']}")
print(client.dataset(run['defaultDatasetId']).list_items(limit=5).items)
```

### MCP use

Select this Actor explicitly rather than a growing category:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/europages-b2b-company-directory-scraper"
```

Claude Desktop, Cursor and VS Code clients supporting remote MCP can use this equivalent configuration (configure authentication securely in your client):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/europages-b2b-company-directory-scraper"
    }
  }
}
```

Example prompts: “Find five Europages coffee suppliers and show their company name, country, employee range and source URL. Do not enrich contacts.”

Discover actual tool names and schemas using `tools/list`; selecting an Actor also exposes run/dataset helpers, not exactly one tool. For an authorized invocation, start once, retain its run and dataset IDs, and follow the same run. `call-actor` waits at most 0–45 seconds (default 30). For a pending run, check `get-actor-run` with 2/4/8-second backoff capped at 10 seconds and a 120-second total deadline. Set client timeouts above the chosen server wait plus transport margin. Recover the known run after a timeout; never restart only to poll. At deadline, report pending and stop.

After success, fetch bounded dataset pages using `get-dataset-items`, explicit offsets/limits and needed fields. A practical consumer context budget is pages of 20 source rows, at most 100 rows and 64 KiB serialized UTF-8, whichever comes first. Enforce bytes host-side before model injection, including oversized rows; if the client cannot intercept responses, do not claim a hard byte guarantee. Report truncation, source continuation offset and partial status. Keep complete exports outside model context. Discovery caching depends on the client; no universal token-savings promise is made.

### Comparing supplier snapshots

Use `companyId` as the preferred join key and retain `profileUrl` as provenance. Compare records only after both runs succeed with equivalent inputs and limits. A missing company in a later bounded search may reflect ranking or pagination changes, not a business closure. Keep previous exports if you need history; this Actor does not retain snapshots across runs.

### Reliability and limitations

Requests run sequentially through company-paid Apify datacenter proxies, without login or a browser. Source challenges (403/405), HTTP 429, server errors and network failures have at most three attempts with fresh proxy identities and bounded backoff. Other non-200 statuses, redirects, unexpected content or exhausted challenges fail clearly instead of reporting a false empty success. Residential proxies are not enabled. Proxy infrastructure is included in the Actor's event prices.

The English directory may contain international suppliers and multilingual descriptions. Geographic coverage follows the supplied directory page; no unimplemented country filter is implied. Optional details can be slower and a failed required profile fetch fails the run rather than silently producing a listing-only substitute.

### Troubleshooting and FAQ

**Why fewer companies than requested?** The page limit, directory exhaustion, duplicates, explicitly irrelevant recommendations, billing cap or runtime limit may stop collection. Increase only the relevant bounded limit.

**Why did an unusual keyword return zero?** Europages may offer sponsored recommendations instead of relevant results. Explicit all-zero relevance recommendations are excluded. Try a real product category.

**Does this verify company data?** No. Records reflect public source values and should be checked before supplier decisions.

**Does it bypass access restrictions?** No private-account access or human CAPTCHA-solving is included. Bounded datacenter identity retries handle ordinary source throttling; exhausted challenges fail honestly.

**Can it find emails or VAT IDs?** No. This product deliberately focuses on repeatable supplier-directory snapshots.

### Legality and responsible use

Use public company information responsibly, comply with applicable laws and source terms, and verify your rights before downstream marketing or redistribution. Public availability does not remove privacy obligations. This Actor is independent and is not affiliated with Europages.

The Actor uses no AI at runtime and sends no scraped records to an AI provider. Company records are stored in the run's Apify dataset; the account's current platform retention is 31 days, subject to your plan and deletions. Delete datasets/runs through Apify to remove stored exports; delete your downstream copies separately. No cross-run company cache is kept. Use the Actor's Apify Issues tab for support.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; diagnostic reports are retained for 30 days. Do not put credentials or private personal data into keyword fields.

### Related Actors

For a separate supplier-directory source, see [Kompass Company Directory Scraper](https://apify.com/automation-lab/kompass-company-directory-scraper). It is an independent dataset, not an automatic fallback or an enrichment dependency of this Actor.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/europages-b2b-company-directory-scraper/changelog.md

# Actor input Schema

## `searchTerms` (type: `array`):

Up to 20 keywords, combined as separate directory searches (ANY source, not an ALL filter). Case and repeated whitespace are normalized; words are hyphenated for Europages listing slugs. English letters, numbers, spaces, apostrophes, ampersands and hyphens only; 1–100 characters each. Europages controls relevance, not exact phrase matching. Additive with start URLs; at least one source is required.

## `startUrls` (type: `array`):

Up to 20 HTTPS www.europages.co.uk /companies/...html listings or public company profiles (legacy /NAME/ID.html or /en/company/slug). No query strings, credentials or fragments. Objects accept only url and optional method GET; custom headers, payload and userData are rejected. These URLs add to keyword sources and are not filtered by searchTerms. Company URLs fetch that profile directly; listing URLs use the same page and global item limits.

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

Global maximum unique companies saved across all keywords and URLs, in input order. Default 50; 1–1000; zero/unlimited is unsupported. Duplicates do not consume this limit. Pagination caps, source exhaustion, billing limit or run timeout can return fewer.

## `maxPagesPerSearch` (type: `integer`):

Maximum listing pages fetched per distinct source URL/keyword, starting at its supplied page. Default 3; 1–20; no unlimited mode. Stops earlier at source exhaustion or the global maxItems limit. Ignored for direct company profiles.

## `includeCompanyDetails` (type: `boolean`):

When true, fetch each accepted listing company's public profile to refresh its fields before saving. Listing-only mode already includes available descriptions and postal addresses. Missing source values remain null. Direct company URLs always fetch the profile regardless of this setting. Detail failure fails the run; previously saved rows are partial. No email, phone or VAT enrichment.

## Actor input object example

```json
{
  "searchTerms": [
    "coffee"
  ],
  "maxItems": 5,
  "maxPagesPerSearch": 3,
  "includeCompanyDetails": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Company records in the 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 = {
    "searchTerms": [
        "coffee"
    ],
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/europages-b2b-company-directory-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 = {
    "searchTerms": ["coffee"],
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/europages-b2b-company-directory-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 '{
  "searchTerms": [
    "coffee"
  ],
  "maxItems": 5
}' |
apify call automation-lab/europages-b2b-company-directory-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/europages-b2b-company-directory-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/kkxfsVYAOfZpkwCFe/builds/hbWclYL1Lx1Iwo2g2/openapi.json
