# DACH Impressum Extractor (`beltech/dach-impressum-extractor`) Actor

Company website in, legal company data out. Reads the Impressum of German, Austrian and Swiss websites: legal name, legal form, managing directors, address, register court and number, VAT ID (checked against VIES), phone, email and industry.

- **URL**: https://apify.com/beltech/dach-impressum-extractor.md
- **Developed by:** [Bel](https://apify.com/beltech) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 company extracteds

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

## DACH Impressum Extractor

Give it the website of a German, Austrian or Swiss company and get the legal company data back as a clean row: legal name, legal form, managing directors, address, register court and number, VAT ID, phone, email and industry.

The data comes from the Impressum (imprint), which the law requires on business websites in Germany, Austria and Switzerland. It is public, it follows a predictable layout and it is almost never behind bot protection. Fields are read with rules first. An AI model only fills what the rules could not read, and every value it returns must appear in the page text, otherwise it is dropped.

Use it to enrich lead lists, check who is behind a website, or prepare KYC and onboarding forms.

### Copy-paste prompt for your agent

Add this to your agent's instructions or system prompt. It tells the agent when and how to use this Actor.

```text
You can look up legal company data of German, Austrian and Swiss websites with the Apify Actor
"beltech/dach-impressum-extractor". Call it with the Apify MCP tool call-actor, then read the rows
with get-dataset-items using the returned datasetId. The run's statusMessage contains a one-line summary.

WHEN TO USE IT
- The user wants to know who is behind a website, needs the legal name, address, register number,
  VAT ID or managing directors of a company, or wants to enrich a list of company domains.
- Only for companies in Germany, Austria and Switzerland. Websites of other countries return the status
  "not_dach" or "no_impressum". Do not use it for private persons' profiles or social media pages.

HOW TO BUILD THE INPUT
- "urls": the company websites or bare domains, for example ["example.de", "https://www.firma.at"].
  One entry per company. Up to 1000 per run. The path does not matter, the Actor finds the Impressum itself.
- Leave "useAI" and "checkVat" on true unless the user wants the cheapest possible run.
- Set callOptions.maxTotalChargeUsd to about 0.01 per URL to cap the cost.

HOW TO READ THE RESULT
- One row per input URL. Look at "status" first. Only "ok" rows contain data.
  no_impressum = no Impressum page found, not_reachable = site down or invalid, blocked = the site
  or its robots.txt refuses access, not_dach = the company is outside Germany, Austria and Switzerland.
- "confidence" high means name, address and a register number or VAT ID were found. For low confidence,
  tell the user that the data is incomplete and name the empty fields.
- "vat_valid": true or false is the official EU VIES answer. null means not checked or VIES had no
  definite answer (Swiss VAT IDs cannot be checked). Never call a null VAT ID invalid.
- "industry" is an AI estimate from a fixed list, shown with "industry_confidence". Say so.
- Use only values from the result. Never fill empty fields from memory.
- Managing directors are personal data. Use them only for a lawful purpose.
```

### What you get

One row per input URL. Every row has the same fields.

| Field | Description |
|---|---|
| `input_url` | The website as you entered it |
| `status` | `ok`, `no_impressum`, `not_reachable`, `blocked` or `not_dach` |
| `impressum_url` | The page the data was read from |
| `company_name` | Legal company name |
| `legal_form` | For example GmbH, UG (haftungsbeschränkt), AG, e.K., GmbH & Co. KG |
| `managing_directors` | List of managing directors, owners or board members |
| `address` | `street`, `postal_code`, `city`, `country` |
| `register_court` | For example Amtsgericht München |
| `register_number` | For example HRB 12345 or FN 123456a |
| `vat_id` | VAT ID (USt-IdNr.) |
| `vat_valid` | VIES result: true, false, or null when not checked |
| `phone`, `email` | Contact details from the Impressum |
| `industry` | One class from a fixed list of 23 industries |
| `industry_confidence` | high, medium or low |
| `confidence` | high, medium or low, based on how complete the row is |
| `extracted_by` | `rules` or `rules+ai` |
| `checked_at` | ISO timestamp |

### Input

```json
{
  "urls": ["hetzner.com", "https://www.heise.de", "willhaben.at"],
  "useAI": true,
  "checkVat": true
}
```

| Field | Description |
|---|---|
| `urls` | Company websites or bare domains, up to 1000 |
| `useAI` | Let AI fill missing fields and assign an industry. Default true. Off means rules only: cheaper, lower recall |
| `checkVat` | Check found VAT IDs against the EU VIES service. Default true |

An empty input runs a demo with one example website, so the Actor never fails on default values.

### Example output

```json
{
  "input_url": "willhaben.at",
  "status": "ok",
  "impressum_url": "https://www.willhaben.at/iad/impressum",
  "company_name": "willhaben internet service GmbH",
  "legal_form": "GmbH",
  "managing_directors": ["Mag. Sylvia Dellantonio"],
  "address": {
    "street": "Landstraßer Hauptstraße 97-101",
    "postal_code": "1030",
    "city": "Wien",
    "country": "AT"
  },
  "register_court": null,
  "register_number": "FN 271791v",
  "vat_id": "ATU62224319",
  "vat_valid": true,
  "phone": null,
  "email": null,
  "industry": "Retail and e-commerce",
  "industry_confidence": "high",
  "confidence": "high",
  "extracted_by": "rules+ai",
  "checked_at": "2026-10-23T06:10:00+00:00"
}
```

### How it works

1. The Actor opens the home page and looks for the Impressum link in the footer and menu. If there is none, it tries the usual paths such as `/impressum` and `/kontakt`.
2. Rules read VAT ID, register number, email, phone, address, legal form and managing directors from the cleaned page text.
3. If fields are still empty and AI is on, one model call reads the Impressum text. Every name, street and city it returns must be found in the page text. Anything else is discarded. The industry is the only value the model may infer, from the page text and the home page title.
4. Found VAT IDs are checked against the EU VIES service.

The Actor is polite: it follows robots.txt, makes at most one request per second to the same website and reads only the pages it needs.

### Pricing

Pay per event. No platform usage is billed on top.

| Event | Price | When |
|---|---|---|
| Company extracted | $0.008 | One website with a found Impressum (status `ok`) |
| VAT ID checked | $0.002 | One VAT ID with a definite VIES answer |

Rows with the status `no_impressum`, `not_reachable`, `blocked` or `not_dach` are not charged. A website with a VAT ID costs $0.010 in total.

### Good to know

- Germany, Austria and Switzerland only.
- Public pages only. Sites that block automated access or disallow it in robots.txt return `blocked`.
- Some sites load the Impressum with JavaScript or publish it as an image or PDF. Those return `no_impressum` or a row with fewer fields.
- Rows with low confidence are possible, for example a company name without an address or a run with AI turned off. A row gets `status` `ok` only if it has a company name or a postal code plus city.
- Swiss VAT IDs (MWST) are not part of VIES, so `vat_valid` stays null for them.
- Names of managing directors and owners are personal data. You are responsible for using them lawfully, for example under GDPR.
- This Actor is not affiliated with any of the companies or services it reads.

### FAQ

**Does it work for small businesses and freelancers?**
Yes. Sole proprietors, e.K., UG, GmbH & Co. KG and associations are covered. Freelancers often have no register number, so the confidence is medium.

**What if the Impressum is missing?**
The row gets the status `no_impressum` and you are not charged. The Actor never invents a company.

**Can I turn the AI off?**
Yes, set `useAI` to false. You then only get what the rules can read, and no industry.

**How accurate is the VAT check?**
`vat_valid` is the answer of the official EU VIES service. If VIES is down, the value is null and the row is still returned.

# Actor input Schema

## `urls` (type: `array`):

Websites or bare domains of German, Austrian or Swiss companies (up to 1000), for example example.de. One result row per entry.

## `useAI` (type: `boolean`):

Fills in what the rules could not read (company name, managing directors, address) and assigns an industry. Values are only accepted if they appear in the page text. Off means rules only: cheaper, lower recall.

## `checkVat` (type: `boolean`):

Validates found VAT IDs with the EU VIES service. Swiss VAT IDs cannot be checked and stay empty.

## Actor input object example

```json
{
  "urls": [
    "https://www.golem.de"
  ],
  "useAI": true,
  "checkVat": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `result` (type: `string`):

No description

# 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 = {
    "urls": [
        "https://www.golem.de"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("beltech/dach-impressum-extractor").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 = { "urls": ["https://www.golem.de"] }

# Run the Actor and wait for it to finish
run = client.actor("beltech/dach-impressum-extractor").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 '{
  "urls": [
    "https://www.golem.de"
  ]
}' |
apify call beltech/dach-impressum-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,beltech/dach-impressum-extractor"
        }
    }
}
```

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/yVMEheoqpZUJj97f2/builds/xPkaL9EUiHqeuWkZE/openapi.json
