# Impressum Extractor: DACH Register, VAT ID and Directors (`nanoscrape/impressum-extractor`) Actor

Paste company websites, get the legal notice (Impressum) as structured data: company name, legal form, address, register court and number, VAT id, managing directors, responsible person, e-mail and phone. Germany, Austria, Switzerland.

- **URL**: https://apify.com/nanoscrape/impressum-extractor.md
- **Developed by:** [NanoScrape](https://apify.com/nanoscrape) (community)
- **Categories:** Lead generation, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Impressum Extractor - DACH Company Legal Notice, Register, VAT ID, Directors

Paste a list of company websites and get the legal notice (Impressum) of each one as structured data: company name, legal form, address, register court and number, VAT id, managing directors, responsible person, e-mail and phone. Built for Germany, Austria and Switzerland. One row per website, ready for lead generation, company data enrichment and KYC onboarding checks.

No login, no API key and no proxy setup required.

***

### Features

- One row per website, in input order, with a clear `status` for every site
- Legal identity: `company_name`, `legal_form` (GmbH, AG, KG, GmbH & Co. KG, UG, e.K., SE, eG, e.V., GesmbH, OG, Sàrl, SA, Sagl and more)
- Address split into `street`, `postal_code`, `city`, `country` (DE, AT, CH, LI) plus `full_address`
- People: `managing_directors`, `representatives` (name plus the role wording as published), `supervisory_board_chair`
- Register data: `register_court`, `register_type` (HRB, HRA, GnR, PR, VR, FN, ZVR, UID), `register_number`, and other register numbers named on the same page
- Tax data: `vat_id` (USt-IdNr, ATU, CHE MWST), `tax_number`, `share_capital`, and other VAT ids named on the same page
- Contact data: `emails` (protected and "(at)" spellings decoded), `phones` in E.164 format plus `phones_raw` as written, `fax`
- Responsibility and supervision: `responsible_for_content` (V.i.S.d.P., section 18 MStV), `publisher`, `supervisory_authority`, `chamber`, `professional_rules`, `business_purpose`
- `impressum_url` of the page the data came from, and the cleaned `impressum_text` (first 8,000 characters)
- German, English, French and Italian legal pages (Impressum, Imprint, Legal notice, Rechtliche Hinweise, Mentions légales, Note legali)
- Missing fields are `null` (single values) or `[]` (lists), so every row has the same shape
- Pay only for rows where the legal notice was found

***

### Sample input

```json
{
  "websites": ["bosch.de", "wienenergie.at"],
  "maxPagesPerSite": 3,
  "language": "auto",
  "maxResults": 100
}
```

Domains and URLs are both fine, with or without `https://` and `www`. Paths and tracking parameters are ignored, duplicates are removed. An e-mail address is reduced to its domain.

### Sample output

```json
{
  "website": "https://www.bosch.de",
  "status": "ok",
  "status_detail": null,
  "impressum_url": "https://www.bosch.de/impressum/",
  "company_name": "Robert Bosch GmbH",
  "legal_form": "GmbH",
  "street": "Robert-Bosch-Platz 1",
  "postal_code": "70839",
  "city": "Gerlingen-Schillerhöhe",
  "country": "DE",
  "full_address": "Robert-Bosch-Platz 1, 70839 Gerlingen-Schillerhöhe",
  "managing_directors": ["Dr. Christian Fischer", "Dr. Markus Forschner", "Stefan Grosch", "Dr. Markus Heyn", "Dr. Frank Meyer", "Katja von Raven", "Dr. Tanja Rückert"],
  "register_court": "Amtsgericht Stuttgart",
  "register_type": "HRB",
  "register_number": "14000",
  "vat_id": "DE811128135",
  "emails": ["kontakt@bosch.de"],
  "phones": ["+4971140040990"],
  "phones_raw": ["+49 711 400 40990"],
  "responsible_for_content": "Dr. Michael Schmidtke",
  "pages_fetched": 2,
  "scraped_at": "2026-11-04T16:00:00Z"
}
```

(The output above is shortened; every row carries all fields listed in the dataset schema.)

***

### Input fields

| Field | Type | Default | Description |
|---|---|---|---|
| `websites` | list of strings | bosch.de | Company websites, domains or URLs. One row per website. |
| `maxPagesPerSite` | integer 2-6 | 3 | Pages requested per website: the homepage, the legal page and fallback paths. |
| `language` | auto, de, en, fr, it | auto | Preferred language of the legal page. Auto follows the homepage language. |
| `maxResults` | integer | 100 | Maximum number of websites processed, which is the maximum number of rows. |
| `maxConcurrency` | integer 1-20 | 5 | Websites processed in parallel. |

#### How the legal page is found

The actor reads the homepage and looks for a link labelled Impressum, Imprint, Legal notice, Rechtliche Hinweise, Rechtliches, Mentions légales, Note legali or Kontakt & Impressum, preferring links in the footer and links on the same site. If there is no such link it tries the common paths `/impressum`, `/imprint`, `/de/impressum`, `/legal` and `/legal-notice`. A privacy policy is not accepted as a legal notice.

### FAQ

**The homepage itself shows the register number or VAT ID, but there is no legal page. What do I get?**
A row with `status: ok` and `impressum_url` set to the homepage. `status_detail` says "legal data found on the homepage". The row is charged.

**The homepage and the legal page both return 403 or a block page. What do I get?**
A row with `status: not_reachable` (for example `http_403`). It is not charged.

**The homepage is blocked (403) but the legal page is open. What do I get?**
After a 401, 403, 429 or 5xx on the homepage the actor still tries the usual legal paths such as `/impressum` and `/service/impressum`. If one of them returns a legal notice, the row is `ok` and charged. A 404 on a guessed path never counts as a legal notice.

**The Impressum link is not a normal link in the page.**
Footers that ship their links as JSON (common on JavaScript sites) are read too.

### Status values

| `status` | Meaning | Charged |
|---|---|---|
| `ok` | The legal notice was found and read. | Yes |
| `no_impressum_found` | The site loaded, but no legal page was found within `maxPagesPerSite`. | No |
| `not_reachable` | The site or its legal page could not be read. | No |

`status_detail` explains every status other than `ok`: `http_403`, `http_404`, `js_required`, `bot_challenge`, `cookie_wall`, `consent_redirect`, `off_site_redirect`, `empty_response`, `timeout`, `dns_error`, `connection_failed`, `tls_error`, `legal_page_<reason> (url)` when the legal page itself could not be read, or `no legal page found (checked N pages)`.

### Pricing (Pay-per-event)

| Event | Description | When it fires |
|---|---|---|
| `impressum-result` | One website with a legal notice read | Once per row with `status: ok`, after the row is delivered |

Price: $0.001 per run start plus $0.003 per Impressum found ($3.00 per 1,000 results). Rows with `no_impressum_found` or `not_reachable` are free. The actor stops cleanly when your maximum total charge is reached.

***

### Limits

- Websites that only show their content after JavaScript runs come back as `not_reachable` with `js_required`. The actor does not run scripts.
- Cookie walls, consent pages and bot challenges are not clicked through or worked around. These sites come back as `not_reachable` (`cookie_wall`, `consent_redirect`, `bot_challenge`).
- Switzerland has no general Impressum duty. Many Swiss sites publish little or nothing, so expect more `no_impressum_found` rows and sparser fields there.
- Fields are read from the text of the notice. If a site leaves placeholders such as "\[bitte ergänzen]", those fields stay `null`.
- Sites that publish several legal entities on one page return the first one in the main fields and the others in `additional_register_numbers` and `additional_vat_ids`.
- The data is what the website publishes. You are responsible for lawful use of personal data such as names, e-mail addresses and phone numbers (GDPR, UWG and national marketing rules).

### Use with AI agents (MCP)

This actor works with the Apify MCP server, so AI assistants can call it as a tool. Actor id: `nanoscrape/impressum-extractor`.

Endpoint: `https://mcp.apify.com?tools=nanoscrape/impressum-extractor`

Example prompt: "Get the register number, VAT id and managing directors for bosch.de and wienenergie.at."

***

### Support

Questions, bugs and feature requests: <contact@nanoscrape.com> or open an issue via the Apify Console.

***

*Built by NanoScrape.*

# Changelog

This Actor's version history is a separate document: https://apify.com/nanoscrape/impressum-extractor/changelog.md

# Actor input Schema

## `websites` (type: `array`):

Company websites to read the legal notice from. Domains or URLs, with or without https:// and www, paths and tracking parameters are ignored. One row is returned per website.

## `maxPagesPerSite` (type: `integer`):

Pages requested per website: the homepage, the legal page and fallback paths. 3 means the homepage plus up to two legal page candidates.

## `language` (type: `string`):

Preferred language of the legal page. Auto uses the language of the homepage.

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

Maximum number of websites processed, which is the maximum number of rows. Only rows with status ok are charged.

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

Websites processed in parallel. Each website is read one page at a time.

## Actor input object example

```json
{
  "websites": [
    "bosch.de",
    "wienenergie.at"
  ],
  "maxPagesPerSite": 3,
  "language": "auto",
  "maxResults": 100,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `legalNotices` (type: `string`):

Dataset with one row per website.

# 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 = {
    "websites": [
        "bosch.de",
        "wienenergie.at"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nanoscrape/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 = { "websites": [
        "bosch.de",
        "wienenergie.at",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("nanoscrape/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 '{
  "websites": [
    "bosch.de",
    "wienenergie.at"
  ]
}' |
apify call nanoscrape/impressum-extractor --silent --output-dataset

```

## MCP server setup

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