概要
WebReaderAPIでウェブサイトをMarkdownにクロールするには?
開始URLをlimit付きでPOST https://webreaderapi.com/api/v1/crawlに送信します。WebReaderAPIがバックグラウンドでサイトを読み取り、同一ドメインのみ、サイトマップ、robots.txt、Crawl-delayに従い、1ジョブあたり最大10,000ページまで処理します。Markdownページはジョブから1ページ分ずつ取得します。各ページの読み取りは1クレジット(ブラウザ使用時は2クレジット)、$0.07/1Kページより2026年10月時点。KYC不要、暗号資産で支払い。
ジョブの概要
| バッチ | バッチジョブあたり最大100 URL: POST https://webreaderapi.com/api/v1/batch |
|---|---|
| クロール | 1ジョブにつき1つのWebサイトを、最大10,000ページまでクロール。サイトマップ、robots.txt、Crawl-delayに従います: POST https://webreaderapi.com/api/v1/crawl |
| 結果 | GET https://webreaderapi.com/api/v1/jobs/{job_id}、その後次の結果ページには?cursor=… |
| 課金 | 各ページの読み取りは/read呼び出しと同様に課金されます。robots.txtによりスキップされたページと失敗したページは無料です |
| クロールの範囲 | 1つのドメインのみ。他のドメインへのリンクは一覧表示されますが読み取られません |
| ジョブのキャンセル | v1では利用できません: 支払い可能な最大ページ数をlimitに設定してください |
バッチジョブの開始
POST/api/v1/batch
同じオプションでバックグラウンドで最大100 URLを読み取ります。オプションは/readの名前と値を使用します。返答はジョブIDです。値は例示です。
認証:Authorization: Bearer API_KEYヘッダーのAPIキー。
リクエストボディ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
urls | array of strings | はい | httpまたはhttpsのURLを1〜100個。重複は1回だけ読み取ります。 |
options | object | いいえ | url以外の任意の/readパラメータ: 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
}
}レスポンス 202 Accepted
{
"job_id": "job_5c1e8a90d2"
}コード例
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);クロールジョブを開始する
POST/api/v1/crawl
開始URLから1つのWebサイトを幅優先で読み取ります。クローラーは開始URLのドメインに留まり、robots.txtで指定された、または/sitemap.xmlで見つかったsitemapを使用し、robots.txtとCrawl-delayを尊重します。値は例示です。
認証:Authorization: Bearer API_KEYヘッダーのAPIキー。
リクエストボディ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
url | string | はい | 開始ページ。たとえばドキュメントサイトのホームページなどです。 |
limit | integer | いいえ | 読み取るページ数の上限で、最大10,000。ジョブはこの数に達すると停止します。 |
max_depth | integer | いいえ | 開始URLからリンクをたどる最大ステップ数。 |
include_paths | array of strings | いいえ | 保持する単純なパスパターン。たとえば/docs/*などです。 |
exclude_paths | array of strings | いいえ | スキップする単純なパスパターン。たとえば/blog/tag/*などです。 |
options | object | いいえ | url以外の任意の/readパラメータで、すべてのページに適用されます。 |
{
"url": "https://docs.example.com/",
"limit": 500,
"max_depth": 3,
"include_paths": [
"/docs/*"
],
"exclude_paths": [
"/docs/archive/*"
],
"options": {
"format": "markdown"
}
}レスポンス 202 Accepted
{
"job_id": "job_8b27f41c0e"
}コード例
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);ジョブのステータスと結果を取得する
GET/api/v1/jobs/{job_id}
ジョブのステータスと、準備ができた次の結果を返します。resultsの各項目は、完全な/readレスポンスか、読み取れなかったページの{url, error}のいずれかです。next_cursorが存在する場合は、cursorにその値を設定してもう一度呼び出してください。存在しない場合は、まだ準備ができた結果はありません。値は例示です。
認証:Authorization: Bearer API_KEYヘッダーのAPIキー。
パラメータ
| 名前 | 場所 | 型 | 必須 | 説明 |
|---|---|---|---|---|
job_id | path | string | はい | /batchまたは/crawlが返したID。 |
cursor | query | string | いいえ | 前回の呼び出しで得たnext_cursorの値。続きの結果を取得するために使用します。 |
レスポンス 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
}コード例
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);ジョブの完了を待つにはどうすればよいですか?
/jobs/{job_id}をポーリングし、カーソルを保持します。取得した結果を処理したら、cursorにnext_cursorを設定してもう一度呼び出してください。next_cursorが存在せず、statusがdoneであれば、すべて取得済みです。存在せず、ジョブがまだrunningの場合は、待ってから同じカーソルで再度問い合わせてください。
最初は2秒ごとにポーリングし、新しい結果が届かない間は待ち時間を最大30秒まで倍にしていきます。サイトのCrawl-delayを尊重するクロールは時間がかかることがあるため、長い待ち時間は正常であり、コストはかかりません。
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")
クロールはどのページを読み取りますか?
クロールは指定したURLから開始し、幅優先で1リンクステップずつ外側へ進み、最大max_depthまで読み取ります。また、リンクが少ないページを見つけるため、robots.txtで指定されたアドレスまたは/sitemap.xmlにあるサイトのsitemapも読み取ります。
- 同一ドメインのみ。 他のドメインへのリンクは、
include_linksを指定した場合に結果に一覧表示されますが、読み取られません。 - パスフィルター。
include_pathsとexclude_pathsは/blog/*のような単純なパターンを受け付けます。 - ページごとに1コピー。 サイトがcanonical URLを宣言している場合、複数のアドレスから到達したページは1回だけ読み取られます。
- 強制停止。 ジョブは
limitページを読み取ると終了します。
クロールはどのようにサイトを尊重しますか?
すべてのリクエストにはUser-Agent WebReaderAPI/1.0 (+https://webreaderapi.com/bot/)が付与され、クローラーはWebReaderAPIに対するrobots.txtのルール、または該当する名前がない場合はすべてのロボットに対するルールに従います。許可されていないページはスキップされ、エラーblocked_by_robotsとともにresultsに表示されます。コストはかかりません。
robots.txtにCrawl-delayが設定されている場合、クローラーはサイトへの2つのリクエストの間にその時間だけ待機します。サイトがrobots.txtでContent-Signal: ai-input=noを宣言している場合、そのページをあなたのために読み取ることはありません。サイトが当社のリクエストを拒否した場合、その回答をそのまま返します。CAPTCHAを解いたり、アカウントにログインしたり、ブロックやペイウォールを回避しようとしたりすることは決してありません。
クロールのコストはいくらですか?
読み取った各ページには、/read呼び出しと同じコストがかかります: 1クレジット、ブラウザが必要なページは2クレジット、さらにcountryの場合は10クレジット。スキップされたページと失敗したページは無料です。ジョブのcredits_usedには、これまでに課金された分が合計されます。
ブラウザなしで読み取る500ページのドキュメントサイトでは約500クレジットを使用し、10,000ページパック($3.75)で何度もまかなえます。v1ではジョブをキャンセルできないため、limitが支出上限となります。支払ってもよい最大ページ数に設定してください。
バッチとクロール: よくある質問
/batchと多数の/read呼び出しのどちらを使うべきですか?
URLがすでにあり、各回答をすぐに必要としない場合は/batchを使用してください。並行処理とリトライの管理が不要になります。エージェントが今すぐ1ページを必要とする場合は/readを使用してください。
ジョブの結果はどのくらい保持されますか?
準備ができたらすぐに取得してください。結果は取得するまで保持されますが、一度配信されるとページ内容のコピーは保持されないため、必要なものはご自身の側で保存してください。
クロールは他のWebサイトへのリンクをたどれますか?
いいえ。クロールは1つのドメインのみを読み取ります。他のドメインは、指定した場合にリンクとして表示され、読み取りたい場合はそれらのURLを/batchに送ることができます。
開始したクロールを停止できますか?
v1ではできません。ジョブはlimitページで停止するため、予算に合った上限を選んでください。読み取り済みのページは課金されたままになり、読み取られなかったページは無料です。