# DACH Company Data Extractor – Impressum + VIES (`etse/dach-company-data-extractor`) Actor

Turn any German, Austrian or Swiss website into a structured company record: legal name, legal form, address, register court/number, VAT ID (validated via EU VIES), representatives, contact data. One record per domain.

- **URL**: https://apify.com/etse/dach-company-data-extractor.md
- **Developed by:** [Este Lab](https://apify.com/etse) (community)
- **Categories:** Lead generation, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$6.00 / 1,000 company records

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 Company Data Extractor – Impressum → structured company record (+ VIES VAT check)

**Run it on Apify (no setup, free tier included): https://apify.com/etse/dach-company-data-extractor** · Source: MIT-licensed, this repository.

Give it a list of website URLs from Germany, Austria or Switzerland and get back **one clean, structured company record per domain**: legal name, legal form, street address, commercial-register court and number, VAT ID (validated live against the EU VIES service), managing directors / representatives, e-mail, phone and the URL the data was taken from.

Every DACH company website must publish these facts in its *Impressum* (§ 5 DDG in Germany, § 5 ECG in Austria, UWG/Impressumspflicht in Switzerland). This Actor finds that page, reads it and turns the free text into fields you can load straight into a CRM, a KYC workflow or an LLM pipeline.

### What you get

| Field | Example |
|---|---|
| `companyName`, `legalForm` | `Heise Medien GmbH & Co. KG`, `GmbH & Co. KG` |
| `street`, `postalCode`, `city`, `country` | `Karl-Wiechert-Allee 10`, `30625`, `Hannover`, `DE` |
| `registerCourt`, `registerType`, `registerNumber` | `Amtsgericht Hannover`, `HRB`, `60405` |
| `vatId` + `vies` | `DE813501887` → `{ valid: true, name: "...", address: "..." }` |
| `taxNumber` | `231/123/45678` (sole traders) |
| `representatives[]`, `responsiblePerson` | `["Ansgar Heise", "Beate Gerold"]` |
| `email`, `phone`, `fax` | |
| `supervisoryAuthority`, `chamber`, `profession` | regulated professions |
| `impressumUrl`, `fetchedVia`, `confidence`, `fieldsFound[]`, `errors[]` | provenance and quality signals |

`confidence` (0–1) tells you how many of the four core blocks were found: name, full address, an official identifier (register number / VAT ID / tax number) and a contact channel. Filter on `confidence >= 0.75` for CRM-grade data.

### Why this Actor

- **Legal-notice aware.** Finds the Impressum even when it is called *Imprint*, *Legal notice*, *Anbieterkennzeichnung* or hidden behind an index page with several sub-Impressums (e.g. one per product line).
- **Handles JavaScript shops.** If the legal page is rendered client-side (common for large e-commerce sites) it is automatically re-fetched with a headless browser.
- **DACH formats built in.** German `HRB/HRA`, Austrian `FN 123456a` + `ATU` UIDs, Swiss `CHE-123.456.789 MWST` UIDs, obfuscated e-mails (`info (at) firma (dot) de`), public bodies (KdöR), sole traders without a legal form.
- **VIES validation included.** DE/AT VAT IDs are checked against the official EU service and enriched with the registered name and address where the member state returns it. (Swiss UIDs are not in VIES.)
- **Agent-ready.** Works through the Apify MCP server, so an AI agent can call `dach-company-data-extractor` as a tool to look up a company before writing an offer, a contract or a due-diligence memo.

### Typical uses (Impressum scraper / imprint extractor / German company data API)

- Enrich a lead list of domains with legal entity, register number and VAT ID before importing into HubSpot, Pipedrive or Salesforce.
- Supplier / customer onboarding (KYB): confirm that the entity on the website matches the entity on the invoice.
- Verify Impressum completeness across your own portfolio of sites or client sites (agencies).
- Build a DACH company dataset from a list of websites.
- Replace manual Impressum lookups: works as an Impressum scraper, imprint extractor, Handelsregister-number finder and VAT-ID (VIES) checker in one run, for Germany (DE), Austria (AT) and Switzerland (CH).
- Call it from an AI agent via the Apify MCP server when a workflow needs the legal entity behind a website (offers, contracts, due diligence).

### Input

```json
{
  "startUrls": [{ "url": "https://www.sevdesk.de" }, { "url": "heise.de" }],
  "validateVatId": true,
  "maxCandidatesPerSite": 3,
  "tryCommonPaths": true,
  "useBrowserFallback": true
}
```

Any page of the site works as a start URL; the Actor always returns one record per domain.

### Output example

```json
{
  "inputUrl": "https://www.sevdesk.de/",
  "domain": "sevdesk.de",
  "impressumUrl": "https://sevdesk.de/impressum/",
  "fetchedVia": "http",
  "companyName": "sevDesk GmbH",
  "legalForm": "GmbH",
  "street": "Im Unteren Angel 1",
  "postalCode": "77652",
  "city": "Offenburg",
  "country": "DE",
  "registerCourt": "Amtsgericht Freiburg",
  "registerType": "HRB",
  "registerNumber": "710506",
  "vatId": "DE291779897",
  "vies": { "checked": true, "valid": true, "name": "---", "address": "---", "requestDate": "2026-10-27" },
  "representatives": ["Rico Andersen"],
  "email": "support@sevdesk.com",
  "phone": "+49-781-125508-10",
  "confidence": 1,
  "fieldsFound": ["companyName", "legalForm", "street", "postalCode", "city", "country", "registerCourt", "registerType", "registerNumber", "vatId", "representatives", "email", "phone"],
  "errors": []
}
```

### Pricing

Pay per event: you are charged **only for records with `confidence >= 0.25`**, i.e. domains where company data was actually found. Sites that are unreachable, blocked or have no legal notice cost nothing. Platform usage (compute) is billed separately by Apify as with every Actor; a typical plain-HTTP lookup uses a few seconds of compute, a browser fallback lookup a bit more.

### Accuracy and limits

- Benchmark on 20 well-known DACH sites (October 2026): 18/20 records with `confidence >= 0.75`, all 13 VAT IDs found confirmed valid by VIES. Failures were sites with aggressive bot protection.
- Impressum pages are free text; the extraction is heuristic. Always keep `impressumUrl` and `confidence` next to the data and spot-check records with `confidence < 0.75`.
- Sites that block automated access (some large retailers) may return `errors`. Use Apify Proxy (residential) via `proxyConfiguration` if you need those.
- VIES is an external EU service with occasional outages; `vies.checked = false` with a `reason` is returned in that case and the record is still delivered.

### Data protection

The Actor only reads information that the website operator is legally obliged to publish. Names of managing directors are such mandatory disclosures. Under the Apify Store terms you (the user) are the controller of the data you collect and are responsible for having a lawful basis for the intended use (e.g. B2B contact, KYB). Do not use the output for unsolicited marketing where local law requires consent.

### Run locally / contribute

```bash
npm install
npm test                      # unit tests
node test/smoke-quick.js      # 5 live sites over plain HTTP
CHROME_PATH=/usr/bin/chromium node test/smoke-live.js   # 20-site benchmark incl. browser fallback
```

Issues and pull requests are welcome. The Actor is rebuilt automatically from `main`.

### Changelog

- 0.1 – initial release: DACH extraction, index-page following, browser fallback, VIES validation, pay-per-event pricing.

# Actor input Schema

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

Homepages (or any page) of the companies you want to look up. One record per domain is returned.

## `validateVatId` (type: `boolean`):

Checks every found DE/AT VAT ID against the official EU VIES service and adds the registered name/address when the member state returns it. Swiss UIDs are not in VIES.

## `maxCandidatesPerSite` (type: `integer`):

How many candidate pages (Impressum, Imprint, Legal notice, Kontakt) to fetch per site. Higher = more robust, slightly more usage.

## `tryCommonPaths` (type: `boolean`):

If the homepage has no recognizable Impressum link, probe /impressum, /imprint, /legal-notice, /kontakt.

## `useBrowserFallback` (type: `boolean`):

If a legal page comes back empty over plain HTTP (single-page apps, consent walls), re-fetch it with a headless browser. Costs more compute per page but rescues most large e-commerce sites.

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

Parallel requests.

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

Apify Proxy (datacenter) is used by default so that sites blocking cloud IPs still answer. Switch to residential proxies (optionally with a country) for sites with strong bot protection or strict geo-routing.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.sevdesk.de"
    },
    {
      "url": "https://www.heise.de"
    }
  ],
  "validateVatId": true,
  "maxCandidatesPerSite": 3,
  "tryCommonPaths": true,
  "useBrowserFallback": true,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `companyRecords` (type: `string`):

Dataset with one item per domain: company name, legal form, address, register data, VAT ID with VIES result, representatives, contact data, confidence and provenance.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.sevdesk.de"
        },
        {
            "url": "https://www.heise.de"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("etse/dach-company-data-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 = {
    "startUrls": [
        { "url": "https://www.sevdesk.de" },
        { "url": "https://www.heise.de" },
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("etse/dach-company-data-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 '{
  "startUrls": [
    {
      "url": "https://www.sevdesk.de"
    },
    {
      "url": "https://www.heise.de"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call etse/dach-company-data-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,etse/dach-company-data-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/ckIl83odZXI5iAt4t/builds/z9kLXbTCAizFbuAp5/openapi.json
