Impressum Scraper: German Company Contacts & VAT avatar

Impressum Scraper: German Company Contacts & VAT

Pricing

from $3.12 / 1,000 imprint record returneds

Go to Apify Store
Impressum Scraper: German Company Contacts & VAT

Impressum Scraper: German Company Contacts & VAT

Scrape the Impressum (legal notice) of German company websites: registered name, address, register court and number, VAT ID, managing directors, email, phone and social profiles. Paste domains or search with your own key. Sites with nothing to read are free.

Pricing

from $3.12 / 1,000 imprint record returneds

Rating

0.0

(0)

Developer

Pradio Actors

Pradio Actors

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 hours ago

Last modified

Share

What does Impressum Scraper do?

Impressum Scraper reads the Impressum of every German company website you give it and returns one row per site. Each row carries the registered company name, address, register court and number, VAT ID, the legal representatives with their roles (managing directors, the board, the owner, or a liable company), email, phone, fax and the company's social profiles. Paste a list of domains or type search terms with your own search-API key, press Start, and the rows land in your dataset as they are read. A site that yields nothing costs nothing, and its row says why.

On 104 German company sites it had never seen, run where a buyer runs it, 61 came back filled (59%), at $0.004 a row plus the platform's start charge of two $0.00005 events a run.

Who uses Impressum Scraper

BuyerWhat they run it for
Contact-data buildersTurn a list of domains into legal entity names, emails and phone numbers pulled from each company's own legal page.
Compliance and due-diligence teamsCollect register court, register number and legal representatives, the disclosures an imprint is required to carry, without opening each site by hand.
B2B sales and outreach teamsFind the real legal entity and a working contact behind a website before writing to it.
Data teams enriching company listsAdd registered address and registration identifiers to rows that only hold a domain.

Features

  • Automatic imprint discovery. The site's own impressum, imprint or legal-notice link is followed up to two hops: homepage to legal hub to notice. The contact page is tried when the homepage carries no link. A related host is followed across a locale redirect. The conventional paths (/impressum, /imprint…) are probed when the footer only exists after hydration.
  • Dual fetching. A plain request first, then a headless browser pass when the page needs one (useBrowser, on by default).
  • A refusal is the answer. Requests go through the proxy you configure, datacenter by default. A site that blocks them comes back as a free fetch_failed row and is never retried through another proxy. Configure RESIDENTIAL groups yourself to proxy everything.
  • Search mode, bring-your-own-key. Type terms instead of pasting URLs. They resolve to domains through the Brave or Google Custom Search API with your key, then read like any start URL.
  • Decision makers with their roles. decision_makers lists each person on the imprint's representative lines as {name, role}: Geschäftsführer, Vorstand, Inhaber, Managing Director, with the role the page prints beside each name. legal_representatives keeps the same lines word for word.
  • One column per social network. facebook, linkedin, xing, instagram, twitter and youtube each hold the company's profile link, ready to filter on. Share buttons are left out. The full list stays in social_profiles.
  • Fax as well as phone. fax reads the imprint's Fax or Telefax line.
  • Optional e-mail check. With verifyEmails on, each e-mail is checked for syntax and for mail servers (MX records) on its domain. A DNS lookup only: no mail server is contacted.
  • Only sites with an e-mail, if you want. With skipWithoutEmail on, a site whose imprint names no e-mail comes back as a free no_email row instead of a billed company row.
  • Legal-notice vocabulary, German- and English-first. Impressum, imprint, legal notice/information, Anbieterkennzeichnung, mentions légales, colophon, disclaimer.
  • Exclusion that is honoured, not promised. The exclude input and the Actor's own exclusion list are read at the start of every run. A domain skips its start URLs before any fetch. A company or person name drops the row that carries it. See Personal data.

What you can count on

  • You pay only for rows whose status is ok. A site that refused the request, hid its imprint behind JavaScript, or publishes none is pushed as an uncharged ITEM_STATUS row. The dataset still tells you what happened.
  • Every billed row is charged only after it is written to your dataset. A row you cannot see is never billed.
  • A run that finds nothing returns one PROFILE_NOT_FOUND row that says so, never an empty dataset.
  • A spending limit stops the run cleanly. A STOPPED_EARLY row says how many rows were returned and how many were not.
  • Every run writes RUN_SUMMARY to the key-value store: rowsFetched, rowsPushed, rowsCharged, duplicatesDropped, excluded. A short run and a broken one are told apart.
  • No value is invented. A field the page does not show is null, and the field table below says which.

What data does Impressum Scraper return?

One row per start URL. This is a real row from a captured run, slightly shortened:

{
"domain": "trigema.de",
"website": "https://trigema.de",
"pageUrl": "https://www.trigema.de/kundenservice/rechtliches/impressum/",
"title": "Impressum | trigema",
"email": "bestellservice@trigema.de",
"phone": "+497475880",
"websites": [
"https://trigema.de"
],
"imprint_url": "https://www.trigema.de/kundenservice/rechtliches/impressum/",
"company_name": "Trigema W. Grupp KG",
"street": "Josef-Mayer-Str. 31-35",
"postal_code": "72393",
"city": "Burladingen",
"country": "DE",
"legal_representatives": "Wolfgang Grupp, Trigema Verwaltung GmbH",
"decision_makers": [
{ "name": "Wolfgang Grupp", "role": "Persönlich haftende Gesellschafter" },
{ "name": "Trigema Verwaltung GmbH", "role": "Persönlich haftende Gesellschafter" }
],
"register_court": "Stuttgart",
"register_number": "HRA 740365",
"vat_id": "DE144846566",
"fax": "+49747588228",
"facebook": "https://facebook.com/trigema",
"linkedin": "https://www.linkedin.com/company/trigema-w-grupp-kg",
"xing": null,
"instagram": "https://www.instagram.com/trigema_de/",
"twitter": "https://twitter.com/TRIGEMA",
"youtube": "https://www.youtube.com/@TRIGEMAtv",
"email_mx_ok": true,
"social_profiles": [
"https://facebook.com/trigema",
"https://www.instagram.com/trigema_de/",
"https://www.tiktok.com/@trigema_de",
"https://www.linkedin.com/company/trigema-w-grupp-kg",
"https://www.youtube.com/@TRIGEMAtv",
"https://instagram.com/trigemafashion",
"http://www.facebook.com/TRIGEMA",
"https://www.youtube.com/user/TRIGEMAtv",
"https://twitter.com/TRIGEMA"
],
"status": "ok",
"legal_form": "KG",
"row_type": "ROW"
}

Every field, and where it is read from:

FieldWhat it is
pageUrlThe page actually read for this site: the imprint/legal-notice URL when one was found, else the start page that was fetched.
titleThe <title> text of the page that was read.
emailThe first mailbox on the page, own-domain addresses first. An address whose name starts with a department word is skipped: privacy, press, media, jobs, careers, recruiting, compliance, whistleblowing, abuse, webmaster, no-reply, investor or accessibility, and their German forms. Regulator addresses are skipped too. Customer-service and order mailboxes such as kundenservice@ and bestellservice@ are contacts and are returned. A page that lists only skipped addresses yields null.
phoneThe page's tel: link, else a Tel:/Phone: line in the text, as digits: +497475880 when the page gives the international form, the national form (07475880) when that is all it states. No country code is added.
websiteThe site's root URL (https://<domain>), derived from the start URL's hostname. Never null.
websitesThe site root plus every non-social URL the page's JSON-LD Organization lists under sameAs. Social links never land here; they go to social_profiles.
domainThe site's hostname without www., taken from the start URL.
imprint_urlThe URL of the imprint/legal-notice page the site links to, followed from the fetched page and read for the company fields. Null when none was found.
company_nameThe registered company name: the first company-shaped line in the imprint body (GmbH, AG, SE, Ltd. …), else the page's JSON-LD Organization name. Never invented from the domain; null when nothing qualifies.
streetThe street-and-number line of the postal address stated in the imprint body.
postal_codeThe postal code in the address block of the imprint body. Only counted near the street or register lines, so a price like "1398" elsewhere on the page is not mistaken for one.
cityThe city named beside the postal code in the imprint body's address.
countryThe country the imprint body names, as a two-letter code (DE, AT, CH …), or taken from the VAT ID prefix. Never guessed from the domain, so it is null on pages that do not name one.
legal_representativesEvery representative named on the imprint's Geschäftsführer: / Vertreten durch: / Vorstand / Managing Director: line, people or companies (a limited partnership's liable company, such as Trigema Verwaltung GmbH, appears here), in one comma-separated string as the page prints them. Multi-line lists are kept whole, so one name is never mistaken for the list.
decision_makersThe representatives on the imprint's representative lines, people or companies, one {name, role} each. role is what the page prints beside the name (Vorsitzender, CFO) or above the list (Geschäftsführer, Vorstand, Inhaber); null when the page names a representative with no role. For a limited partnership the liable company can appear here with its printed role. Null when the page names nobody.
register_courtThe commercial-register court the imprint names (Registergericht / Amtsgericht / "registered at").
register_numberThe commercial-register number the imprint carries (HRB / HRA …).
vat_idThe VAT identification number the imprint states (USt-IdNr / VAT ID), whitespace stripped. For example DE144846566.
faxThe fax number on the imprint's Fax: or Telefax: line, as digits, in the same form the page states it, like phone.
social_profilesEvery social-media profile linked anywhere on the page: anchors to known social hosts plus the JSON-LD Organization's sameAs links. Share buttons are left out. Null when none are linked.
facebookThe first Facebook page linked on the page.
linkedinThe first LinkedIn company or profile page linked on the page.
xingThe first XING page linked on the page.
instagramThe first Instagram profile linked on the page.
twitterThe first X (Twitter) profile linked on the page, from x.com or twitter.com.
youtubeThe first YouTube channel linked on the page; a single video link only when no channel is linked.
email_mx_okOnly with verifyEmails on: true when email is well formed and its domain has mail servers (MX records), false when it is malformed or the domain has none, null when the lookup did not answer or the option is off.
statusThis source's per-URL verdict: ok, no_imprint_found, fetch_failed, bad_url, timeout, js_required, search_failed, aborted, or no_email when skipWithoutEmail is on, meaning why a row has no company data. Any verdict but ok is free (see row_type).
legal_formThe entity's registered legal form parsed from the company name: AG, GmbH, SE, KGaA, SE & Co. KGaA, e.V.… Null when the name declares none.
row_typeROW on a filled row (billed); ITEM_STATUS on a per-site miss (pushed so you see the verdict, never billed); PROFILE_NOT_FOUND and STOPPED_EARLY on the run-level status rows described under Output.

Fields that appear only on a status row:

FieldWhat it is
reasonWhy the run returned no rows, why it stopped early, or, on a per-site miss, the detail behind status (for example fetch_failed carries HTTP 403).
rowsFetchedHow many rows the source handed over before de-duplication and the cap.
rowsReturnedHow many data rows are in the dataset (0 on an empty result; the count before the charge limit on STOPPED_EARLY).
rowsRemainingHow many fetched rows were not returned (0 on an empty result).

In the Console, the dataset's Overview view shows the columns a buyer reads first. The "All fields" view and every export carry the full contract. A null cell means that site did not publish the value, not that the field is missing.

How much does it cost?

This Actor bills per profile-returned event: $0.004. One profile-returned event is one site row written to your dataset whose status is ok.

A row reporting no_imprint_found, fetch_failed, timeout, js_required, bad_url, search_failed or no_email is pushed as ITEM_STATUS: visible in the dataset, never billed. You do not pay for this Actor's inability to read a site.

What that looks like in sites, at the measured 59% fill rate:

  • 100 sites: about 59 filled rows, about $0.24
  • 1,000 sites: about 590 filled rows, about $2.36
Filled rows (status: ok)profile-returned charge
100$0.40
1,000$4.00
10,000$40.00

Why this one

Measured against the most-used alternative on this shelf, run on 2026-09-12:

  • A row costs $0.004. The most-used alternative on this shelf charges $0.005 a row plus $0.04 to start.
  • A run costs $0.0001 to start, two $0.00005 events at the 2048 MB default. The sellers on this shelf that bill a start fee charge $0.04 to $0.08.
  • A miss is free and explained. The row says why the site yielded nothing, and it is never billed.
  • The fill rate is measured, not promised: 61 of 104 sites this Actor had never seen, on the platform build a buyer's run uses. No seller on this shelf publishes one.
  • Every row carries imprint_url and pageUrl. The page each value was read from is on the row.

Search mode is bring-your-own-key: searchQueries with your own searchApiKey (and searchCx for Google) use your own API quota, and this Actor bills nothing per query. A query with no key returns a free search_failed row. The sites a search finds are billed like any other: per filled row.

Apify also bills its own apify-actor-start event once per gigabyte of memory allocated: two events, $0.0001, at this Actor's 2048 MB default. A single-domain lookup costs under half a cent in total.

What is not charged:

  • ITEM_STATUS miss rows, PROFILE_NOT_FOUND and STOPPED_EARLY status rows.
  • Start URLs skipped and rows dropped by the exclusion list (excluded in RUN_SUMMARY).
  • Duplicate rows dropped on domain before anything is pushed.
  • The RUN_SUMMARY entry in the key-value store.

How do I use Impressum Scraper?

  1. Open the Actor's Store page and press Try for free / Start.
  2. In Start Urls, list the sites to read: one URL per line, a site's root or its imprint page itself. Or skip the list entirely. Put terms in Search queries and the Actor resolves them to domains through an official search API.
  3. Optionally lower or raise Maximum items (default 100). Adjust the proxy (datacenter by default; a site that blocks it comes back as a free miss row). Switch the headless-browser fallback off for cheap list runs. Fill Exclude with domains and names this run must not return.
  4. Press Start. Rows land in the dataset as they are written. The key-value store's RUN_SUMMARY entry carries the run's counts when it finishes.

Example input:

{
"startUrls": [
{ "url": "https://www.trigema.de" },
{ "url": "https://www.mymuesli.com" },
{ "url": "https://www.bosch.de" }
],
"maxItems": 100
}

Or search mode, with no list required:

{
"searchQueries": ["site:.de GmbH Impressum Maschinenbau"],
"searchEngine": "brave",
"searchApiKey": "<your Brave Search API key, required, you bring your own>"
}

Or run it through the Apify API:

curl "https://api.apify.com/v2/acts/Pradio~imprint-contact/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-X POST -H "Content-Type: application/json" \
-d '{"startUrls":[{"url":"https://www.trigema.de"}]}'

Input

InputTypeDefaultWhat it does
startUrlsarrayprefilled with https://www.trigema.deThe sites to read, one output row per entry.
searchQueriesarraynoneSearch terms resolved to domains through an official search API, then read like startUrls entries.
searchEnginestringbravebrave (Brave Search API) or google (Google Custom Search JSON API; needs searchCx).
searchApiKeystringnoneYour own key for the chosen engine, required for searchQueries. No key is configured for you, so a query with no key returns a free search_failed row.
searchCxstringnoneGoogle programmable engine ID, required only for searchEngine: "google".
proxyConfigurationobjectdatacenter Apify proxyHow site requests are proxied, used as you configure it. A site that answers with a block page comes back as a free fetch_failed row. Configure RESIDENTIAL groups to proxy every request that way.
useBrowserbooleantrueRender pages that answer plain requests with a JavaScript shell. Costs more memory and seconds per hit.
maxItemsinteger100The most rows one run returns. Fetching and pushing stop there and the log notes the cap. Minimum 1.
verifyEmailsbooleanfalseCheck each e-mail for syntax and for MX records on its domain; the answer lands in email_mx_ok. DNS only, no mail server contacted.
skipWithoutEmailbooleanfalseReturn a free no_email row instead of the company row when the imprint names no e-mail.
maxDomainsPerSearchQueryinteger20The most domains one searchQueries entry may resolve to, so a broad query cannot starve the ones after it.
excludearraynoneDomains and names this run must not return, one entry per line. A domain skips its start URLs before any fetch; a company or person name drops the row that carries it. Counted under excluded in RUN_SUMMARY.

startUrls

Each entry is { "url": "..." } or a bare string; https:// is added when it is missing. Give it a site's root. The Actor fetches that page. If it is not itself an imprint, the Actor finds the site's own imprint link (same domain, by URL or by link label) and reads that page instead. It follows up to two hops, and tries the contact page when the homepage carries no link. You can also hand it the imprint URL directly. Every domain it reads comes back as a row, whatever the outcome. Two start URLs on one domain share that row. A start URL on a domain your exclusion list names is skipped before any fetch. Start URLs past maxItems are not read.

exclude

One entry per line. An entry that is a domain, like dm.de or a full URL, skips every start URL on that host or a subdomain of it, before any request is made. Any other entry is read as a name. A fetched row whose company_name or legal_representatives carries it is dropped: not pushed, not billed. The Actor's own exclusion list is applied the same way on every run. See Personal data for how a person or company joins it.

verifyEmails

Off by default. When on, every email a row carries is checked twice: is it a well-formed address, and does its domain publish mail servers (MX records) in DNS? email_mx_ok says true, false, or null when the DNS lookup did not answer. Nothing is sent and no mail server is contacted, so a true means the domain accepts mail, not that the mailbox exists. Each domain is looked up once per run.

{
"startUrls": [{ "url": "https://www.trigema.de" }, { "url": "https://www.mymuesli.com" }],
"verifyEmails": true
}

skipWithoutEmail

Off by default. When on, a site whose imprint names no company e-mail comes back as a no_email row: the site, its imprint page and the reason, without the company row, and never billed. Use it when an address to write to is the only thing you are paying for.

{
"startUrls": [{ "url": "https://www.trigema.de" }, { "url": "https://www.stihl.de" }],
"skipWithoutEmail": true
}

How to scrape German Impressum pages

Use it to build an outreach list you only pay for when there is an address to write to. Paste the domains, turn on both e-mail options, and keep the rows where email_mx_ok is true.

{
"startUrls": [
{ "url": "https://www.trigema.de" },
{ "url": "https://www.stihl.de" },
{ "url": "https://www.mymuesli.com" }
],
"verifyEmails": true,
"skipWithoutEmail": true
}

Use it to check who runs a supplier before signing. Give the supplier's site; the row carries company_name, register_court, register_number, vat_id and decision_makers with each person's role.

{
"startUrls": [{ "url": "https://www.stihl.de" }],
"maxItems": 1
}

Use it to enrich a CRM export with social profiles. A list of company sites comes back with linkedin, xing, facebook, instagram, twitter and youtube as separate columns you can join on domain. The browser fallback is off here to keep a large list cheap.

{
"startUrls": [
{ "url": "https://www.trigema.de" },
{ "url": "https://www.mymuesli.com" }
],
"useBrowser": false,
"maxItems": 500
}

Use it to read a list while leaving some companies out. Domains and names in exclude are skipped before any request, and never billed.

{
"startUrls": [{ "url": "https://www.trigema.de" }, { "url": "https://www.dm.de" }],
"exclude": ["dm.de"]
}

Output

A run writes two kinds of things:

  • Data rows in the default dataset, one per domain read. Each carries every contract field and row_type: "ROW". The per-row status field tells a filled row (ok) from a site that yielded nothing (no_imprint_found, fetch_failed, timeout, js_required, bad_url, search_failed, aborted, and no_email when skipWithoutEmail is on). A miss row carries row_type: "ITEM_STATUS" and is never billed. Rows are de-duplicated by domain: two start URLs on the same domain return one row.
  • One status row, only when there is something to report. A run that pushes no data rows ends with one PROFILE_NOT_FOUND row carrying reason, rowsFetched, rowsReturned and rowsRemaining, never an empty dataset that could mean "nothing found" or "it broke". A run cut short by the charge limit ends with a STOPPED_EARLY row saying how many rows were returned and how many were not. Neither is charged.

The key-value store's RUN_SUMMARY entry carries rowsFetched, rowsPushed, rowsCharged, duplicatesDropped, excluded and stoppedEarly for every run, alongside the input and start/finish timestamps. That is how a short run and a broken one are told apart.

A zero result is an answer, not silence: one PROFILE_NOT_FOUND row, uncharged, with rowsFetched telling you whether the source handed over anything at all.

What can you do with the data?

  • Build a B2B contact list you can defend. Every email and phone number comes off the company's own legally required disclosure page, not a third-party guess. The row keeps imprint_url, so you can show where each value was read.
  • Verify who is behind a site before you pay it. company_name, register_court and register_number are the identifiers a court or a contracts team asks for. A supplier list of domains becomes a list of registered entities in one run.
  • Enrich a domain list into company records. Feed a CRM export of websites in, get legal names, registered addresses and legal representatives out, and join back on domain.
  • Find the real contact for outreach. status separates sites that answered from sites that did not. The follow-up list is only the rows where there is something to follow up.

Use Impressum Scraper with AI agents

$claude mcp add --transport http apify "https://mcp.apify.com?tools=Pradio/imprint-contact"

Paste that line to give an MCP-capable agent this Actor as a tool. It can then hand the agent a list of sites and get imprint rows back.

Personal data

A row can name a person: a managing director printed on a company's own legal-notice page. This section says plainly what the product is for, where the data comes from, and how removal works.

  • Purpose. For identifying and verifying a company and reaching it through the business contact details it publishes. Not for building people databases, profiling, or marketing to individuals. You supply the domains. You are the controller of the rows your runs collect, you owe your own privacy notice, and each named site's own terms still apply to you.
  • Transparency and source. Every row comes from the company's own legal-notice page, published under its duty to identify itself and its representatives. A row may carry a representative's name and role exactly as printed there (in legal_representatives as the page words it, and in decision_makers one name and role at a time), plus the company's published contact channels. The names appear twice on purpose. legal_representatives is the line as the notice prints it, ready for a letter. decision_makers splits the same names with their roles, ready for a filter. No other field repeats them. Every row carries imprint_url and pageUrl, so the source of every value is visible. The publisher keeps no rows: runs and datasets live in your Apify account and belong to you.
  • Sole traders. When the notice belongs to a one-person business or a private site, the whole row is one person's data: their name, address, phone and email. The same limits apply to all of it.
  • Exclusion. A person or company that wants its imprint left out can ask: open an issue on this Actor's Issues tab in the Apify Console. A request is honoured through the Actor's exclusion list, which every run reads at start. The domain is skipped before any fetch, and a fetched row naming the company or person is dropped. The exclude input applies the same mechanism to your own list. Every skip and drop is counted in RUN_SUMMARY under excluded.

Release notes

  • 0.2.59 (2026-09-25): the column managing_director is renamed legal_representatives, because it holds every representative the notice names, people or companies. Update any filter or mapping that reads the old name.
  • 0.2.52 (2026-09-24, later): decision_makers as {name, role} beside managing_director; one column per social network (facebook, linkedin, xing, instagram, twitter, youtube) beside social_profiles, with share buttons left out; fax; the optional verifyEmails check (email_mx_ok) and the skipWithoutEmail option.
  • 2026-09-24: a site that refuses the request is no longer retried through a residential proxy; the refusal comes back as a free miss row. Search with your own key is no longer listed with a per-query price, because none applies.
  • 0.2.34: harder reads on app-shell sites, a render that returns only the shell waits for hydration once, then escalates to residential like a refused request did (both retries retired on 2026-09-24).
  • 0.2.20: the exclude input and the Actor's own exclusion list, read at the start of every run. Domains skip their start URLs; names drop the row that carries them. Counted under excluded in RUN_SUMMARY.
  • 0.2.17: country now fills from the VAT-ID prefix when the page names no country. Register filings no longer appear inside director names.
  • 0.2.15: a per-query domain cap in search mode, so one broad query cannot starve the ones after it.
  • 0.2.14: the drawn listing icon.
  • 0.2.12: representatives are listed exactly as the page prints them, inside managing_director (now legal_representatives).
  • 0.2.6: client-redirect shims, a tighter register-court boundary, name and address parsing polish.
  • 0.2.4: parser fixes measured on a second, larger run over German company sites; datacenter-first proxy with residential only when a site earns it; bring-your-own-key search made honest. The residential fallback was removed on 2026-09-24.
  • 0.2: unbilled misses, search mode, proxy escalation and the browser fallback.

Limits

  • Coverage is measured on the platform, not total: 59%. On 104 German company sites (DAX, MDAX and SDAX members, every one new to this build), run as the deployed build through the same proxied egress a buyer's run uses: 61 came back filled; 22 refused the fetch outright (bot walls, dead domains); 13 answered but exposed no notice; 8 timed out. Each such row says its status and is unbilled. Of the rows that resolved: company name 98%, register number 75%, register court 64%, VAT ID 48%, email 61%, legal representatives 69%.
  • The vocabulary is German- and English-first. A site that names its notice something outside the list under Features is reported no_imprint_found, not misread.
  • A field the page does not publish is null; nothing is invented. company_name is null rather than guessed from the domain. country needs a named country or a VAT-ID prefix. email is the page's first mailbox that is not a department or regulator address, own domain first, or null.
  • One notice page per domain, and a refusal ends it. The Actor reads only the pages that lead to a site's notice. Those are the start page and the site's own imprint or legal link, at most two hops through a legal hub. The contact page is read when the homepage links to no notice. The conventional notice paths such as /impressum are read when the footer only appears after scripts run. It also reads robots.txt, and follows no other link on the host. A page that refuses the request is answered with that refusal, as a free fetch_failed row, and nothing else is tried.
  • Only an empty input stops the run. A run with no entry in startUrls or searchQueries fails at once, with an error naming both fields. No row is pushed. Anything that goes wrong with one entry becomes a free row, and the run carries on. An entry that cannot be read as a URL is a bad_url row. A search term with no key, or a failed search, is a search_failed row. A site that could not be read gets fetch_failed, timeout, js_required or no_imprint_found.

Troubleshooting

I passed 50 URLs and got 47 rows. Where did three go? Rows are de-duplicated by domain, the site's domain. Two start URLs on the same domain, say dm.de and www.dm.de/shop, return one row between them. RUN_SUMMARY.duplicatesDropped says how many went that way. maxItems caps the total. RUN_SUMMARY.excluded counts any that your exclude list skipped.

A row has row_type: "ITEM_STATUS" and almost every field is null. Look at status and reason. no_imprint_found means the site answered but no imprint page was found on it. fetch_failed means the page could not be retrieved (the HTTP status is in reason). js_required means the page only renders in a browser, and reason says why it still could not be read: the browser was switched off, the site's robots.txt disallows the page, or the render produced no readable text (some sites block automated browsers). timeout means the host never answered. bad_url means the entry could not be parsed. These rows are returned free. Filter on status: ok or row_type: ROW when you only want billed rows.

The dataset holds a single row saying PROFILE_NOT_FOUND. The run pushed no data rows, for example because it was aborted before the first site answered. reason and rowsFetched on that row say what the source handed over. The row is not charged. It is the difference between "nothing was there" and an empty dataset you would have to second-guess.

The last row says STOPPED_EARLY. Your charge limit was reached mid-run. rowsReturned and rowsRemaining say how many fetched rows made it out and how many did not. Raise the limit and re-run to see the rest. Only the returned rows were billed.

FAQ

Can I use integrations with Impressum Scraper? Yes. Like every Apify Actor it plugs into Apify's integrations: Zapier, Make, Google Sheets, webhooks. It can also run on a schedule. A scheduled run over the same site list re-reads each imprint page and hands you the fresh rows.

Can I use Impressum Scraper with the Apify API? Yes. The curl line under "How do I use" is the whole call: POST the input to acts/Pradio~imprint-contact/run-sync-get-dataset-items with your token and the dataset rows come back in the response. The apify-client libraries for JavaScript and Python wrap the same call.

Can I use Impressum Scraper through an MCP server? Yes. The line in "Use with AI agents" registers it with an MCP-capable agent through Apify's hosted MCP server, no local install needed.

Is it legal to scrape imprint pages? Imprint and Impressum pages are disclosures companies are legally required to publish. Each page is fetched only on a path its site's robots.txt allows, and there is a 400 ms gap between requests to the same host. That said, the contact data you collect is yours to handle lawfully. GDPR and local rules still apply to how you store and use it. The Personal data section above says what this product is for and how a company or person can ask to be left out.

Report a problem

Something off, a site that parses wrong, a field that should be there? Open an issue on this Actor's Issues tab in the Apify Console. That is the fastest way to get it fixed, and the report is never a bother.

Not affiliated

This Actor is not affiliated with, endorsed by, or sponsored by any of the sites it reads. Imprint and legal-notice pages belong to their operators. The tool reads what each site publishes and nothing more.