# Impressum Scraper - German Company Imprint (GmbH, HRB, VAT) (`lukehunter/impressum-scraper`) Actor

Impressum scraper for company websites in Germany, Austria and Switzerland: legal name and form (GmbH, UG, AG, KG), managing directors, HRB/HRA or FN register, VAT id and address from the legal imprint. $0.006 per company, only when found.

- **URL**: https://apify.com/lukehunter/impressum-scraper.md
- **Developed by:** [Luke Hunter](https://apify.com/lukehunter) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$6.00 / 1,000 company with legal entity data

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 Scraper - German Company Imprint (GmbH, HRB, VAT)

**Impressum scraper for sales teams, KYC and onboarding teams, data providers and researchers who need legal entity data for a list of companies in Germany, Austria and Switzerland.** Give it company websites and you get one row per company with what its legally required imprint (Impressum) publishes: **legal name and legal form (GmbH, UG, AG, KG, GmbH & Co. KG, e.K., e.U., SARL ...), managing directors with their role, register court and number (HRB/HRA, FN, CHE), VAT id (USt-IdNr), postal address, phone, fax, email and the person responsible for the content**, with the page it was read from. It finds the imprint page for you (home page links, then the usual paths, then the sitemap), also in French (mentions légales) and Italian (note legali).

It takes your list the way you have it: **paste domains**, give it **a link to a CSV file**, or **hand it another Actor's dataset** (for example Google Maps Scraper results with a `website` field), and turn on **change mode** to run it on a schedule and hear only about companies whose legal data changed (new managing director, new address, new VAT id).

Pay-per-result: **$0.006 per company where a legal entity is found - 1,000 companies = $6.** Sites with no imprint, no legal entity, blocked, unreachable or disallowing reading in `robots.txt` are delivered with a clear status and are **never charged**.

### Impressum scraper quick start (1 minute)

1. Open the **Input** tab and paste company domains or website URLs (one per line, or a whole column at once).
2. Click **Start**, then export the dataset to CSV/Excel/JSON or pull it through the API shown below.

```json
{
  "websites": ["heise.de", "golem.de", "spiegel.de"],
  "maxPagesPerSite": 4,
  "maxItems": 100
}
```

That prefilled example reads three company sites, up to four pages each at one page per second, and costs at most $0.018 (only companies where a legal entity is found are charged). A URL with a path works too (`"https://www.example-firma.de/rechtliches/impressum"`): it is read as the first extra page after the home page.

### What you get

| Field | What it holds |
|---|---|
| `legalName`, `legalForm` | The company name as the imprint prints it, e.g. `Musterbau GmbH & Co. KG`, and its form normalised to one spelling, e.g. `GmbH & Co. KG` |
| `managingDirectors` | `[{name, role}]`: Geschäftsführer, Vorstand, Inhaber, Verwaltungsrat, gérant, Managing Director ..., names and roles as published |
| `registerCourt`, `registerType`, `registerNumber` | `Amtsgericht München`, `HRB`, `HRB 123456`; Austria `FN 123456a` with `Handelsgericht Wien`; Switzerland `CHE-123.456.789`; France `RCS Paris B 123 456 789` |
| `vatId` | USt-IdNr / UID / TVA / P.IVA validated by format: `DE` + 9 digits, `ATU` + 8 digits, `CHE-123.456.789 MWST`, `FR` + 11, `IT` + 11 |
| `address` | `{street, postalCode, city, country}` of the legal entity |
| `phone`, `fax`, `email` | Exactly as the imprint prints them (an "info \[at] firma \[dot] de" spelling is written as the address it spells) |
| `responsibleForContent` | The V.i.S.d.P. / "Verantwortlich nach § 18 MStV" / directeur de la publication person |
| `imprintUrl`, `confidence`, `notes` | The page it was read from, `high` / `medium` / `low`, and anything worth knowing |
| `status`, `statusReason` | `ok`, `no-legal-entity`, `no-imprint-found`, `blocked`, `unreachable`, `robots-disallowed`, and why |

Nothing is guessed or enriched: every value is on the imprint page the row points to. Open `imprintUrl` to check any row.

### Example output

```json
{
  "inputUrl": "musterbau-beispiel.de",
  "domain": "musterbau-beispiel.de",
  "finalDomain": "musterbau-beispiel.de",
  "imprintUrl": "https://musterbau-beispiel.de/impressum",
  "status": "ok",
  "statusReason": null,
  "legalName": "Musterbau GmbH & Co. KG",
  "legalForm": "GmbH & Co. KG",
  "managingDirectors": [
    { "name": "Hans Muster", "role": "Geschäftsführer der Komplementärin" },
    { "name": "Greta Muster", "role": "Geschäftsführer der Komplementärin" }
  ],
  "registerCourt": "Amtsgericht Köln",
  "registerType": "HRA",
  "registerNumber": "HRA 4711",
  "vatId": "DE987654321",
  "address": { "street": "Am Beispielhafen 5", "postalCode": "50667", "city": "Köln", "country": "DE" },
  "phone": "0221 987654-0",
  "fax": "0221 987654-9",
  "email": "info@musterbau-beispiel.example",
  "responsibleForContent": null,
  "confidence": "high",
  "notes": "address country (DE) taken from the register or VAT id format, not printed on the page",
  "pagesCrawled": 2,
  "changedFields": null,
  "scrapedAt": "2026-10-10T12:00:00.000Z"
}
```

The company in this example is invented. A company with nothing to report comes back as a short row, e.g. `"status": "no-imprint-found"` with a `statusReason`, and no entity fields.

### What it reads, and where

- **Germany:** the Impressum (§ 5 DDG / TMG, § 18 MStV): Firma and legal form, Vertretungsberechtigte, Registergericht and Registernummer (HRB, HRA, GnR, VR, PR), Umsatzsteuer-Identifikationsnummer, Telefon, Telefax, E-Mail, V.i.S.d.P.
- **Austria:** Impressum / Offenlegung (ECG, MedienG): Firmenbuchnummer (FN with its letter), Firmenbuchgericht (Handelsgericht, Landesgericht), UID (ATU), Geschäftsführer.
- **Switzerland:** Impressum: UID (CHE-xxx.xxx.xxx, with MWST/TVA/IVA as the VAT id), Handelsregisteramt, Verwaltungsrat or Geschäftsleitung.
- **France and Italy:** mentions légales (SARL, SAS, RCS, TVA intracommunautaire, gérant, directeur de la publication) and note legali (S.r.l., S.p.A., P.IVA, REA, amministratore).
- **Legal forms:** GmbH, gGmbH, UG (haftungsbeschränkt), AG, SE, KG, KGaA, OHG, GbR, eG, e.V., e.K., GmbH & Co. KG (and AG/UG/SE & Co. KG), PartG mbB, GesmbH, OG, KEG, e.U., SARL, SAS, SASU, EURL, SA, Sagl, S.r.l., S.p.A., S.n.c., B.V., N.V., Ltd, LLC, Inc.

### How it finds the imprint page

At most four page requests per site by default (home page included; up to six):

1. **The home page**, and the imprint links on it: a footer link called "Impressum", "Imprint", "Legal notice", "Mentions légales", "Note legali", "Offenlegung" or "Anbieterkennzeichnung", under any path.
2. **The usual paths:** `/impressum`, `/imprint` (French sites start with `/mentions-legales`, Italian ones with `/note-legali`).
3. **The sitemap** (from `robots.txt` or `/sitemap.xml`), looking for an imprint page under an unusual path.
4. The remaining paths: `/legal-notice`, `/impressum.html`.

It stops at the first page that states a legal entity. A privacy policy, cookie page or terms page is never taken for an imprint, even when it names the controller with address and register number.

**What counts as a legal entity (and is charged):** a legal name that carries a legal form, plus at least one more fact from the same page: a register number, a VAT id, a named director or a postcode. A freelancer or sole proprietor with no legal form and no register entry (just a name and an address) is reported as `no-legal-entity` and not charged, and the row carries none of their details.

### Enrich Google Maps results

Hand it the dataset of a Google Maps Scraper run (or any dataset with a website column) and you get a legal-entity row per business website, with the columns you pick copied into `source` so you can join the result back:

```json
{
  "datasetId": "AbCdEf123456",
  "datasetPassthroughFields": ["title", "placeId"],
  "maxItems": 500
}
```

To chain it automatically, set `datasetId` to `{{resource.defaultDatasetId}}` in the Google Maps Scraper's Integrations tab. Rows without a website, and "websites" that are only a Google Maps, Facebook or Instagram link, are skipped and counted in the `INPUT_REPORT` record. Several branches with one website share one row (`source.sourceRowCount` says how many).

### Track imprint changes on a schedule

Set `onlyChangedSinceLastRun` to `true` and put the task on a Schedule (monthly is plenty: imprints change rarely). The first run delivers every company and remembers its legal data; later runs deliver only companies where something changed and say what in `changedFields` (`managingDirectors`, `address`, `vatId`, `register`, `legalName` ...). Unchanged companies are not delivered and not charged. A site that was read before and is now down, blocked or without an imprint is not a change: its last known data is kept. Whitespace, capitalisation and phone punctuation changes are ignored.

### Input

| Input | Meaning |
|---|---|
| `websites` | Company domains or URLs; de-duplicated by domain; up to 5,000 per run. Required unless `websitesFileUrl` or `datasetId` is given |
| `websitesFileUrl` | A public https link to a `.csv` or `.txt` list (max 5 MB, 20,000 lines) |
| `datasetId`, `datasetWebsiteField`, `datasetPassthroughFields` | Read the websites of another Actor's dataset; copy chosen columns into `source` |
| `maxPagesPerSite` | 1-6, default 4: page requests per site, home page included |
| `maxItems` | 1-5,000, default 100: the most companies returned, and your cost cap |
| `outputFormat` | `full` (default) or `compact` (a much smaller row for AI agents) |
| `onlyChangedSinceLastRun`, `stateStoreName` | Change mode and the name of the store that remembers the last run |

Entries that are not websites (a header cell, a free-mail address, `localhost`) are skipped and listed with their reason in the run status and in the `INPUT_REPORT` record of the key-value store. A bad entry never fails the run; only a list with nothing usable does.

### Data protection

This Actor only reads what a company publishes itself on the legal notice of its own website, which the law requires it to publish. It respects `robots.txt`, does not log in, and does not look anything up outside the sites you list (VAT ids are checked by format, not against a register). Managing directors and the person responsible for the content are named individuals, so their names are personal data under the GDPR even though the company published them: **you are responsible for having a lawful basis for your use of the data**, for example for B2B due diligence, KYC or a documented legitimate-interest assessment, and for honouring deletion requests. Imprints of sole proprietors and freelancers often carry a private address; rows without a legal form carry no entity data at all. This is general information, not legal advice.

### Use it as an API

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("lukehunter/impressum-scraper").call(
    run_input={"websites": ["heise.de", "golem.de"], "maxPagesPerSite": 4}
)

for company in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(company["domain"], company["status"], company["legalName"], company["registerNumber"], company["vatId"])
```

Or with one HTTP call that runs the Actor and returns the data:

```bash
curl -s -X POST "https://api.apify.com/v2/acts/lukehunter~impressum-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"websites": ["heise.de", "golem.de"], "maxItems": 10}'
```

### Use with AI agents (MCP)

This Actor works as a tool for Claude, ChatGPT, Cursor and other MCP clients through [Apify's MCP server](https://mcp.apify.com). The only input a call needs is `websites`.

**Claude Desktop** (`claude_desktop_config.json`), with your own token from Apify Console > Settings > API & Integrations:

```json
{
  "mcpServers": {
    "apify-imprint": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://mcp.apify.com?tools=lukehunter/impressum-scraper",
        "--header", "Authorization: Bearer ${APIFY_TOKEN}"
      ],
      "env": { "APIFY_TOKEN": "<YOUR_APIFY_TOKEN>" }
    }
  }
}
```

Clients that support OAuth (Claude.ai, VS Code) can connect with just the URL `https://mcp.apify.com?tools=lukehunter/impressum-scraper` and sign in to Apify. Never paste a real token into a shared file or chat.

**Example.** You ask: "Who is the legal entity behind heise.de, and what is its register number and VAT id?" The agent calls the tool `lukehunter--impressum-scraper` with:

```json
{
  "websites": ["heise.de"],
  "outputFormat": "compact",
  "maxItems": 5
}
```

`outputFormat: "compact"` returns a small row per company (legal name and form, directors, register, VAT id, a one-line address, phone, email and the imprint URL) so the result fits an agent's context. It is charged exactly like the default `"full"` output: $0.006 per company where a legal entity is found, nothing otherwise. Set `maxItems` to cap spend. The `status` and `confidence` fields tell the agent when to say "not found" or to open `imprintUrl` and check.

### Pricing and cost control

Pay per result, and only for a result with a legal entity in it.

**$0.006 per company where a legal entity is found - 1,000 companies = $6.**

| Companies with a legal entity | Cost |
|---:|---:|
| 3 | $0.018 |
| 100 | $0.60 |
| 1,000 | $6.00 |
| 5,000 | $30.00 |

There is no charge for starting a run. One company is one charge. A row with status `no-legal-entity`, `no-imprint-found`, `blocked`, `unreachable` or `robots-disallowed` is delivered but **not charged**, and neither is an entry that is not a website, a duplicate domain or (in change mode) a company whose data did not change. Hard caps: `maxItems` is an upper bound on cost (at most `maxItems` x $0.006), at most five sites are read at a time, each site at about one request per second with a per-site time budget, and the Actor never delivers a charged row your run's maximum-cost setting cannot pay for.

### Limitations

- **It reads what the imprint publishes, nothing more.** It does not look companies up in the Handelsregister, Firmenbuch or Zefix, does not check VAT ids in VIES (they are validated by format only), and does not guess or complete missing fields. A missing field is `null`.
- **Rule-based extraction, not a guarantee.** Imprints are free text. The common layouts (labelled lines, tables, definition lists, one-line imprints) are read; a very unusual layout can give a partial row (`confidence` `low` or `medium`) or no entity. Spot-check rows against `imprintUrl` before relying on them, especially for compliance use.
- **Page budget.** By default four pages per site (home page, then the likeliest imprint pages). An imprint under an unusual path that is not linked from the home page and not in the sitemap is not found: the row says `no-imprint-found`. Raise `maxPagesPerSite` to 6.
- **No JavaScript rendering.** An imprint that a script builds in the browser (some single-page apps), an imprint shown as an image, or an email protected by a script shows nothing in the page's HTML and is not read.
- **One entity per row.** If an imprint lists several companies (a group site), the first legal entity that carries the facts is returned. A Komplementär GmbH named as the legal representative is not listed as a person.
- **Sole proprietors and freelancers without a legal form or register entry** are not reported as legal entities (and not charged). Natural-person data is not output for them.
- **Countries.** Built for Germany, Austria and Switzerland; French and Italian formats are read too. Other countries' imprints work only where they follow the same shape (legal name with form, register number, VAT id).
- **Sites can block automated requests.** If a site answers HTTP 401/403/429, shows a bot check or refuses `robots.txt`, the row is `blocked` and nothing is charged. This Actor does not use proxies, rotate identities, solve CAPTCHAs, accept cookies or try to get around a block.
- **robots.txt is respected** for every website it reads, page by page. A disallowed imprint path is not read (reported in `notes`), and a site whose `robots.txt` disallows its home page is `robots-disallowed`. The one thing it is not applied to is downloading your own list file (`websitesFileUrl`), which is an input document.
- **Dataset input (`datasetId`)** must be one the account that starts this Actor can open (the default dataset of a run in the same account); at most 20,000 rows are read and 5,000 companies per run. It reads the website the row already has and never requests Google Maps or any other site named in the dataset except the companies' own websites.
- **Speed:** about one page per second per site and five sites at a time, so 1,000 sites take roughly 10-20 minutes.
- This is an independent tool, not affiliated with any company whose website it reads. You are responsible for using the collected data in line with the law and the sites' terms.

### Use it from your code, Make, Zapier or n8n

Built for automated pipelines: small runs, stable field names, one row per item, and you pay only for rows actually delivered.

**One API call that runs the Actor and returns the data** (replace `$APIFY_TOKEN` with your token from Apify Console → Settings → Integrations):

```bash
curl -s -X POST "https://api.apify.com/v2/acts/lukehunter~impressum-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"websites": ["heise.de", "spiegel.de", "zeit.de"], "maxPagesPerSite": 4, "maxItems": 20}'
```

**JavaScript** (`npm i apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('lukehunter~impressum-scraper').call({
  "websites": [
    "heise.de",
    "spiegel.de",
    "zeit.de"
  ],
  "maxPagesPerSite": 4,
  "maxItems": 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python** (`pip install apify-client`):

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('lukehunter~impressum-scraper').call(run_input={"websites": ["heise.de", "spiegel.de", "zeit.de"], "maxPagesPerSite": 4, "maxItems": 20})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

**Schedules, webhooks and no-code tools:** save your input as a Task, add a Schedule (e.g. hourly or daily), and attach a webhook or one of the Integrations tab's connectors (Make, Zapier, n8n, Google Sheets, Slack) to push each run's results into your own system.

**Only new items each run:** set `"onlyChangedSinceLastRun": true` on a scheduled Task and every run after the first delivers, and charges for, only items it hasn't delivered before for that same input. That makes it a ready-made change feed.

**Reliability:** an automated test runs this Actor's parser against the live site every day, and failures are fixed fast.

**Feedback:** an honest rating on the Store page, good or bad, helps others decide if this Actor fits their needs. Missing a field or found a problem? Open an issue on the Actor page.

### FAQ

#### What is an Impressum and why does every German company website have one?

German, Austrian and Swiss law (for example § 5 DDG and § 18 MStV in Germany) requires commercial websites to publish who runs them: the legal name and form, address, a way to contact them, the register entry and VAT id where there is one, and who is responsible for the content. That makes the imprint a reliable public source of legal entity data, and this Impressum scraper reads it for you.

#### Is it the same as a Handelsregister lookup?

No. It reads what the company itself prints on its website. It does not query the Handelsregister, Firmenbuch or Zefix, so it cannot tell you whether the entry is current. It does give you the register court and number you would need to look the company up there.

#### Why was a company not charged?

Only status `ok` is charged: a legal name with a legal form plus a register number, VAT id, director or postcode on a real imprint page. `no-legal-entity` (an imprint with no complete entity, for example a freelancer), `no-imprint-found`, `blocked`, `unreachable` and `robots-disallowed` are free, and say why in `statusReason`.

#### Does it work for companies outside Germany, Austria and Switzerland?

French mentions légales and Italian note legali are read. For other countries it works where the imprint has the same shape; otherwise the row says `no-legal-entity` or `no-imprint-found`, free of charge.

#### Why is `address.country` set when the page does not print a country?

When the page prints none, the country is derived from the register or VAT id format (an `HRB` number or a `DE` VAT id means Germany). `notes` says so when that happened.

#### Are VAT ids verified?

They are validated by format (DE + 9 digits, ATU + 8, CHE-123.456.789 MWST, FR + 11, IT + 11) and written without spaces. They are not looked up in VIES.

#### Can I just give it a domain?

Yes. Give it `heise.de`, `www.heise.de` or `https://www.heise.de/impressum`; all of them work, and the first two are found through the home page and the usual paths.

#### How do I only get companies whose legal data changed?

Set `onlyChangedSinceLastRun` to `true` and run the task on a Schedule. The first run delivers everything; later runs deliver only changes, with `changedFields`.

#### Does it respect robots.txt?

Yes, for every page it requests. See Limitations.

#### How do I join the results back to my Google Maps rows?

Use `datasetId` with `datasetPassthroughFields` (for example `["title", "placeId"]`): each result carries a `source` object with those columns.

### Related Actors

Other data tools from the same developer, built to the same standard: official or public sources, hard cost caps, and honest documentation of limits.

- **[Domain Email Provider Lookup](https://apify.com/lukehunter/email-provider-mx-lookup)**: the email provider (Google Workspace, Microsoft 365, Zoho and more), MX, SPF and DMARC setup of any domain list, with provider-change alerts.
- **[French Company Leads](https://apify.com/lukehunter/french-companies-sirene)**: French companies (entreprises) from the official Sirene-based API, filtered by NAF code, department, headcount and creation date, with SIREN/SIRET, officers and new-company alerts.
- **[Poland KRS Company Lookup](https://apify.com/lukehunter/poland-krs-company-lookup)**: Polish companies from the official KRS API by KRS number: NIP, REGON, address, share capital, PKD and the board as published, with optional VAT status.
- **[Website Contact Scraper](https://apify.com/lukehunter/company-contact-scraper)**: emails, phone numbers and social profile links from any list of company domains, with the page each came from and a changed-contacts-only mode for scheduled runs.
- **[Website Tech Stack Detector](https://apify.com/lukehunter/website-tech-stack-detector)**: the technologies a website runs (CMS, ecommerce platform, analytics, ad pixels, chat, payments, CDN, frameworks) from its home page, with the evidence for each match and a changed-technologies-only mode for scheduled runs.
- **[Google Maps Email Extractor & Lead Enricher](https://apify.com/lukehunter/lead-enricher)**: emails, phones, social links, named people and the tech stack of every business in your Google Maps Scraper results, one enriched row per business, with filters such as only Shopify stores with an email.
- **[Federal Register Rule Effective-Date Radar](https://apify.com/lukehunter/federal-rule-effective-date-radar)**: final federal rules about to take effect, with citations, dockets and urgency.
- **[UK Tenders Monitor](https://apify.com/lukehunter/uk-tenders-monitor)**: new UK public-sector tenders from Contracts Finder and Find a Tender, filtered by CPV code, value, region and buyer, with Slack, Discord and Telegram alerts.

# Actor input Schema

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

REQUIRED unless websitesFileUrl or datasetId is given. Company websites whose imprint (Impressum, Legal notice, Mentions légales, Note legali) to read, as domains or URLs, e.g. \["heise.de", "https://www.example-firma.de"]. One per line or a pasted column. De-duplicated by domain; up to 5,000 per run. Unusable entries are listed in INPUT_REPORT, never an error.

## `websitesFileUrl` (type: `string`):

Optional https link to a public .csv or .txt file of company websites, e.g. "https://example.com/companies.csv" (a Google Sheets "Publish to web" CSV link, Dropbox shared link or GitHub Raw link works). Max 5 MB / 20,000 lines; every cell is read like an entry of the list above, and the result is merged and de-duplicated with it. Must open without signing in.

## `datasetId` (type: `string`):

Optional. ID of an Apify dataset whose rows have a `website` field, typically a Google Maps Scraper run (defaultDatasetId), e.g. "AbCdEf123456". Each row's website is read for its imprint; `source` carries the row's fields (title, placeId ...) to join back. To chain, set it to {{resource.defaultDatasetId}} in the source Actor's Integrations tab. Up to 20,000 rows. Merged with Websites and the file URL.

## `datasetWebsiteField` (type: `string`):

Name of the dataset column holding the website, exactly as spelled, e.g. "website". Only used with a dataset. Leave empty to auto-detect (website, websiteUrl, web, url, domain, homepage, companyWebsite, company_website, site; the first non-empty wins). Google Maps, social network and marketplace links are never read as a website.

## `datasetPassthroughFields` (type: `array`):

Optional, only with datasetId. Extra dataset columns to copy into each result's `source` object, e.g. \["title", "placeId"], max 15 columns. Leave empty for the common Google Maps columns (title, placeId, address, city, countryCode, phone, categoryName, totalScore, reviewsCount, url). Values are cut at 2,000 characters. datasetRow, sourceRowCount and otherRows are always added and reserved.

## `outputFormat` (type: `string`):

"full" (default): every field. "compact": a much smaller row for AI agents, with only domain, status, legalName, legalForm, managingDirectors, registerCourt, registerNumber, vatId, a one-line address, phone, email and imprintUrl. Charging is identical in both formats. Example: "compact" when the result is read into an LLM context.

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

Page requests per website (home page included), integer 1-6, default 4. The imprint is nearly always the second or third page: first the links on the home page, then /impressum, /imprint, /legal-notice and the sitemap. Raise to 6 for sites with unusual imprint paths, e.g. 6. Each site is read at about one page per second.

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

Maximum companies to return, integer 1-5000, default 100. Also your cost cap: at most maxItems x $0.006, charged only for companies where a legal entity was found. For a quick test use 5 (e.g. maxItems 5). Sites with no imprint, blocked, unreachable or disallowed by robots.txt are delivered free and count toward the cap.

## `onlyChangedSinceLastRun` (type: `boolean`):

true returns a company only if its legal data (name, directors, register, VAT id, address, contacts) changed since the previous scheduled run. Default false: a normal one-off run returns every company. Agents making one-off calls should leave it false. Example: run monthly on a Schedule with true; the first run delivers everything, later runs only changes (`changedFields`); unchanged companies are not charged.

## `stateStoreName` (type: `string`):

Only used when change mode is on. Name of the key-value store that remembers each company's last legal data between scheduled runs, e.g. "company-imprint-scraper-state". Letters, numbers and hyphens only. Leave the default unless several tasks need separate histories.

## Actor input object example

```json
{
  "websites": [
    "heise.de",
    "spiegel.de",
    "zeit.de"
  ],
  "outputFormat": "full",
  "maxPagesPerSite": 4,
  "maxItems": 100,
  "onlyChangedSinceLastRun": false,
  "stateStoreName": "company-imprint-scraper-state"
}
```

# Actor output Schema

## `companies` (type: `string`):

No description

## `registerData` (type: `string`):

No description

## `inputReport` (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 = {
    "websites": [
        "heise.de",
        "spiegel.de",
        "zeit.de"
    ],
    "maxPagesPerSite": 4,
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("lukehunter/impressum-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 = {
    "websites": [
        "heise.de",
        "spiegel.de",
        "zeit.de",
    ],
    "maxPagesPerSite": 4,
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("lukehunter/impressum-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 '{
  "websites": [
    "heise.de",
    "spiegel.de",
    "zeit.de"
  ],
  "maxPagesPerSite": 4,
  "maxItems": 100
}' |
apify call lukehunter/impressum-scraper --silent --output-dataset

```

## MCP server setup

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