Kort antwoord
Hoe crawl ik een website naar Markdown met WebReaderAPI?
Stuur de start-URL naar POST https://webreaderapi.com/api/v1/crawl met een pagina-limit. WebReaderAPI leest de site op de achtergrond, alleen hetzelfde domein, volgens de sitemap, robots.txt en Crawl-delay, tot 10.000 pagina's per job. Haal de Markdown-pagina's op uit de job, een pagina resultaten per keer. Elke gelezen pagina kost 1 credit (2 met een browser), vanaf US$ 0,07/1K pagina's per oktober 2026. Zonder KYC; betalen met crypto.
Jobs in één oogopslag
| Batch | Tot 100 URL's per batch-job: POST https://webreaderapi.com/api/v1/batch |
|---|---|
| Crawlen | Crawl één website per job, tot 10.000 pagina's, volgens de sitemap, de robots.txt en de Crawl-delay: POST https://webreaderapi.com/api/v1/crawl |
| Resultaten | GET https://webreaderapi.com/api/v1/jobs/{job_id}, daarna ?cursor=… voor de volgende pagina resultaten |
| Facturering | Elke gelezen pagina wordt gefactureerd zoals een /read-aanroep; pagina's die robots.txt overslaat en mislukte pagina's kosten niets |
| Bereik van een crawl | Eén domein; links naar andere domeinen worden vermeld maar niet gelezen |
| Een job annuleren | Niet beschikbaar in v1: stel limit in op het maximale aantal pagina's dat je bereid bent te betalen |
Een batchjob starten
POST/api/v1/batch
Leest tot 100 URL's op de achtergrond met dezelfde opties. Opties gebruiken de namen en waarden van /read. Het antwoord is de job-ID; waarden zijn ter illustratie.
Authenticatie: API-sleutel in de Authorization: Bearer API_KEY-header.
Request body
| Field | Type | Vereist | Beschrijving |
|---|---|---|---|
urls | array of strings | Ja | 1 tot 100 http- of https-URL's. Duplicaten worden één keer gelezen. |
options | object | Nee | Elke /read parameter behalve 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
}
}Response 202 Accepted
{
"job_id": "job_5c1e8a90d2"
}Codevoorbeelden
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);Start een crawl-job
POST/api/v1/crawl
Leest één website vanaf een start-URL, breadth first. De crawler blijft op het domein van de start-URL, gebruikt de sitemap die in robots.txt wordt genoemd of die op /sitemap.xml staat, en respecteert robots.txt en Crawl-delay. Waarden zijn ter illustratie.
Authenticatie: API-sleutel in de Authorization: Bearer API_KEY-header.
Request body
| Field | Type | Vereist | Beschrijving |
|---|---|---|---|
url | string | Ja | De startpagina, bijvoorbeeld de homepagina van een documentatiesite. |
limit | integer | Nee | Maximaal aantal te lezen pagina's, tot 10.000. De job stopt wanneer dit aantal is bereikt. |
max_depth | integer | Nee | Maximaal aantal linkstappen vanaf de start-URL. |
include_paths | array of strings | Nee | Eenvoudige padpatronen om te behouden, zoals /docs/*. |
exclude_paths | array of strings | Nee | Eenvoudige padpatronen om over te slaan, zoals /blog/tag/*. |
options | object | Nee | Elke /read parameter behalve url, toegepast op elke pagina. |
{
"url": "https://docs.example.com/",
"limit": 500,
"max_depth": 3,
"include_paths": [
"/docs/*"
],
"exclude_paths": [
"/docs/archive/*"
],
"options": {
"format": "markdown"
}
}Response 202 Accepted
{
"job_id": "job_8b27f41c0e"
}Codevoorbeelden
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);Jobstatus en resultaten ophalen
GET/api/v1/jobs/{job_id}
Geeft de status van de job en de volgende resultaten die klaar zijn. Elk item van results is ofwel een volledige /read-response of {url, error} voor een pagina die niet kon worden gelezen. Als next_cursor aanwezig is, roep dan opnieuw aan met cursor ingesteld daarop; als het ontbreekt, zijn er nog geen nieuwe resultaten klaar. Waarden zijn ter illustratie.
Authenticatie: API-sleutel in de Authorization: Bearer API_KEY-header.
Parameters
| Naam | In | Type | Vereist | Beschrijving |
|---|---|---|---|---|
job_id | path | string | Ja | De ID die je van /batch of /crawl terugkrijgt. |
cursor | query | string | Nee | De next_cursor-waarde uit de vorige aanroep, om de volgende resultaten op te halen. |
Response 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
}Codevoorbeelden
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);Hoe wacht ik tot een job klaar is?
Poll /jobs/{job_id} en houd de cursor bij. Verwerk de resultaten die je krijgt en roep dan opnieuw aan met cursor ingesteld op next_cursor. Als next_cursor ontbreekt en status is done, heb je alles; als het ontbreekt en de job is nog running, wacht dan en vraag opnieuw met dezelfde cursor.
Begin elke 2 seconden te pollen en verdubbel de wachttijd tot 30 seconden zolang er niets nieuws binnenkomt. Een crawl die de Crawl-delay van een site respecteert kan even duren, dus een lange wachttijd is normaal en kost niets.
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")
Welke pagina's leest een crawl?
Een crawl begint bij je URL en gaat linkstap voor linkstap naar buiten, breadth first, tot max_depth. Hij leest ook de sitemap van de site, vanaf het adres in robots.txt of vanaf /sitemap.xml, om pagina's te vinden waar weinig links naartoe wijzen.
- Alleen hetzelfde domein. Links naar andere domeinen staan in de resultaten als je om
include_linksvraagt, maar ze worden niet gelezen. - Padfilters.
include_pathsenexclude_pathsnemen eenvoudige patronen zoals/blog/*. - Eén kopie per pagina. Wanneer een site een canonieke URL aangeeft, wordt een pagina die via meerdere adressen bereikbaar is één keer gelezen.
- Een harde stop. De job eindigt wanneer hij
limitpagina's heeft gelezen.
Hoe respecteert een crawl de site?
Elke request draagt de User-Agent WebReaderAPI/1.0 (+https://webreaderapi.com/bot/), en de crawler volgt de robots.txt-regels voor WebReaderAPI, of voor alle robots wanneer geen enkele hem noemt. Geweigerde pagina's worden overgeslagen en verschijnen in results met de fout blocked_by_robots; ze kosten niets.
Wanneer robots.txt een Crawl-delay instelt, wacht de crawler zo lang tussen twee requests naar de site. Als een site Content-Signal: ai-input=no in zijn robots.txt aangeeft, lezen we de pagina's niet voor je. Als een site ons verzoek weigert, geven we het antwoord terug: we lossen nooit CAPTCHA's op, loggen niet in op accounts en proberen nooit een blokkade of paywall te omzeilen.
Hoeveel kost een crawl?
Elke gelezen pagina kost wat een /read-aanroep zou kosten: 1 credit, 2 als de pagina een browser nodig had, plus 10 met country. Overgeslagen en mislukte pagina's zijn gratis. De credits_used van de job telt op wat tot nu toe is gefactureerd.
Een documentatiesite van 500 pagina's die zonder browser wordt gelezen, gebruikt ongeveer 500 credits, wat het 10.000 pagina's-pack (US$ 3,75) ruimschoots dekt. Omdat een job in v1 niet kan worden geannuleerd, is limit je bestedingslimiet: stel het in op het maximale aantal pagina's waarvoor je wilt betalen.
Batch en crawl: veelgestelde vragen
Moet ik /batch of veel /read-aanroepen gebruiken?
Gebruik /batch wanneer je de URL's al hebt en niet elk antwoord direct nodig hebt. Het scheelt je het beheren van concurrency en retries. Gebruik /read wanneer een agent nu één pagina nodig heeft.
Hoe lang worden jobresultaten bewaard?
Haal ze op zodra ze klaar zijn. Resultaten wachten tot je ze ophaalt; zodra ze zijn geleverd, wordt er geen kopie van de paginainhoud bewaard, dus sla op wat je nodig hebt aan jouw kant.
Kan een crawl links naar andere websites volgen?
Nee. Een crawl leest één domein. Andere domeinen verschijnen als links wanneer je erom vraagt, en je kunt die URL's naar /batch sturen als je ze wilt laten lezen.
Kan ik een crawl die al is gestart stoppen?
Niet in v1. De job stopt bij limit pagina's, dus kies een limiet die bij je budget past. Al gelezen pagina's blijven gefactureerd; pagina's die nooit zijn gelezen kosten niets.