# Batch- und Crawl-API: viele URLs oder eine ganze Website lesen

> Referenz zur Batch- und Crawl-API: bis zu 100 URLs pro Job oder eine Website mit 10.000 Seiten lesen, robots.txt wird respektiert. Ohne KYC.

- URL: https://webreaderapi.com/de/docs/crawl/
- Zuletzt aktualisiert: 2026-10-22
- Herausgeber: WebReaderAPI (https://webreaderapi.com)

Zwei Endpoints starten Hintergrund-Jobs: `/batch` für eine Liste mit bis zu 100 URLs, `/crawl` für eine Website. Beide liefern eine Job-ID; die Seiten holen Sie sich von `/jobs/{job_id}`, jeweils eine Seite Ergebnisse.

**Kurze Antwort — Wie crawle ich eine Website mit WebReaderAPI zu Markdown?**

Senden Sie die Start-URL an `POST https://webreaderapi.com/api/v1/crawl` mit einer Seiten-`limit`. **WebReaderAPI** liest die Website im Hintergrund, nur dieselbe Domain, folgt ihrer Sitemap, robots.txt und Crawl-delay, bis zu 10.000 Seiten pro Job. Holen Sie die Markdown-Seiten aus dem Job, jeweils eine Seite Ergebnisse. Jede gelesene Seite kostet 1 Credit (2 mit Browser), ab 0,07 $/1K Seiten Stand Oktober 2026. Ohne KYC; mit Krypto bezahlen.

## Jobs auf einen Blick

| Attribut | Wert |
| --- | --- |
| Batch | Bis zu 100 URLs pro Batch-Job: `POST https://webreaderapi.com/api/v1/batch` |
| Crawl | Eine Website pro Job crawlen, bis zu 10.000 Seiten, entlang ihrer Sitemap, ihrer robots.txt und ihrer Crawl-delay: `POST https://webreaderapi.com/api/v1/crawl` |
| Ergebnisse | `GET https://webreaderapi.com/api/v1/jobs/{job_id}`, dann `?cursor=…` für die nächste Seite Ergebnisse |
| Abrechnung | Jede gelesene Seite wird wie ein `/read`-Aufruf abgerechnet; von robots.txt übersprungene Seiten und fehlgeschlagene Seiten kosten nichts |
| Umfang eines Crawls | Eine Domain; Links zu anderen Domains werden aufgeführt, aber nicht gelesen |
| Job abbrechen | In v1 nicht verfügbar: Setzen Sie `limit` auf die maximale Seitenzahl, die Sie zu zahlen bereit sind |

## Batch-Job starten

`POST /api/v1/batch`

Liest bis zu 100 URLs im Hintergrund mit denselben Optionen. Die Optionen verwenden die Namen und Werte von [/read](https://webreaderapi.com/de/docs/read/). Die Antwort ist die Job-ID; Werte dienen nur als Beispiel.

Authentifizierung: API-Schlüssel im `Authorization: Bearer API_KEY`-Header.

**Request-Body**

| Feld | Typ | Erforderlich | Beschreibung |
| --- | --- | --- | --- |
| `urls` | array of strings | Ja | 1 bis 100 http- oder https-URLs. Duplikate werden einmal gelesen. |
| `options` | object | Nein | Jeder `/read`-Parameter außer `url`: `format`, `main_content`, `render`, `include_links`, `include_images`, `country`, `max_chars`, `timeout`. |

*Beispiel: three articles as Markdown*

```json
{
  "urls": [
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2",
    "https://example.org/news/launch"
  ],
  "options": {
    "format": "markdown",
    "max_chars": 50000
  }
}
```

**Antwort** `202 Accepted`

```json
{
  "job_id": "job_5c1e8a90d2"
}
```

*curl*

```bash
curl -s "https://webreaderapi.com/api/v1/batch" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}}'
```

*Python*

```python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/batch",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}},
    timeout=30,
)
print(resp.json()["job_id"])
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/batch', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ urls: ['https://example.com/blog/post-1', 'https://example.com/blog/post-2'], options: { format: 'markdown' } }),
});
console.log((await res.json()).job_id);
```

## Crawling-Job starten

`POST /api/v1/crawl`

Liest eine Webseite ab einer Start-URL, breadth first. Der Crawler bleibt auf der Domain der Start-URL, nutzt die in robots.txt genannte oder unter `/sitemap.xml` gefundene Sitemap und beachtet robots.txt und Crawl-delay. Werte sind beispielhaft.

Authentifizierung: API-Schlüssel im `Authorization: Bearer API_KEY`-Header.

**Request-Body**

| Feld | Typ | Erforderlich | Beschreibung |
| --- | --- | --- | --- |
| `url` | string | Ja | Die Startseite, etwa die Homepage einer Dokumentationsseite. |
| `limit` | integer | Nein | Maximale Anzahl zu lesender Seiten, bis zu 10.000. Der Job endet, wenn er diese Zahl erreicht. |
| `max_depth` | integer | Nein | Maximale Anzahl an Link-Schritten weg von der Start-URL. |
| `include_paths` | array of strings | Nein | Einfache Pfadmuster zum Behalten, etwa `/docs/*`. |
| `exclude_paths` | array of strings | Nein | Einfache Pfadmuster zum Überspringen, etwa `/blog/tag/*`. |
| `options` | object | Nein | Jeder `/read`-Parameter außer `url`, angewendet auf jede Seite. |

*Beispiel: the docs section of a site, 500 pages at most*

```json
{
  "url": "https://docs.example.com/",
  "limit": 500,
  "max_depth": 3,
  "include_paths": [
    "/docs/*"
  ],
  "exclude_paths": [
    "/docs/archive/*"
  ],
  "options": {
    "format": "markdown"
  }
}
```

**Antwort** `202 Accepted`

```json
{
  "job_id": "job_8b27f41c0e"
}
```

*curl*

```bash
curl -s "https://webreaderapi.com/api/v1/crawl" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]}'
```

*Python*

```python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/crawl",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]},
    timeout=30,
)
print(resp.json()["job_id"])
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/crawl', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://docs.example.com/', limit: 500, include_paths: ['/docs/*'] }),
});
console.log((await res.json()).job_id);
```

## Job-Status und Ergebnisse abrufen

`GET /api/v1/jobs/{job_id}`

Gibt den Status des Jobs und die nächsten fertigen Ergebnisse zurück. Jedes Element von `results` ist entweder eine vollständige `/read`-Antwort oder `{url, error}` für eine Seite, die nicht gelesen werden konnte. Wenn `next_cursor` vorhanden ist, rufen Sie erneut auf, wobei `cursor` darauf gesetzt ist; fehlt es, sind noch keine weiteren Ergebnisse fertig. Werte sind beispielhaft.

Authentifizierung: API-Schlüssel im `Authorization: Bearer API_KEY`-Header.

**Parameter**

| Name | In | Typ | Erforderlich | Beschreibung |
| --- | --- | --- | --- | --- |
| `job_id` | path | string | Ja | Die von `/batch` oder `/crawl` zurückgegebene ID. |
| `cursor` | query | string | Nein | Der `next_cursor`-Wert aus dem vorherigen Aufruf, um die folgenden Ergebnisse zu erhalten. |

**Antwort** `200`

```json
{
  "job_id": "job_8b27f41c0e",
  "status": "running",
  "results": [
    {
      "url": "https://docs.example.com/docs/install",
      "final_url": "https://docs.example.com/docs/install",
      "status_code": 200,
      "rendered": false,
      "credits_used": 1,
      "title": "Install",
      "markdown": "# Install\n\nRun the installer...",
      "word_count": 418
    },
    {
      "url": "https://docs.example.com/docs/private",
      "error": {
        "code": "blocked_by_robots",
        "message": "robots.txt disallows this page for WebReaderAPI."
      }
    }
  ],
  "next_cursor": "c_000002",
  "credits_used": 1
}
```

*curl*

```bash
curl -sG "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -d cursor=c_000002
```

*Python*

```python
import os
import requests

resp = requests.get(
    "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    params={"cursor": "c_000002"},
    timeout=30,
)
job = resp.json()
print(job["status"], len(job["results"]), job.get("next_cursor"))
```

*Node.js*

```javascript
const res = await fetch('https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e?cursor=c_000002', {
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
});
const job = await res.json();
console.log(job.status, job.results.length, job.next_cursor);
```

## Wie warte ich, bis ein Job fertig ist?

Fragen Sie `/jobs/{job_id}` ab und behalten Sie den Cursor. Verarbeiten Sie die erhaltenen Ergebnisse und rufen Sie dann erneut auf, wobei `cursor` auf `next_cursor` gesetzt ist. Wenn `next_cursor` fehlt und `status` `done` ist, haben Sie alles; wenn es fehlt und der Job noch `running` ist, warten Sie und fragen Sie mit demselben Cursor erneut.

Beginnen Sie mit einer Abfrage alle 2 Sekunden und verdoppeln Sie die Wartezeit bis 30 Sekunden, solange nichts Neues eintrifft. Ein Crawl, der das Crawl-delay einer Website beachtet, kann eine Weile dauern, also ist eine lange Wartezeit normal und kostet nichts.

*Python: einen Job abfragen und jede Seite als .md-Datei speichern*

```python
import os
import pathlib
import re
import time

import requests

JOBS = "https://webreaderapi.com/api/v1/jobs"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


def save_job(job_id, folder="pages"):
    out = pathlib.Path(folder)
    out.mkdir(exist_ok=True)
    cursor, wait, saved = None, 2, 0
    while True:
        params = {"cursor": cursor} if cursor else {}
        job = requests.get(f"{JOBS}/{job_id}", headers=HEADERS, params=params, timeout=30).json()
        for item in job["results"]:
            if "error" in item:
                print("skipped", item["url"], item["error"]["code"])
                continue
            name = re.sub(r"[^a-z0-9]+", "-", item["final_url"].lower()).strip("-")[:120]
            (out / f"{name}.md").write_text(item["markdown"], encoding="utf-8")
            saved += 1
        if job.get("next_cursor"):
            cursor, wait = job["next_cursor"], 2
            continue
        if job["status"] == "done":
            return saved
        time.sleep(wait)
        wait = min(wait * 2, 30)


if __name__ == "__main__":
    print(save_job("job_8b27f41c0e"), "pages saved")

```

## Welche Seiten liest ein Crawl?

Ein Crawl startet bei Ihrer URL und bewegt sich Link-Schritt für Link-Schritt nach außen, breadth first, bis zu `max_depth`. Er liest außerdem die Sitemap der Website, aus der in robots.txt angegebenen Adresse oder aus `/sitemap.xml`, um Seiten zu finden, auf die nur wenige Links zeigen.

- **Nur dieselbe Domain.** Links zu anderen Domains werden in den Ergebnissen aufgeführt, wenn Sie `include_links` anfordern, aber sie werden nicht gelesen.
- **Pfadfilter.** `include_paths` und `exclude_paths` nehmen einfache Muster wie `/blog/*`.
- **Eine Kopie pro Seite.** Wenn eine Website eine kanonische URL angibt, wird eine über mehrere Adressen erreichte Seite einmal gelesen.
- **Ein hartes Limit.** Der Job endet, wenn er `limit` Seiten gelesen hat.

## Wie beachtet ein Crawl die Website?

Jede Anfrage trägt den User-Agent `WebReaderAPI/1.0 (+https://webreaderapi.com/bot/)`, und der Crawler folgt den robots.txt-Regeln für `WebReaderAPI` oder für alle Roboter, wenn keine ihn nennen. Nicht erlaubte Seiten werden übersprungen und erscheinen in `results` mit dem Fehler `blocked_by_robots`; sie kosten nichts.

Wenn robots.txt ein `Crawl-delay` festlegt, wartet der Crawler diese Zeit zwischen zwei Anfragen an die Website. Wenn eine Website in ihrer robots.txt Content-Signal: ai-input=no deklariert, lesen wir ihre Seiten nicht für Sie. Wenn eine Website unsere Suchanfrage ablehnt, geben wir ihre Antwort zurück: Wir lösen nie CAPTCHAs, melden uns nie in Konten an und versuchen nie, eine Sperre oder eine Paywall zu umgehen.

## Was kostet ein Crawl?

Jede gelesene Seite kostet, was ein `/read`-Aufruf kosten würde: 1 Credit, 2 wenn die Seite einen Browser benötigte, plus 10 mit `country`. Übersprungene und fehlgeschlagene Seiten sind kostenlos. Die `credits_used` des Jobs summiert, was bisher berechnet wurde.

Eine Dokumentationsseite mit 500 Seiten, ohne Browser gelesen, verbraucht etwa 500 Credits, was das 10.000 Seiten-Pack (3,75 $) um ein Vielfaches abdeckt. Da ein Job in v1 nicht abgebrochen werden kann, ist `limit` Ihre Ausgabengrenze: Setzen Sie es auf die maximale Anzahl Seiten, für die Sie zu zahlen bereit sind.

## Batch und Crawl: häufige Fragen

### Soll ich /batch oder viele /read-Aufrufe verwenden?

Verwenden Sie `/batch`, wenn Sie die URLs bereits haben und nicht jede Antwort sofort benötigen. Das erspart Ihnen die Verwaltung von Nebenläufigkeit und Wiederholungen. Verwenden Sie `/read`, wenn ein Agent jetzt eine Seite braucht.

### Wie lange werden Job-Ergebnisse aufbewahrt?

Holen Sie sie ab, sobald sie fertig sind. Ergebnisse warten, bis Sie sie abrufen; nach der Zustellung wird keine Kopie des Seiteninhalts aufbewahrt, speichern Sie also selbst, was Sie brauchen.

### Kann ein Crawl Links zu anderen Websites folgen?

Nein. Ein Crawl liest eine Domain. Andere Domains erscheinen als Links, wenn Sie sie anfordern, und Sie können diese URLs an `/batch` senden, wenn Sie sie lesen lassen möchten.

### Kann ich einen gestarteten Crawl stoppen?

In v1 nicht. Der Job endet bei `limit` Seiten, wählen Sie also ein Limit, das zu Ihrem Budget passt. Bereits gelesene Seiten bleiben berechnet; nie gelesene Seiten kosten nichts.

---

WebReaderAPI: WebReaderAPI ist eine Web-Reader-API für KI-Agenten, die jede öffentliche Webseite in sauberes Markdown umwandelt und die Sie mit Krypto bezahlen – ohne ID-Upload. Prepaid-Credits ab 0,07 $/1K Seiten verfallen nie, und robots.txt wird respektiert. Ohne KYC – keine ID, kein Selfie, keine Dokumente. Mit Krypto bezahlen.
