# Energieeffizienz-Experten - German Energy Adviser Leads (`scrapersdelight/energie-effizienz-experten-scraper`) Actor

From $0.90 per 1,000 experts, no start fee. Germany's official dena Energieeffizienz-Expertenliste: name, email (95% filled, measured), phone, firm, full address and the per-expert BAFA/KfW/BEG accreditation matrix. Search by postcode and radius across all three lanes, filter by funding programme.

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

## Pricing

$0.90 / 1,000 per expert row 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

## 🇩🇪 Energieeffizienz-Experten — German Energy Adviser Leads

**Germany's official register of the experts who sign off federal energy-efficiency subsidies —
with the email address.**

The *Energieeffizienz-Expertenliste für Förderprogramme des Bundes*, run by the **dena** (Deutsche
Energie-Agentur) for the federal economics ministry, is the gate every German subsidy goes through.
A homeowner cannot claim a BAFA Energieberatung, a KfW Effizienzhaus loan or a BEG Einzelmaßnahme
grant unless a **listed expert** signs it off. So this register is, in practice, the national list of
the people who decide which heat pump, which insulation system and which window goes into a
subsidised German renovation.

This Actor turns it into a lead list — one row per expert, with the contact details **and** the full
matrix of which funding programmes that expert is actually accredited for.

***

### 📊 The numbers, measured on 2026-09-16

Every figure below was counted against the live site on the day this Actor was built, from **213
records captured across 8 regions, all three register lanes, and pages ranging from the first to
page 401 of 676**. Nothing here is an estimate, and the offline validator recounts all of it on
every run.

#### Field fill — counted over 179 distinct experts

| Field | Fill | | Field | Fill |
|---|---|---|---|---|
| `name` | **100.0%** | | `email` | **95.0%** |
| `street` | **100.0%** | | `phone` | **87.7%** |
| `postalCode` | **100.0%** | | `website` | 65.4% |
| `city` | **100.0%** | | `organisation` (firm) | 83.8% |
| `accreditedProgrammes` | **100.0%** | | `education` | 98.3% |
| `programmes` (full matrix) | **100.0%** | | `businessTypes` | 86.0% |
| **email or phone** | **97.2%** | | `selfDescription` | 50.3% |

"Filled" excludes `""`, `null`, `"N/A"` and `"-"`. 213 parsed rows collapsed to 179 distinct experts,
so 34 of them were returned by more than one of the searches — deduplication is on by default and a
dropped duplicate is never charged.

#### How big each search is

| Search (from 10115 Berlin, 250 km) | Experts | Pages |
|---|---|---|
| Wohngebäude (residential) | **3,467** | 232 |
| Nichtwohngebäude (companies + municipalities) | **1,556** | 104 |
| Anlagen und Prozesse (industry) | **537** | 36 |

Other regions, residential lane, 250 km: Stuttgart **10,131** · Köln 9,002 · München 6,687 ·
Hamburg 5,127 · Leipzig 4,916 · Dresden 2,908. Radius matters more than anything else: around
10115 Berlin, 5 km = 228 experts, 20 km = 774, 250 km = 3,467.

***

### 🎯 What makes a row here different from Google Maps

Maps can tell you a German engineering office exists. It cannot tell you **that this person may sign
a KfW Effizienzhaus application but not a KFN new-build one.** This register can, because it
publishes a per-expert checkbox matrix, and this Actor reads every box — ticked *and* unticked:

```json
"accreditedProgrammes": [
  "Energieberatung für Wohngebäude",
  "Effizienzhaus (KfW)",
  "Einzelmaßnahmen",
  "Wärmedämmung",
  "Denkmal und besonders erhaltenswerte Bausubstanz",
  "Klimafreundlicher Neubau (KFN) und Wohneigentum für Familien (WEF)"
],
"programmes": [
  { "scope": "residential", "category": "Energieberatung",
    "programme": "bafa", "label": "Energieberatung für Wohngebäude", "accredited": true },
  { "scope": "residential", "category": "Sanierung",
    "subCategory": "Bundesförderung für effiziente Gebäude: Wohngebäude",
    "programme": "kfw-single", "label": "Einzelmaßnahmen", "accredited": true },
  { "scope": "residential", "measureOf": "kfw-single",
    "label": "Heizung", "accredited": false }
]
```

An unticked box is recorded as `"accredited": false`, not dropped — so "who is accredited for
**Heizung** but not yet doing KFN new-builds" is a query you can actually run on the export. The
matrix runs from **7 to 29 programme rows per expert, 15 on average**.

`servesResidential` / `servesNonResidential` / `servesIndustry` summarise the same thing as three
booleans. In the captured sample, 42.5% of residential-lane experts also hold non-residential
accreditations and 17.3% hold industrial ones.

**Who buys this:** heat-pump and insulation brands recruiting Fachpartner (Viessmann, Vaillant,
Stiebel Eltron, Bosch), Energieberater software vendors (Hottgenroth, ZUB, EVEBI), renovation lead
networks, and anyone selling into the German BEG subsidy chain.

***

### 📧 The email column is the whole product, and it is a trap

dena hides the addresses. Not by splitting them across tags — **the "@" is not a character at all.**
It is an `<img src=".../at.svg">` inside a `<span class="print-color">`, so the raw text of an email
block reads:

```
roland.orgel        ro-ingenieure.de
```

Any scraper that takes the element's text and runs an email regex over it finds **zero addresses on
a page where 95% of the records publish one**, reports SUCCESS, and hands you a contact list with no
contacts. This Actor puts the "@" back before the tags are stripped, and then checks itself:

- **202** of the 213 captured records publish an email block; **201** reconstructed into a valid
  address (**99.5%**).
- The 1 that did not (`ebfe.ebfe@info`) is malformed **on dena's own site** — no TLD. It is returned
  as `null` rather than shipped, because a broken address in your CRM is worse than an empty field.
- If dena ever changes that markup, the run **fails** rather than delivering a silently blank email
  column. `RUN_SUMMARY.emailReconstruction` carries the counts on every run.
- 169 of the 170 addresses in the sample are distinct, so these are per-expert mailboxes, not one
  chamber-of-commerce inbox repeated.

***

### ⚙️ What you control

| Input | What it does |
|---|---|
| **Register lane** | residential / non-residential / industry. Three separate searches, run in turn. |
| **Postcodes or place names** | `"10115"` or `"Berlin"` — the site's own box takes both (verified: "Berlin" / 20 km → 778 experts). |
| **Radius** | 5, 10, 20, 50, 100 or 250 km. These six are the only values the site offers; anything else is refused before a request is made. |
| **Funding programme** | The register's own checkboxes (BAFA, KfW Effizienzhaus, Denkmal, KFN, QNG, DIN 18599, DIN 16247 …). Measured: no filter 3,467 → `bafa` 3,024 → `kfn_wg` 3,173 → both 2,788, i.e. they combine as **AND**. |
| **Single measures** | Wärmedämmung / Fenster und Türen / **Heizung** / Lüftung — the heat-pump filter. Residential lane only. |
| **Surname or firm** | "Müller" within 250 km of Berlin → 39 of 3,467. |
| **requireEmail / requirePhone / requireContact** | Filter *before* delivery, so an excluded row is never charged. |
| **Deduplicate** | On by default. Overlapping radii are matched on name + postcode + street + firm. |
| **Include the full matrix** | Turn off for a slim CRM-sized row that keeps only the ticked labels. |
| **maxExperts** | Hard cap on rows delivered *and therefore charged*. |

***

### 💸 Pricing

**$0.90 per 1,000 experts** ($0.0009 per row). **No run-start fee.** You are charged once per row
delivered to your dataset, and:

- duplicates from overlapping radii are dropped **before** delivery and never charged,
- rows excluded by your email/phone filters are never charged,
- a search that matches nobody costs **nothing** — no start fee means a zero-result run is free,
- a run that fails mid-way has charged only for the rows already in your dataset, and `RUN_SUMMARY`
  says exactly how many that was.

For comparison, German business-directory scrapers on this Store that sell a plain listing row
charge $0.001–$0.004. Nobody else scrapes this register.

***

### ⏱️ Speed, and the throttle we measured

The register throttles **by IP**, and it does it politely:

> 10 result-page requests inside ~51 seconds, then **HTTP 429** with an honest **`Retry-After: 60`**.
> Proven IP-scoped, not session-scoped: a brand-new session on the same IP was throttled too.
> Honouring the header recovered cleanly on the first try.

There is no CAPTCHA, no ban, no escalation — and the 429 body is 1.5 MB of ordinary TYPO3 HTML, so a
scraper that only looks at the response size sees a fat, healthy-looking page. This Actor sends every
request on a **fresh Apify proxy session**, i.e. a different IP, so the per-IP budget is not the
ceiling. With no proxy configured at all it paces itself to 8 page requests per minute instead and
says so in the log.

- 15 experts per page. Each page is **287–395 KB** and takes **3.7–7.1 s**.
- At the default 3 parallel requests that is roughly **675 experts a minute**.
- Verified 2026-09-16 that the site answers HTTP 200 through Apify **RESIDENTIAL**, through the
  datacenter pool and through BUYPROXIES94952 alike. RESIDENTIAL/DE is the default because the pages
  are ~300 KB — the size at which a datacenter proxy starts truncating a body mid-stream while still
  returning HTTP 200 — and because a throttle scoped to the IP wants the larger pool.

***

### 🧯 Honest limits — read these before you buy

1. **There is no nationwide search.** 250 km is the largest radius the site offers, and an **empty
   location makes it answer HTTP 200 with its 141,923-byte homepage** and no results at all. National
   coverage means tiling postcodes and deduplicating the overlap. This Actor refuses an empty
   location up front rather than charging you for a search it never ran.

2. **We do not know the national de-duplicated total, and will not guess it.** The largest single
   query observed is 10,131 (Stuttgart, 250 km, residential). Regional tiles overlap heavily — 34 of
   213 captured rows were duplicates — so the national unique figure is well below the sum of the
   tiles and has not been measured.

3. **The register publishes no expert ID.** No profile URL, no numeric id, no data attribute —
   checked across all 318 captured records. Rows are deduplicated on **name + postcode + street +
   firm**. Two genuinely different people with the same name at the same address would merge; an
   expert who moves office will look like a new row in a later run.

4. **The three lanes are three corpora, not three views.** A record found through the residential
   search still carries its full accreditation matrix for all three scopes — but only the lane you
   search decides *which* experts come back. Run all three if you want the union.

5. **`selfDescription` is only on half the rows** (50.3%), and dena states plainly that it does not
   verify it: *"Diese Selbstangaben werden von der dena nicht überprüft."*

6. **Fields that do not exist here, and are therefore not advertised:** no expert ID, no
   registration or listing date, no rating or review, no company register (Handelsregister) number,
   no VAT ID, no coordinates, no distance-from-centre value, no photo. The site does not publish any
   of them on the result page, so this Actor does not pretend to.

7. **`robots.txt`.** Quoted verbatim, as fetched on 2026-09-16:

   ```
   User-agent: *
   Disallow: /typo3/
   Disallow: /fileadmin/
   Disallow: /uploads/
   Disallow: /index.php
   Disallow: /?id=
   Disallow: /fuer-private-bauherren/finden-sie-experten-in-ihrer-naehe/
   Disallow: /login
   Disallow: /aussteller/suche-experten
   Disallow: /profil-anlegen
   ```

   The **residential** lane's results path is on that list. The non-residential
   (`/fuer-unternehmen-und-kommunen/…`) and industry (`/anlagen-prozesse/…`) paths are not.
   AhrefsBot, SemrushBot and MJ12bot are disallowed site-wide. Decide for yourself what that means
   for your use of the data.

8. **This is personal data about named individuals** — many of these experts are sole traders
   (20.5% of the captured addresses are free-mail: gmx, web.de, t-online, gmail). GDPR applies.
   Germany's UWG also restricts unsolicited commercial email to businesses. Use it for recruiting
   partners and for research, and honour opt-outs.

***

### 📤 Output

One row per expert:

```json
{
  "name": "Dipl.-Ing. (FH) Hanna Full",
  "organisation": null,
  "email": "kontakt@hannafull.de",
  "phone": "015237060357",
  "website": "https://www.hannafull.de",
  "street": "Schwartzkopffstraße 8",
  "postalCode": "10115",
  "city": "Berlin",
  "addressFull": "Schwartzkopffstraße 8, 10115 Berlin",
  "education": ["Technisches/ naturwissenschaftliches Studium"],
  "businessTypes": ["sonstige Branche"],
  "selfDescription": "Hanna Full ist Ingenieurin im Bereich erneuerbare Energien …",
  "servesResidential": true,
  "servesNonResidential": false,
  "servesIndustry": false,
  "accreditedCount": 6,
  "accreditedProgrammes": ["Energieberatung für Wohngebäude", "Einzelmaßnahmen", "Wärmedämmung",
                           "Fenster und Türen", "Heizung", "Lüftung"],
  "programmes": [ "…the full ticked/unticked matrix…" ],
  "foundViaLane": "residential",
  "searchLocation": "10115",
  "searchRadiusKm": 5,
  "resultPage": 1,
  "resultRank": 2,
  "sourceUrl": "https://www.energie-effizienz-experten.de/…",
  "scrapedAt": "2026-09-16T…"
}
```

Umlauts and ß survive end to end (`Schwartzkopffstraße`, `Wohngebäude`) — the transfer is checked
for completeness before a single record is read from it.

**`RUN_SUMMARY`** (key-value store) reconciles the run: what each search said was there versus what
was read, rows delivered (== rows charged), duplicates dropped, rows excluded by your filters,
**field fill counted on the rows this run actually delivered**, how many email blocks reconstructed,
and every non-200 that was retried.

***

### 🛡️ What this Actor refuses to do

- **Report a partial region as a complete one.** If a search says 3,467 experts and the walk reads
  materially fewer without hitting your cap, the run **fails**.
- **Treat a non-200 as the end of the data.** It is a transport failure; it is retried on a fresh IP,
  and if it still fails the run fails and tells you how many rows were already charged.
- **Parse a body that did not arrive whole.** Pages are fetched uncompressed on purpose so the
  declared `Content-Length` can be checked against what arrived, and a page missing its closing
  `</html>` is treated as truncated rather than short.
- **Confuse "no experts here" with "we could not read it".** A genuine `0 Einträge` is reported and
  charged nothing; the homepage-instead-of-results response fails the run.
- **Bill for a page it was served twice.** Every page is checked against the "Seite X von Y" label it
  came back with, so a stale pagination token can never quietly re-deliver page 1 fifteen times.

***

*Source: [energie-effizienz-experten.de](https://www.energie-effizienz-experten.de/), the
Energieeffizienz-Expertenliste operated by the Deutsche Energie-Agentur (dena) on behalf of the
German federal government. This Actor reads only the register's public search results. It is not
affiliated with dena.*

# Actor input Schema

## `lanes` (type: `array`):

Which of the register's three searches to run. Selecting more than one runs each in turn over every location you give.

## `locations` (type: `array`):

German postcodes ("10115") or place names ("Berlin", "München") - the site's own field accepts both, verified 2026-09-16 ("Berlin" / 20 km returned 778 experts). Required: this register has no nationwide search, and an empty location makes it answer HTTP 200 with its homepage and no results at all.

## `radiusKm` (type: `string`):

Search radius around each location. These six values are the only ones the site's own dropdown offers; anything else is rejected before a single request is made. Measured around 10115 Berlin: 5 km = 228 experts, 20 km = 774, 250 km = 3,467.

## `maxExperts` (type: `integer`):

Hard cap on rows delivered, and therefore on rows charged. 0 means no cap. The register returns 15 experts per page, so a cap of 150 reads 10 pages. Counted across every lane and location in this run.

## `programmes` (type: `array`):

Leave empty for every expert in the area regardless of accreditation.

## `measureGroups` (type: `array`):

The BEG Einzelmaßnahmen groups. This filter only exists on the residential lane's own search form. Measured on Berlin / 250 km: no filter 3,467 experts, Heizung alone 3,226, Wärmedämmung + Lüftung together 3,305. (Those two numbers do not isolate whether several groups combine as AND or OR, and this description will not pretend they do.)

## `nameQuery` (type: `string`):

The register's own name box - matches an expert's surname or their firm. Measured: "Müller" within 250 km of Berlin returns 39 of the 3,467. Combined with everything above using AND.

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

Drop experts who publish no email. 5.0% of the register's rows have none.

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

Drop experts who publish no phone number. 12.3% of the register's rows have none.

## `requireContact` (type: `boolean`):

Drop experts who publish neither. 2.8% of rows publish neither - they are still real register entries with a name, a firm and a postal address.

## `deduplicate` (type: `boolean`):

Recommended. Two locations 100 km apart at a 250 km radius overlap heavily, and this register publishes no expert id of any kind, so rows are matched on name + postcode + street + firm. A dropped duplicate is never charged. Measured on the build fixtures: 213 parsed rows collapsed to 179 distinct experts.

## `includeProgrammeMatrix` (type: `boolean`):

ON: every row carries a `programmes` array with each funding programme the register lists for that expert and whether its box is ticked - 7 to 29 entries per expert, 15 on average. OFF: the row keeps only `accreditedProgrammes` (the ticked labels) and the three `serves…` flags, which is a much smaller row for a CRM import.

## `maxConcurrency` (type: `integer`):

Result pages fetched at the same time. 3 is a deliberately polite default against a small government-funded site: it is about 45 pages (675 experts) a minute. Each page is 287-395 KB.

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

German residential IPs by default. Verified 2026-09-16 that the site answers 200 through Apify RESIDENTIAL, through the datacenter pool and through BUYPROXIES94952 alike, so datacenter also works and is cheaper - but the result pages are ~300 KB each, which is the size at which a datacenter proxy starts truncating a body mid-stream while still returning HTTP 200, and residential gives a far larger pool of IPs for a throttle that is scoped to the IP.

## Actor input object example

```json
{
  "lanes": [
    "residential"
  ],
  "locations": [
    "10115"
  ],
  "radiusKm": "5",
  "maxExperts": 15,
  "programmes": [],
  "measureGroups": [],
  "requireEmail": false,
  "requirePhone": false,
  "requireContact": false,
  "deduplicate": true,
  "includeProgrammeMatrix": true,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per expert: name, firm, published email and phone, website, full postal address, training, self-declared business type, and the register's own accreditation matrix of which federal funding programmes they may sign off.

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

RUN\_SUMMARY: what each search reported versus what was read, rows delivered and charged, duplicates dropped across overlapping radii, field fill counted on the delivered rows, and how many published email blocks reconstructed into a valid address.

# 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 = {
    "lanes": [
        "residential"
    ],
    "locations": [
        "10115"
    ],
    "radiusKm": "5",
    "maxExperts": 15,
    "deduplicate": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/energie-effizienz-experten-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 = {
    "lanes": ["residential"],
    "locations": ["10115"],
    "radiusKm": "5",
    "maxExperts": 15,
    "deduplicate": True,
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/energie-effizienz-experten-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 '{
  "lanes": [
    "residential"
  ],
  "locations": [
    "10115"
  ],
  "radiusKm": "5",
  "maxExperts": 15,
  "deduplicate": true
}' |
apify call scrapersdelight/energie-effizienz-experten-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/energie-effizienz-experten-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/3TCzyklKmrdwu2VWn/builds/bdelemoY8HZPNBY3Z/openapi.json
