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
| 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. 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. |
{
"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
{
"job_id": "job_5c1e8a90d2"
}Codebeispiele
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"}}'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"])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. |
{
"url": "https://docs.example.com/",
"limit": 500,
"max_depth": 3,
"include_paths": [
"/docs/*"
],
"exclude_paths": [
"/docs/archive/*"
],
"options": {
"format": "markdown"
}
}Antwort 202 Accepted
{
"job_id": "job_8b27f41c0e"
}Codebeispiele
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/*"]}'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"])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
{
"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
}Codebeispiele
curl -sG "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e" \
-H "Authorization: Bearer $WEBREADER_API_KEY" \
-d cursor=c_000002import 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"))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.
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_linksanfordern, aber sie werden nicht gelesen. - Pfadfilter.
include_pathsundexclude_pathsnehmen 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
limitSeiten 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.