# Bundesanzeiger Notices Scraper (Company & Insolvency) (`steadfetch/bundesanzeiger-announcements-extractor`) Actor

Structured German Federal Gazette (Bundesanzeiger) announcements: company filings and court/insolvency notices with area, notice type, case number, company and publication date. Keyword monitor with dedup and category/date filters. For supplier KYB and insolvency monitoring. Public search, no login.

- **URL**: https://apify.com/steadfetch/bundesanzeiger-announcements-extractor.md
- **Developed by:** [Steadfetch](https://apify.com/steadfetch) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 announcement delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Bundesanzeiger Notices Scraper (Company & Insolvency)

Structured German Federal Gazette announcements from the public Bundesanzeiger
search: company filings, court notices and insolvency/winding-up proceedings.
One row per announcement, in a stable schema, with an optional keyword monitor.

Public data only. No login, no CAPTCHA, no paywall, no browser.

### What it does

The Bundesanzeiger is Germany's official gazette. Its search is a server-rendered
Wicket application with no public API and no exportable filters. This Actor reads
the same public search a person would use and returns structured rows:

- **Search mode**  -  one query (free text, category and optional date range) and
  the matching announcements, following the pager up to `maxRows`.
- **Monitor mode**  -  a watchlist of `{ keyword, category, court }` items; each run
  checks every item and delivers only notices never seen before. Announced
  notices are immutable, so a quiet day delivers 0 rows and bills no result.
  `maxRows` caps the *total* new rows delivered by the run, not the checks:
  every watchlist item is always checked and billed once, even when an earlier
  item already filled `maxRows`. Rows beyond the cap stay unseen for the next
  run.

It saves a buyer the manual pagination and the daily re-checking, and it gives
filterable, structured output that the official search does not export.

#### Fields

`company`, `area` (court for court notices, otherwise the published city),
`section` (Bereich), `noticeType`, `caseNumber`, `publishedDate` (ISO 8601),
`category`, `sourceUrl`, plus the common `url` / `scrapedAt` envelope and
`changeType` (`new` in monitor runs, otherwise `null`).

**Privacy guardrail:** the Actor targets legal entities. When a notice concerns a
natural person (a name with no legal form, such as `Dieter Hauschopp` or
`Brasch, Adolf`), `company` and `title` are `null` and only metadata (area,
section, notice type, date, case reference) is kept; personal names are never
extracted as fields (company manual rule 2, RGPD). When the portal publishes an
administrator's name in the court column (`Dr. ... als Verwalter`), `area` is
`null` as well. Detection is deliberately conservative: a company name that
reads like a person's name without a legal form or business word is hidden
rather than risk publishing a personal name.

#### Input

Search mode:

```json
{
  "mode": "search",
  "fulltext": "",
  "category": "3",
  "dateFrom": "20.10.2026",
  "dateTo": "25.10.2026",
  "maxRows": 100,
  "strictSchema": true
}
```

Monitor mode:

```json
{
  "mode": "monitor",
  "watchlist": [
    { "keyword": "GmbH", "category": "3", "court": "" },
    { "keyword": "Insolvenz", "category": "59", "court": "Dresden" }
  ],
  "maxRows": 200,
  "strictSchema": true
}
```

Dates accept `dd.mm.yyyy` (as the portal publishes them) or ISO `YYYY-MM-DD`;
output dates are always ISO. Common category ids: `0` all areas, `3`
GmbH/UG, `59` insolvency and winding-up proceedings, `6` official notices.
The full list is the category selector of the public search form.

### Example output

```json
{
  "url": "https://www.bundesanzeiger.de/pub/de/suchen2?2",
  "sourceUrl": "https://www.bundesanzeiger.de/pub/de/suchen2?2-1.-search~table~panel-rows-1-search~table~row~panel-publication~link",
  "title": "TGB Grundbesitz GmbH & Co. KG",
  "area": "Amtsgericht Dresden",
  "section": "Gerichtlicher Teil",
  "noticeType": "Einstellung",
  "caseNumber": "554 N 1997/98",
  "company": "TGB Grundbesitz GmbH & Co. KG",
  "publishedDate": "2026-09-29",
  "category": "59",
  "changeType": null,
  "scrapedAt": "2026-10-25T13:00:00.000Z"
}
```

### Pricing

Pay per event, no subscription:

- **1.50 USD per 1,000 results** (announcements delivered)  -  charged by the platform through
  `apify-default-dataset-item`, the only per-result charge. Rows rejected by the
  output schema are not pushed and not charged.
- **0.05 USD per 1,000 checks** (watchlist items)  -  charged through the reserved
  `monitor-check` event once per watchlist item actually checked in a monitor run.
  Search runs never charge this event.
- **0.00005 USD per run start**  -  the Apify-recommended `apify-actor-start` price,
  which also includes the first 5 seconds of compute.

Works on the Apify free plan - about 3,333 results for the $5 monthly credit.

**Measured cost:** about 0.002 USD per 1,000 announcements on local runs
(256 MB, plain HTML, no proxy, no browser), leaving a wide margin. The
335-result cat.-3 / five-day query needs 17 result pages at one request per
second and took about 12 s for the first 100 rows locally.

### FAQ

**Does it need an API key?** No. The Bundesanzeiger search is public and
server-rendered.

**Does it use a browser or proxies?** No. Plain HTTP + Cheerio, one request per
second, honest User-Agent.

**Why are some categories empty with a date range?** The portal's category 59
(insolvency) currently ignores the publication-date filter in some combinations
and returns 0 rows even though notices exist; without dates it returns the full
list. This is a source-side behaviour, documented in the PR that added the
Actor, not a scraper bug.

**Is this legal?** The source is a public official gazette. `robots.txt` only
disallows `/nlp` and the maintenance page; the terms documents checked contain no
anti-scraping clause. The Actor never requests `/nlp`, never logs in and never
touches paid content.

**Can I monitor several companies?** Yes: put one watchlist item per keyword
(or company name), optionally with a category and a court filter.

### Maintenance

- Tested daily by Steadfetch against the live portal with the registered default
  input; failures are reported before customers see them.
- If the Wicket form or the result table changes, the run fails with a clear
  message instead of delivering wrong rows. Steadfetch repairs published Actors
  before starting new builds.
- Tested daily, output-schema validated on every run (`strictSchema` defaults to
  true).

# Actor input Schema

## `mode` (type: `string`):

search returns one page set of matching announcements. monitor checks each watchlist keyword and delivers only notices never seen before.

## `fulltext` (type: `string`):

Free-text search (company name, keyword or case reference). Leave empty to browse the chosen category.

## `category` (type: `string`):

Bundesanzeiger category id. Common ids: 0 = all areas, 3 = GmbH/UG, 59 = insolvency and winding-up proceedings, 6 = official notices. See README for the full list.

## `dateFrom` (type: `string`):

Lower bound of the publication date, as dd.mm.yyyy (Apache-style German date). Empty means no lower bound. ISO dates are accepted and converted.

## `dateTo` (type: `string`):

Upper bound of the publication date, as dd.mm.yyyy. Empty means no upper bound. ISO dates are accepted and converted.

## `watchlist` (type: `array`):

Required in monitor mode. One check is billed per watchlist item per run. Each item: { "keyword": "GmbH", "category": "59", "court": "Dessau" } (court is an optional case-insensitive filter on the court/area).

## `maxRows` (type: `integer`):

Maximum number of validated announcements to push to the dataset.

## `strictSchema` (type: `boolean`):

Fail the run when an item does not match the OUTPUT schema. Keep enabled: Steadfetch never publishes Actors without output-schema validation.

## Actor input object example

```json
{
  "mode": "search",
  "fulltext": "GmbH",
  "category": "3",
  "dateFrom": "",
  "dateTo": "",
  "watchlist": [],
  "maxRows": 10,
  "strictSchema": true
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset items validated against the item schema in dataset_schema.json (fields).

# 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 = {
    "mode": "search",
    "fulltext": "GmbH",
    "category": "3",
    "dateFrom": "",
    "dateTo": "",
    "watchlist": [],
    "maxRows": 10,
    "strictSchema": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadfetch/bundesanzeiger-announcements-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 = {
    "mode": "search",
    "fulltext": "GmbH",
    "category": "3",
    "dateFrom": "",
    "dateTo": "",
    "watchlist": [],
    "maxRows": 10,
    "strictSchema": True,
}

# Run the Actor and wait for it to finish
run = client.actor("steadfetch/bundesanzeiger-announcements-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 '{
  "mode": "search",
  "fulltext": "GmbH",
  "category": "3",
  "dateFrom": "",
  "dateTo": "",
  "watchlist": [],
  "maxRows": 10,
  "strictSchema": true
}' |
apify call steadfetch/bundesanzeiger-announcements-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadfetch/bundesanzeiger-announcements-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/9pFrTXEBHKhbdU6Ph/builds/ZezCIPT3DHsJxIUno/openapi.json
