# wlw.de Supplier Scraper — Phone, VAT ID & Contacts (DACH) (`scrapersdelight/wlw-supplier-scraper`) Actor

Industrial suppliers from wlw.de (Wer liefert was) by German product keyword: phone, website, address, VAT id, named contact persons, employee band, founding year, supplier type, certificates, products. Filter by DE/AT/CH, city, type. Deduped on wlw's id. $1.10 per 1,000 suppliers.

- **URL**: https://apify.com/scrapersdelight/wlw-supplier-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.10 / 1,000 per supplier returneds

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

## wlw.de Supplier Scraper — Phone, VAT ID & Contacts (DACH)

Turn a German product keyword into a list of **industrial suppliers from wlw.de ("Wer liefert was")** —
Germany's B2B supplier directory, run by Visable — with the contact data procurement and sales teams
actually use: **phone, website, street address, VAT id (USt-IdNr.), named contact persons with their role**,
plus employee band (Mitarbeiter), founding year (Gründungsjahr), supplier type (Hersteller, Großhändler,
Dienstleister …), certificates (ISO 9001, EN 1090, IATF 16949 …) and the products each supplier lists.

**One row = one supplier profile.** Deduplicated on wlw's own company id, so a supplier is never delivered or
charged twice — not across pages, keywords, countries or supplier types.

**$1.10 per 1,000 suppliers** ($0.0011 per supplier). No start fee. You pay only for suppliers delivered.

### Who uses it

- **Procurement and sourcing teams** building a longlist of DACH manufacturers for a part or process
  ("CNC-Fräsen", "Kunststoffspritzguss", "Blechbearbeitung") with size, founding year and certifications
  to pre-qualify on.
- **B2B lead-generation agencies** selling into the German Mittelstand: a named managing director and a
  direct phone on most rows.
- **MRO distributors and machinery vendors** mapping every manufacturer of a product type in a region.

### What you get — measured

Fill rates below are **measured on real runs on 2026-09-23**, not promised. They vary by search, so each
field is quoted as the range across the searches we ran (first-N rows of four searches with the profile
pass: CNC-Fräsen 300, Metallbau 300, Kunststoffspritzguss/DE 150, Verpackungsmaschinen/AT 150; and three
complete searches read end-to-end: CNC-Fräsen/CH 454, Verpackungsmaschinen/AT 428, Drehteile/CH 711).

| Field | What it is | Fill |
|---|---|---|
| `phone` | Company phone as wlw publishes it (international format, verbatim) | **98.7–100%** |
| `website` | Company website | **98.9–100%** |
| `street`, `postalCode`, `city`, `countryCode` | Postal address | 100% |
| `latitude`, `longitude` | Geo-coordinates of the address | 99.3–100% |
| `vatId` | VAT id (USt-IdNr. / UID / MWST), verbatim | **28.3–52.0%** |
| `contactPersons[]` | Named people: `name`, `firstName`, `lastName`, `role` (wlw code: executive, sales, marketing, technology, procurement …), `phone` | **87.7–97.3%** |
| `employeeBand` | Mitarbeiter band: 1-4, 5-9, 10-19, 20-49, 50-99, 100-199, 200-499, 500-999, 1000+ | 92.1–96.0% |
| `foundingYear` | Gründungsjahr | 65.3–85.3% |
| `supplierTypes[]` | wlw's type codes: production, customer\_specific\_manufacturing, distribution, service, wholesaler | 91.1–96.7% |
| `supplierTypeLabels[]` | wlw's own German labels for those codes, read from the page (Hersteller/Fabrikant, Kundenspezifische Fertigung, Lieferant, Dienstleister, Großhändler) | same as types |
| `certificatesCount` | Number of certificates on the profile | 100% |
| `certificates[]` | Certificate names (`{ "name": "DIN EN ISO 9001:2015" }`) | 34.7–75.3% |
| `distributionArea` | regional, national, europe, international | 97.9–100% |
| `mainBusinessArea` | wlw's main category for the company | 98.7–100% |
| `productNames[]`, `productKeywords[]` | Products the supplier showcases for your search, and their keywords | 86.3–100% |
| `productCategories[]`, `businessCategories[]` | Every product/business category on the profile | 100% / 90.7–96.3% |
| `productCount` | Number of products on the profile | 100% |
| `description` | The company's own description | 88.8–99.0% |
| `catalogUrl` | Link to the catalogue PDF the company uploaded | 16.7–30.0% |
| `isPaidProfile` | Whether the company is a paying wlw customer | 100% |
| `responseRate`, `averageResponseTime` | How reliably / quickly the supplier answers wlw enquiries (when wlw shows it) | 5.1–17.3% / 11.7–23.3% |
| `emailOnFileAtWlw` | `true` = wlw holds an email for the company but only forwards messages through its contact form | 100% (true on ~99%) |
| `europagesId` | The company's Europages id, when wlw shows it | ~1% |
| `logoUrl`, `profileUrl`, `slug`, `uuid`, `companyId` | Identity and links | 100% |
| `searchKeyword`, `searchUrl`, `searchRank`, `profileDetails`, `scrapedAt` | Provenance | 100% |

`vatId`, `certificates[]`, `contactPersons[]`, `mainBusinessArea`, `productCategories[]`,
`businessCategories[]`, `catalogUrl` and `europagesId` come from the supplier's profile and need
**Read each supplier's profile** on (the default).

#### Not included, and why

- **Email.** wlw does not publish supplier email addresses: the field is served empty on every row we
  measured (0 of 2,493) and buyers reach suppliers through wlw's contact form. `emailOnFileAtWlw` tells you
  wlw holds one. To get an email, run the `website` column through our
  [Impressum / imprint contact scraper](https://apify.com/scrapersdelight/imprint-contact-scraper), which reads the
  Impressum page of each company website.
- **Fax and profile keywords.** Present in wlw's data model but served empty on every profile we read,
  so they are not shipped as columns of nulls.

### Example row (real, from the verified run)

```json
{
  "companyId": "374838",
  "name": "Metallbau Heimsch GmbH",
  "phone": "+49711755171",
  "website": "http://www.metallbau-heimsch.de/",
  "emailOnFileAtWlw": true,
  "street": "Dreifelderstrasse 29",
  "postalCode": "70599",
  "city": "Stuttgart",
  "countryCode": "DE",
  "latitude": 48.70354,
  "longitude": 9.220421,
  "vatId": "DE814207772",
  "employeeBand": "10-19",
  "foundingYear": 1946,
  "supplierTypes": ["production", "service"],
  "supplierTypeLabels": ["Hersteller/Fabrikant", "Dienstleister"],
  "distributionArea": "regional",
  "certificatesCount": 5,
  "certificates": [{ "name": "TRGS 519" }, { "name": "EN 1090-2 EXC2" }, { "name": "EN 1090-1" }, { "name": "EN 1090" }, { "name": "DIN EN 1090" }],
  "contactPersons": [{ "name": "Bernhard Stöhr", "firstName": "Bernhard", "lastName": "Stöhr", "role": "executive", "roleText": null, "phone": "+49711755171" }],
  "mainBusinessArea": "Bauteile",
  "productNames": ["Metallbau"],
  "productKeywords": ["Metallbau", "Industriegeländer"],
  "productCategories": ["Metallbau (Sonstiges)"],
  "businessCategories": ["Anbaubalkone", "Geländer aus Glas", "Glasvordächer", "Stahlbau (DIN EN 1090-1/-2)"],
  "productCount": 1,
  "isPaidProfile": false,
  "responseRate": null,
  "averageResponseTime": null,
  "description": "Unternehmen im Raum Stuttgart für Metallbau, Schlosser, Schlosserarbeiten, Glasbau und Edelstahlbearbeitung",
  "catalogUrl": null,
  "logoUrl": "https://wlw-1-company-facts-media20191122174836234900000006.s3.eu-central-1.amazonaws.com/9192ffde-db20-43d8-91b0-f06a10143fcb.png",
  "europagesId": null,
  "profileUrl": "https://www.wlw.de/de/firma/metallbau-heimsch-gmbh-374838",
  "slug": "metallbau-heimsch-gmbh-374838",
  "uuid": "00505682-f25b-1ee9-bd91-d257befa97fb",
  "profileDetails": true,
  "searchKeyword": "metallbau",
  "searchUrl": "https://www.wlw.at/de/suche/metallbau",
  "searchRank": 1,
  "scrapedAt": "2026-09-23T08:14:28.703Z"
}
```

### Input

| Field | Default | What it does |
|---|---|---|
| `keywords` | `["CNC-Fräsen"]` | German product keywords, one wlw search each ("Schrauben", "Drehteile", "Metallbau", "Verpackungsmaschinen"). Spaces and umlauts are fine. |
| `countries` | all | `DE`, `AT`, `CH` — only suppliers located there (wlw's own country filter). Each country is its own search. |
| `supplierTypes` | all | `production`, `customer_specific_manufacturing`, `distribution`, `service`, `wholesaler` (wlw's own filter). |
| `city` | — | Suppliers in and around a city, as wlw's city pages do ("München", "Bremen"). wlw includes neighbouring towns in its own radius. |
| `startUrls` | — | wlw search pages copied from your browser (`https://www.wlw.de/de/suche/schrauben/bremen`, `…/supplier-type/wholesaler`, `…/country/austria`) or single profiles (`https://www.wlw.de/de/firma/<name>-<id>`). wlw.at / wlw.ch URLs work too. |
| `employeeBands` | all | Keep only suppliers stating one of these bands. Suppliers without a band are skipped. |
| `requirePhone`, `requireWebsite` | off | Deliver (and charge) only suppliers with a phone / website. |
| `includeProfileDetails` | on | Read each supplier's profile for VAT id, certificates, contact persons, categories, catalogue. Off = search-page fields only, faster. |
| `maxResults` | 50 | Stop after this many suppliers. `0` = no limit. |
| `splitLargeSearches` | on | wlw shows at most 3,000 suppliers per search. When you ask for more than that, big searches are walked per country (DE/AT/CH) and then per supplier type, deduplicated on wlw's id. |
| `concurrency` | 8 | Pages read in parallel. |
| `proxyConfiguration` | Apify RESIDENTIAL (DE) | See "How it works". |

Example — every CNC milling shop in Switzerland with its profile:

```json
{ "keywords": ["CNC-Fräsen"], "countries": ["CH"], "maxResults": 0 }
```

Example — Austrian packaging-machine manufacturers with 20+ staff and a phone:

```json
{ "keywords": ["Verpackungsmaschinen"], "countries": ["AT"], "supplierTypes": ["production"],
  "employeeBands": ["20-49", "50-99", "100-199", "200-499", "500-999", "1000+"], "requirePhone": true, "maxResults": 500 }
```

### How it works (and why it is complete)

- **Search pages are read on wlw.at.** wlw.de, wlw.at and wlw.ch serve the same DACH directory with the
  same German search terms, but wlw.de opens page 1 of every search with paid "Top-Ranking" placements that
  push organic rows off the list: we measured **138 unique suppliers out of 171** on wlw.de against **166 of
  166** on wlw.at for the same search. Profiles are read from wlw.de, and `profileUrl` points there.
- **Completeness is checked, not assumed.** wlw orders suppliers with equal scores differently from one
  request to the next, so neighbouring pages can overlap. When a search is read to the end, the actor
  compares the unique suppliers it found with the total wlw states; if it is short, it re-reads the pages
  (up to 3 more passes) to collect the rows that slid between pages, and **the run fails rather than hand you
  a silently short list** if it still has under 97%. Measured end-to-end: 454 of 454, 711 of 711, 428 of 432.
  Every search's stated total, pages read, passes and coverage are in `RUN_SUMMARY`.
- **Transport.** wlw sits behind an AWS firewall that allows one IP address only about 20 page requests
  before it demands a CAPTCHA and then blocks the IP. The default proxy (Apify RESIDENTIAL, a fresh IP for
  every request) keeps runs going; firewall challenge pages are detected and retried on a new IP, never
  parsed as data. Without a proxy a run can read only ~18 pages.

### Pricing

**$0.0011 per supplier delivered ($1.10 per 1,000).** One event, `supplier-profile`. No start fee.
Not charged: duplicates, suppliers removed by your filters, searches that do not exist, and pages wlw would
not serve. A search with 0 results costs nothing but the platform's run minimum. An empty input (`{}`)
runs the demo search with the 50-supplier default, i.e. $0.055.

### Run summary

Every run writes `RUN_SUMMARY` to the key-value store: each search with wlw's stated total, pages read,
unique suppliers, passes and coverage; rows delivered vs charged; what was not charged and why (duplicates,
filtered, not found, unreachable); profiles that could not be read (those rows are still delivered from the
search page with `profileDetails: false`); a check that no wlw / Visable / Europages address reached the
data; and per-field fill rates for that run.

### Honest limits

- **wlw shows at most 3,000 suppliers per search** (100 pages × 30). With `splitLargeSearches` a larger pull
  is split by country and supplier type; a partition that is still above 3,000 is read to its first 3,000
  and flagged `cappedBySite` in `RUN_SUMMARY`. Suppliers with no supplier type cannot be reached through
  that second split.
- **The wlw.at view lists ~2% fewer suppliers than wlw.de** for the same search (10,688 vs 10,882 for
  "Schrauben"; 166 vs 171 for "Schrauben" in Bremen): a company can restrict its profile to some markets
  (`restrictedIn` in wlw's data). Those companies are not in the wlw.at view.
- **Phone numbers are delivered exactly as published** — never reformatted, completed or "repaired". A
  handful carry wlw's typing quirks (e.g. `+49 0…`). A digit stub under 6 digits (seen once in 2,493 rows)
  is dropped. wlw offers advertisers call-tracking numbers that forward to the company, so a paying
  customer's (`isPaidProfile: true`) published number can be such a forwarding number.
- **Contact persons are what the company entered on wlw** — a name and a role code; their `phone` is
  usually the company switchboard. `roleText` is filled only when the company typed a custom role.
- **VAT ids are verbatim** and not checked against VIES.
- **Labels are wlw's, not ours**: supplier types come with wlw's German labels read from the page; codes such
  as `distributionArea` and `role` are wlw's codes, not translated.
- wlw and Europages are one Visable database: the same company has the same record on both. This actor
  searches wlw's German-language DACH directory.

### Source

Data comes from public wlw.de pages that suppliers publish to be found by buyers. This actor is not
affiliated with, endorsed by or sponsored by Visable GmbH, wlw or Europages.

### Related

- [Impressum / imprint contact scraper](https://apify.com/scrapersdelight/imprint-contact-scraper) — add the
  email, managing director and register number from each supplier's own website.
- [Europages scraper](https://apify.com/scrapersdelight/europages-scraper) — the pan-European sister directory.

# Actor input Schema

## `keywords` (type: `array`):

What the suppliers make, sell or do — in German, as a buyer types it on wlw.de: "CNC-Fräsen", "Schrauben", "Metallbau", "Drehteile", "Verpackungsmaschinen", "Kunststoffspritzguss". Each keyword is one wlw search. Spaces are fine.

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

Only suppliers located in these countries: DE (Germany), AT (Austria), CH (Switzerland). Leave empty for every supplier wlw lists for the keyword (mostly DACH, a few elsewhere). Each country is its own wlw search.

## `supplierTypes` (type: `array`):

Only suppliers of these types, using wlw's own filter (wlw labels in brackets): production (Hersteller/Fabrikant), customer\_specific\_manufacturing (Kundenspezifische Fertigung), distribution (Lieferant), service (Dienstleister), wholesaler (Großhändler). Leave empty for all.

## `city` (type: `string`):

Suppliers in and around one city, as wlw's own city pages do (e.g. "München", "Bremen", "Stuttgart"). wlw includes neighbouring towns within its own radius. Leave empty for the whole area.

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

Instead of (or as well as) keywords: wlw search pages copied from the browser (https://www.wlw.de/de/suche/schrauben/bremen, …/supplier-type/wholesaler, …/country/austria) or single supplier profiles (https://www.wlw.de/de/firma/<name>-<id>). wlw.at and wlw.ch URLs work too.

## `employeeBands` (type: `array`):

Only suppliers whose wlw profile states one of these employee bands (Mitarbeiter). Suppliers that state no band are skipped when this is set. Applied to the rows wlw returns, so the search pages are still read.

## `requirePhone` (type: `boolean`):

Deliver only suppliers that publish a phone number. Others are skipped and never charged.

## `requireWebsite` (type: `boolean`):

Deliver only suppliers that publish a website. Others are skipped and never charged.

## `includeProfileDetails` (type: `boolean`):

On (default): also read each supplier's wlw profile for the VAT id (USt-IdNr.), certificates, named contact persons with their role, main business area, product categories and catalogue link. Off: search-page fields only (name, phone, website, address, employee band, founding year, supplier type, products) — faster.

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

Stop after this many suppliers (you are charged per supplier delivered). 0 = no limit. wlw shows at most 3,000 suppliers per search; with 'Split large searches' on, bigger searches are split by country and supplier type to reach past that.

## `splitLargeSearches` (type: `boolean`):

When a search holds more than wlw's 3,000-row window and you asked for more rows than that, walk it per country (DE/AT/CH) and then per supplier type, de-duplicated on wlw's company id. Only used on very large pulls.

## `concurrency` (type: `integer`):

How many wlw pages are read at the same time.

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

Default: Apify RESIDENTIAL proxy (Germany), a fresh IP per request. wlw's firewall blocks any single IP after about 20 page requests (CAPTCHA, then 403), so a rotating residential pool is what keeps a run going; without a proxy a run can read only ~18 pages.

## Actor input object example

```json
{
  "keywords": [
    "CNC-Fräsen"
  ],
  "countries": [],
  "supplierTypes": [],
  "employeeBands": [],
  "requirePhone": false,
  "requireWebsite": false,
  "includeProfileDetails": true,
  "maxResults": 10,
  "splitLargeSearches": true,
  "concurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}
```

# Actor output Schema

## `suppliers` (type: `string`):

The dataset of wlw supplier profiles (one item per supplier).

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

Measured totals: wlw-stated vs unique suppliers per search, per-field fill, what was not charged and why, transport stats.

# 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 = {
    "keywords": [
        "CNC-Fräsen"
    ],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "DE"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/wlw-supplier-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 = {
    "keywords": ["CNC-Fräsen"],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "DE",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/wlw-supplier-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 '{
  "keywords": [
    "CNC-Fräsen"
  ],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}' |
apify call scrapersdelight/wlw-supplier-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/wlw-supplier-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/IDlnUhUZDhydLf6ks/builds/ztDHXLPkzFCO5xxpC/openapi.json
