Impressum Scraper: German Company Contacts & VAT
Pricing
from $3.12 / 1,000 imprint record returneds
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 hours ago
Last modified
Categories
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
| Buyer | What they run it for |
|---|---|
| Contact-data builders | Turn a list of domains into legal entity names, emails and phone numbers pulled from each company's own legal page. |
| Compliance and due-diligence teams | Collect 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 teams | Find the real legal entity and a working contact behind a website before writing to it. |
| Data teams enriching company lists | Add 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_failedrow 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_makerslists 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_representativeskeeps the same lines word for word. - One column per social network.
facebook,linkedin,xing,instagram,twitterandyoutubeeach hold the company's profile link, ready to filter on. Share buttons are left out. The full list stays insocial_profiles. - Fax as well as phone.
faxreads the imprint's Fax or Telefax line. - Optional e-mail check. With
verifyEmailson, 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
skipWithoutEmailon, a site whose imprint names no e-mail comes back as a freeno_emailrow 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
excludeinput 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
statusisok. A site that refused the request, hid its imprint behind JavaScript, or publishes none is pushed as an unchargedITEM_STATUSrow. 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:
| Field | What it is |
|---|---|
pageUrl | The page actually read for this site: the imprint/legal-notice URL when one was found, else the start page that was fetched. |
title | The <title> text of the page that was read. |
email | The 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. |
phone | The 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. |
website | The site's root URL (https://<domain>), derived from the start URL's hostname. Never null. |
websites | The 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. |
domain | The site's hostname without www., taken from the start URL. |
imprint_url | The 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_name | The 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. |
street | The street-and-number line of the postal address stated in the imprint body. |
postal_code | The 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. |
city | The city named beside the postal code in the imprint body's address. |
country | The 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_representatives | Every 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_makers | The 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_court | The commercial-register court the imprint names (Registergericht / Amtsgericht / "registered at"). |
register_number | The commercial-register number the imprint carries (HRB / HRA …). |
vat_id | The VAT identification number the imprint states (USt-IdNr / VAT ID), whitespace stripped. For example DE144846566. |
fax | The fax number on the imprint's Fax: or Telefax: line, as digits, in the same form the page states it, like phone. |
social_profiles | Every 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. |
facebook | The first Facebook page linked on the page. |
linkedin | The first LinkedIn company or profile page linked on the page. |
xing | The first XING page linked on the page. |
instagram | The first Instagram profile linked on the page. |
twitter | The first X (Twitter) profile linked on the page, from x.com or twitter.com. |
youtube | The first YouTube channel linked on the page; a single video link only when no channel is linked. |
email_mx_ok | Only 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. |
status | This 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_form | The 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_type | ROW 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:
| Field | What it is |
|---|---|
reason | Why 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). |
rowsFetched | How many rows the source handed over before de-duplication and the cap. |
rowsReturned | How many data rows are in the dataset (0 on an empty result; the count before the charge limit on STOPPED_EARLY). |
rowsRemaining | How 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_urlandpageUrl. 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_STATUSmiss rows,PROFILE_NOT_FOUNDandSTOPPED_EARLYstatus rows.- Start URLs skipped and rows dropped by the exclusion list (
excludedin RUN_SUMMARY). - Duplicate rows dropped on
domainbefore anything is pushed. - The
RUN_SUMMARYentry in the key-value store.
How do I use Impressum Scraper?
- Open the Actor's Store page and press Try for free / Start.
- 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.
- 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.
- Press Start. Rows land in the dataset as they are written. The key-value store's
RUN_SUMMARYentry 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
| Input | Type | Default | What it does |
|---|---|---|---|
startUrls | array | prefilled with https://www.trigema.de | The sites to read, one output row per entry. |
searchQueries | array | none | Search terms resolved to domains through an official search API, then read like startUrls entries. |
searchEngine | string | brave | brave (Brave Search API) or google (Google Custom Search JSON API; needs searchCx). |
searchApiKey | string | none | Your 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. |
searchCx | string | none | Google programmable engine ID, required only for searchEngine: "google". |
proxyConfiguration | object | datacenter Apify proxy | How 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. |
useBrowser | boolean | true | Render pages that answer plain requests with a JavaScript shell. Costs more memory and seconds per hit. |
maxItems | integer | 100 | The most rows one run returns. Fetching and pushing stop there and the log notes the cap. Minimum 1. |
verifyEmails | boolean | false | Check 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. |
skipWithoutEmail | boolean | false | Return a free no_email row instead of the company row when the imprint names no e-mail. |
maxDomainsPerSearchQuery | integer | 20 | The most domains one searchQueries entry may resolve to, so a broad query cannot starve the ones after it. |
exclude | array | none | Domains 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-rowstatusfield tells a filled row (ok) from a site that yielded nothing (no_imprint_found,fetch_failed,timeout,js_required,bad_url,search_failed,aborted, andno_emailwhenskipWithoutEmailis on). A miss row carriesrow_type: "ITEM_STATUS"and is never billed. Rows are de-duplicated bydomain: 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_FOUNDrow carryingreason,rowsFetched,rowsReturnedandrowsRemaining, never an empty dataset that could mean "nothing found" or "it broke". A run cut short by the charge limit ends with aSTOPPED_EARLYrow 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_courtandregister_numberare 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.
statusseparates 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_representativesas the page words it, and indecision_makersone name and role at a time), plus the company's published contact channels. The names appear twice on purpose.legal_representativesis the line as the notice prints it, ready for a letter.decision_makerssplits the same names with their roles, ready for a filter. No other field repeats them. Every row carriesimprint_urlandpageUrl, 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
excludeinput applies the same mechanism to your own list. Every skip and drop is counted inRUN_SUMMARYunderexcluded.
Release notes
- 0.2.59 (2026-09-25): the column
managing_directoris renamedlegal_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_makersas{name, role}besidemanaging_director; one column per social network (facebook,linkedin,xing,instagram,twitter,youtube) besidesocial_profiles, with share buttons left out;fax; the optionalverifyEmailscheck (email_mx_ok) and theskipWithoutEmailoption. - 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
excludeinput 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 underexcludedin RUN_SUMMARY. - 0.2.17:
countrynow 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(nowlegal_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
statusand 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_nameis null rather than guessed from the domain.countryneeds a named country or a VAT-ID prefix.emailis 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_failedrow, and nothing else is tried. - Only an empty input stops the run. A run with no entry in
startUrlsorsearchQueriesfails 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 abad_urlrow. A search term with no key, or a failed search, is asearch_failedrow. A site that could not be read getsfetch_failed,timeout,js_requiredorno_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.