# Gelbe Seiten Business Scraper (`axiomworks/gelbe-seiten-business-scraper`) Actor

Search Gelbe Seiten by business category and city or postal code, and collect structured business listings. Results include names, categories, addresses, phone numbers, websites and ratings; optional detail-page visits add email, opening hours and coordinates. Duplicate businesses are removed.

- **URL**: https://apify.com/axiomworks/gelbe-seiten-business-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.95 / 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.
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

## Gelbe Seiten Business Scraper

**Returns:** one row per business with name, category, address, phone, website, email and sourceUrl.
**Input:** `{"searches":[{"query":"zahnarzt","location":"muenchen"}],"maxItems":50}`
**Best for:** local business lists, competitor mapping by city, and refreshing German business contact records.

This Gelbe Seiten scraper provides structured data for Gelbe Seiten API workflows. One real record from a local run:

```json
{
  "id": "ba862339-6447-4091-92d9-50b38785b57b",
  "name": "La Choza de la Anaconda",
  "category": "Gaststätten und Restaurants",
  "address": "Windscheidstraße 31, 10627 Berlin",
  "phone": "030 54 71 08 00",
  "website": "https://www.lachozadelaanaconda.de/",
  "email": "Info@lachozadelaanaconda.de",
  "sourceUrl": "https://www.gelbeseiten.de/gsbiz/ba862339-6447-4091-92d9-50b38785b57b"
}
```

### What does Gelbe Seiten Business Scraper do?

Gelbe Seiten is the German Yellow Pages. It lists shops, restaurants, doctors, craftspeople, lawyers and many other kinds of local business, sorted by category (Branche) and place. This Actor runs a search for each category and city you give it, reads the result pages page by page, and saves one clean record per business.

For every business it reads the listing card (name, category, address, phone, website, rating) and, by default, also opens the business page to add the email address, the opening hours and the map coordinates. Phone numbers and website links are stored in an encoded form on the page; the Actor decodes them for you, so you get plain readable values.

You can run several searches in one run, for example "zahnarzt" in several cities, and set one overall limit. Businesses that appear twice, such as paid listings repeated among the normal results, are saved only once.

Optionally, each business is classified as a single-location small business, a chain or franchise, or an institution. This step uses a third-party AI processor and is off by default; set `enableAiEnrichment` to true to opt in.

### What data can you get?

| Field | Type | Description |
|---|---|---|
| `id` | string | Gelbe Seiten business identifier (UUID), unique per record |
| `sourceUrl` | string | Gelbe Seiten page of the business |
| `fax` | string | Fax number from the business page, when published (needs includeDetails) |
| `logoUrl` | string | Logo image URL from the business page, when published |
| `lastModified` | string | Date the entry was last modified (YYYY-MM-DD) |
| `businessTypes` | array | schema.org business types of the entry, such as Restaurant |
| `name` | string | Business name |
| `category` | string | Primary Gelbe Seiten category |
| `address` | string | Street, postal code and city on one line |
| `street` | string | Street and house number |
| `postalCode` | string | German postal code |
| `city` | string | City or town |
| `phone` | string | Phone number as listed |
| `website` | string | Business website URL |
| `email` | string | Contact email from the business page, when published |
| `openingHours` | array of strings | Opening hours, one entry per day or range |
| `rating` | number | Average rating from 1 to 5, when the business has reviews |
| `reviewCount` | number | Number of reviews |
| `latitude` | number | Latitude of the business |
| `longitude` | number | Longitude of the business |
| `companyType` | string | `sme`, `chain` or `institution` (AI classification) |

Not every business publishes every field. In our test runs, name, category, address and coordinates were present for every record, phone numbers for nearly all, websites and emails for most, and ratings for a minority of businesses. Fields that a listing does not have are returned as `null`.

### How to use

1. Open the Actor and go to the Input tab.
2. Add one or more searches. Each search has a `query` (a German category or keyword such as `zahnarzt`, `restaurants`, `elektriker`) and a `location` (a city or a postal code such as `berlin` or `80331`).
3. Set `maxItems` to the number of businesses you want in total.
4. Leave `includeDetails` on if you need email, opening hours and coordinates. Turn it off for a faster run that only reads the listing cards.
5. Click Start. Results appear in the Output tab as soon as they are saved, and you can download them as JSON, CSV, Excel or XML.

For existing business lists, supply `businessIds` as an array of Gelbe Seiten UUIDs, for example `{"businessIds":["ba862339-6447-4091-92d9-50b38785b57b"],"maxItems":1}`. Each UUID fetches its business page even when `includeDetails` is false, because a UUID has no search card. IDs, URLs and searches share one overall result budget; duplicate businesses appear once.

With `includeDetails: false`, website URLs are decoded directly from listing cards alongside name, address, phone and ratings. This mode avoids opening each business page when you only need those fields. Email, hours, coordinates and other detail fields remain null in this mode. `requireEmail` forces details on; `requireWebsite` can filter listing-card results without detail requests.

Blocked or unresolved targets return no placeholder rows. Retries use fresh proxy sessions with backoff, and a target that cannot be fetched leaves guidance in the status message while other targets continue. Malformed URLs, invalid UUIDs, missing targets and invalid item limits fail input validation. Empty results do not mean a business has closed: check the spelling, filters and source page before interpreting absence.

You can also paste Gelbe Seiten search addresses such as `https://www.gelbeseiten.de/suche/zahnarzt/muenchen` into `startUrls`. The category and city are read from the address.

Tips for good results:

German search terms work best, so use `zahnarzt` rather than `dentist`. To cover a country-wide category, run one search per city instead of one large search. Use `sortBy` to get the best-rated or the closest businesses first.

### Input

| Field | Type | Description |
|---|---|---|
| `searches` | array | Objects with `query` and `location`; required unless startUrls or businessIds is supplied |
| `startUrls` | array | Optional Gelbe Seiten search URLs (`/suche/{what}/{where}`) or single business URLs (`/gsbiz/{id}`) |
| `businessIds` | array of strings | Business UUIDs; always reads detail pages, default empty |
| `sortBy` | string | `relevanz` (default), `bewertung` or `entfernung` |
| `maxItems` | integer | Maximum businesses over all searches, 1 to 5000, default 50 |
| `includeDetails` | boolean | Open each business page for email, opening hours and coordinates, default true |
| `requireEmail` | boolean | Return only businesses that publish an email address (turns detail pages on), default false |
| `requireWebsite` | boolean | Return only businesses that list a website, default false |
| `requirePhone` | boolean | Return only businesses that list a phone number, default false |
| `minRating` | number | Return only businesses rated at least this value (0-5), default 0 |
| `maxItemsPerSearch` | integer | Cap per search or search URL so one broad search cannot use the whole budget, 0 means no cap |
| `enableAiEnrichment` | boolean | Add `companyType` with a third-party AI processor, default false |
| `proxyConfiguration` | object | Apify Proxy settings, datacenter proxy by default |

Example input:

```json
{
  "searches": [
    { "query": "zahnarzt", "location": "muenchen" },
    { "query": "restaurants", "location": "berlin" }
  ],
  "maxItems": 100,
  "sortBy": "bewertung",
  "includeDetails": true,
  "enableAiEnrichment": false,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### Output

Each business is one dataset item. A full example record from a "restaurants" search in Berlin (the email and company type are illustrative):

```json
{
  "id": "ba862339-6447-4091-92d9-50b38785b57b",
  "sourceUrl": "https://www.gelbeseiten.de/gsbiz/ba862339-6447-4091-92d9-50b38785b57b",
  "name": "La Choza de la Anaconda",
  "category": "Gaststätten und Restaurants",
  "address": "Windscheidstraße 31, 10627 Berlin",
  "street": "Windscheidstraße 31",
  "postalCode": "10627",
  "city": "Berlin",
  "phone": "030 54 71 08 00",
  "website": "https://www.lachozadelaanaconda.de/",
  "email": "info@lachozadelaanaconda.de",
  "openingHours": ["Mo 17:00-24:00", "So 12:00-24:00"],
  "rating": null,
  "reviewCount": null,
  "latitude": 52.50771,
  "longitude": 13.299127,
  "companyType": "sme"
}
```

The dataset has an overview table view with the most useful columns. The `id` is the Gelbe Seiten identifier, so you can use it to join runs over time and detect new or removed businesses.

### How much does it cost?

You pay per result: every business saved in the dataset counts as one result. Businesses that also receive the AI classification count as an enriched result. Open the Pricing tab of the Actor for the current rates. The Actor stops at `maxItems`; the platform maximum cost per run setting provides an additional spending limit. Switching off `includeDetails` and `enableAiEnrichment` makes a run faster.

### Use with AI agents (MCP)

You can call this Actor from any MCP client through the Apify MCP server. Add this to your client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=axiomworks/gelbe-seiten-business-scraper"
    }
  }
}
```

Example prompts:

Prompt 1: "Find 30 zahnarzt practices in München and list their phone numbers and websites."
Prompt 2: "Get the best rated restaurants in Berlin from Gelbe Seiten with opening hours."
Prompt 3: "Build a list of elektriker in Hamburg with email addresses."

The agent fills in `searches` and `maxItems` from your request and reads the dataset when the run finishes.

### Integrations and API

You can start the Actor from code with the Apify API clients.

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("axiomworks/gelbe-seiten-business-scraper").call(run_input={
    "searches": [{"query": "zahnarzt", "location": "muenchen"}],
    "maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["name"], item["phone"])
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('axiomworks/gelbe-seiten-business-scraper').call({
    searches: [{ query: 'zahnarzt', location: 'muenchen' }],
    maxItems: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
process.stdout.write(`${items.length} businesses\n`);
```

cURL:

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~gelbe-seiten-business-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searches":[{"query":"zahnarzt","location":"muenchen"}],"maxItems":50}'
```

You can also connect the dataset to Google Sheets, Make, Zapier or n8n with the standard Apify integrations, schedule runs, and trigger webhooks when a run finishes.

### FAQ

**Why do I get fewer results than `maxItems`?**
The search simply has fewer businesses. The Actor logs the total number of results the site reports for each search. Try a broader category or a larger area.

**Why is `email` empty for some businesses?**
Email addresses only exist on the business page, and not every business publishes one. Make sure `includeDetails` is on.

**Why is `rating` empty for most businesses?**
Ratings are returned only when the business publishes reviews. With detail pages enabled, missing listing ratings and review counts fall back to the business page’s structured aggregate rating. Direct business URLs also return these fields when published. The Actor never invents a rating.

**What happens if I mistype the category or city?**
The run finishes with zero items and a warning in the log. No placeholder records are saved.

**Can I search in English?**
Category names are German, so German terms match best, for example `rechtsanwalt` or `friseur`.

**How fast is it?**
The Actor makes about one search request per second and at most two business page requests in parallel, to be polite to the site. Twenty businesses with details take roughly twenty seconds.

**What does the AI step do?**
It sends the business name, category, address and website to a third-party AI processor, which answers whether the business is a small single-location business, a chain or an institution. If the processor is unavailable, the Actor continues without it and leaves `companyType` empty.

### Is it legal to scrape Gelbe Seiten?

The Actor only collects information that is publicly shown on the website without logging in. It does not bypass logins or CAPTCHAs. Business listings can contain personal data, for example when a sole trader's name is the business name. Data protection rules such as the GDPR may apply to how you store and use it, and the site's terms of use may restrict automated access or commercial reuse. You are responsible for using the data lawfully, for example for B2B contact with a legitimate interest, and for respecting opt-out requests. This is not legal advice; ask a lawyer if you are unsure. This Actor is an independent tool and is not affiliated with or endorsed by the operators of Gelbe Seiten.

### Feedback

If a field is wrong or missing for a category you care about, or you need another field, open an issue from the Actor page and include the search that reproduces it. Reports with an example input are the fastest to fix.

# Actor input Schema

## `searches` (type: `array`):

Required unless startUrls is given. Array of objects, each {"query": German category or keyword, "location": city or postal code}. Example: \[{"query":"zahnarzt","location":"muenchen"},{"query":"restaurants","location":"80331"}]. Entries without a query or location are skipped. All searches share the maxItems budget. Missing or blocked targets return no rows and guidance.

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

Optional. Gelbe Seiten URLs: search pages like https://www.gelbeseiten.de/suche/zahnarzt/muenchen (category and city read from the URL) or single business pages like https://www.gelbeseiten.de/gsbiz/<id>. Processed together with 'searches', sharing the maxItems budget. Other hosts are skipped with a warning.

## `businessIds` (type: `array`):

Gelbe Seiten business UUIDs, e.g. ba862339-6447-4091-92d9-50b38785b57b. Alternative to searches or startUrls; all targets share maxItems. Business IDs always fetch detail pages. Missing or blocked businesses produce no row and a status message. Default empty.

## `sortBy` (type: `string`):

Result order for searches: 'relevanz' (relevance, default), 'bewertung' (rating) or 'entfernung' (distance). Example: "bewertung".

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

Total businesses to return across all searches and URLs (shared budget), 1-5000, default 50. Run stops once this many are saved. Example: 20. Use maxItemsPerSearch to split it between searches.

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

true (default) opens each business page to add email, openingHours, latitude/longitude (one extra request per business, slower). false returns only listing-card fields (name, address, phone, website, rating) and is faster.

## `requireEmail` (type: `boolean`):

true returns only businesses that publish an email; forces includeDetails on. Skipped businesses do not count toward maxItems, so the run may scan many pages. Default false.

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

true returns only businesses that list a website. Skipped businesses do not count toward maxItems. Default false.

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

true returns only businesses that list a phone number. Skipped businesses do not count toward maxItems. Default false.

## `minRating` (type: `number`):

Minimum average rating, 0-5 (e.g. 4). When above 0, unrated businesses are skipped. 0 (default) means no filter.

## `maxItemsPerSearch` (type: `integer`):

Cap per single search or search URL, so one broad search cannot use the whole maxItems budget. Example: 25. 0 (default) means no cap.

## `enableAiEnrichment` (type: `boolean`):

Opt in to companyType ('sme', 'chain' or 'institution') classification by a third-party AI processor. Default false leaves companyType null. Successful enrichment is charged separately; AI errors leave companyType null.

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

Apify Proxy settings, e.g. {"useApifyProxy":true}. Datacenter by default; blocked requests are retried on a fresh session. If a search returns 0 results because of blocking, add "apifyProxyGroups":\["RESIDENTIAL"].

## Actor input object example

```json
{
  "searches": [
    {
      "query": "restaurants",
      "location": "berlin"
    }
  ],
  "businessIds": [],
  "sortBy": "relevanz",
  "maxItems": 20,
  "includeDetails": true,
  "requireEmail": false,
  "requireWebsite": false,
  "requirePhone": false,
  "minRating": 0,
  "maxItemsPerSearch": 0,
  "enableAiEnrichment": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One record per business listing.

# 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 = {
    "searches": [
        {
            "query": "restaurants",
            "location": "berlin"
        }
    ],
    "maxItems": 20,
    "includeDetails": true,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/gelbe-seiten-business-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 = {
    "searches": [{
            "query": "restaurants",
            "location": "berlin",
        }],
    "maxItems": 20,
    "includeDetails": True,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/gelbe-seiten-business-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 '{
  "searches": [
    {
      "query": "restaurants",
      "location": "berlin"
    }
  ],
  "maxItems": 20,
  "includeDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call axiomworks/gelbe-seiten-business-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axiomworks/gelbe-seiten-business-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/1KeWWOsfrZ8uO1igG/builds/eKdAVaRbIhp6WYOf8/openapi.json
