빠른 답변
WebReaderAPI로 웹사이트를 Markdown으로 크롤링하려면 어떻게 하나요?
시작 URL을 페이지 limit와 함께 POST https://webreaderapi.com/api/v1/crawl로 보내세요. WebReaderAPI은 백그라운드에서 사이트를 읽으며, 같은 도메인만, sitemap, robots.txt 및 Crawl-delay를 따라 작업당 최대 10,000페이지까지 처리합니다. 작업에서 Markdown 페이지를 한 번에 한 페이지씩 수집하세요. 페이지 읽기마다 1크레딧(브라우저 사용 시 2크레딧)이 들며, 2026년 10월 기준 US$0.07/1K 페이지입니다. KYC 없이 암호화폐 결제.
작업 한눈에 보기
| Batch | 배치 작업당 최대 100개의 URL: POST https://webreaderapi.com/api/v1/batch |
|---|---|
| 크롤링 | 작업당 웹사이트 하나를 크롤링하며, 최대 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에서 건너뛴 페이지와 실패한 페이지는 무료입니다 |
| 크롤링 범위 | 하나의 도메인; 다른 도메인으로의 링크는 나열되지만 읽지 않습니다 |
| 작업 취소 | v1에서는 제공되지 않습니다: 지불할 준비가 된 최대 페이지 수를 limit에 설정하세요 |
Batch 작업 시작
POST/api/v1/batch
동일한 옵션으로 최대 100개 URL을 백그라운드에서 읽습니다. 옵션은 /read의 이름과 값을 사용합니다. 응답은 작업 ID이며, 값은 예시입니다.
인증: Authorization: Bearer API_KEY 헤더에 API 키를 넣습니다.
요청 본문
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
urls | array of strings | 예 | http 또는 https URL 1~100개. 중복된 URL은 한 번만 읽습니다. |
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에서 한 웹사이트를 너비 우선 방식으로 읽습니다. 크롤러는 시작 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이면 모든 결과를 받은 것이고, next_cursor가 없는데 작업이 아직 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에서 시작해 한 번에 링크 한 단계씩, 너비 우선(breadth first) 방식으로 최대 max_depth까지 바깥으로 확장됩니다. 또한 robots.txt에 명시된 주소나 /sitemap.xml에서 사이트의 sitemap을 읽어 링크가 거의 연결되지 않은 페이지도 찾아냅니다.
- 동일 도메인만. 다른 도메인으로 향하는 링크는
include_links를 요청할 때 결과에 나열되지만, 읽지는 않습니다. - 경로 필터.
include_paths과exclude_paths는/blog/*같은 단순한 패턴을 받습니다. - 페이지당 한 번만. 사이트가 canonical URL을 선언하면 여러 주소로 접근되는 페이지는 한 번만 읽습니다.
- 확실한 종료 조건. 작업은
limit개의 페이지를 읽으면 종료됩니다.
크롤링은 사이트를 어떻게 존중하나요?
모든 요청에는 User-Agent WebReaderAPI/1.0 (+https://webreaderapi.com/bot/)가 함께 전송되며, 크롤러는 WebReaderAPI에 대한 robots.txt 규칙을 따르고, 해당 이름을 명시한 규칙이 없으면 모든 로봇에 대한 규칙을 따릅니다. 허용되지 않은 페이지는 건너뛰며 results에 오류 blocked_by_robots과 함께 나타나고, 크레딧이 차감되지 않습니다.
robots.txt에 Crawl-delay이 설정되어 있으면 크롤러는 해당 사이트에 요청을 보낼 때마다 그 시간만큼 기다립니다. 사이트가 robots.txt에 Content-Signal: ai-input=no를 선언하면, 해당 페이지를 대신 읽지 않습니다. 사이트가 우리 요청을 거부하면 그 응답을 그대로 반환합니다. 우리는 CAPTCHA를 풀거나, 계정에 로그인하거나, 차단이나 페이월을 우회하려 시도하지 않습니다.
크롤링 비용은 얼마인가요?
페이지를 읽을 때마다 /read 호출과 같은 비용이 듭니다. 즉 1 크레딧이고, 페이지에 브라우저가 필요했다면 2 크레딧이며, country을 사용하면 10 크레딧이 추가됩니다. 건너뛴 페이지와 실패한 페이지는 무료입니다. 작업의 credits_used에는 지금까지 청구된 금액이 합산됩니다.
브라우저 없이 500페이지 분량의 문서 사이트를 읽으면 약 500 크레딧을 사용하며, 10,000페이지 팩(US$3.75)으로 여러 번 충당할 수 있습니다. v1에서는 작업을 취소할 수 없으므로 limit가 지출 한도입니다. 지불할 의사가 있는 최대 페이지 수로 설정하세요.
배치와 크롤링: 자주 묻는 질문
/batch를 사용해야 하나요, 아니면 /read를 여러 번 호출해야 하나요?
이미 URL이 있고 각 응답을 즉시 받을 필요가 없다면 /batch을 사용하세요. 동시성과 재시도를 직접 관리할 필요가 없습니다. 에이전트가 지금 바로 한 페이지를 필요로 한다면 /read을 사용하세요.
작업 결과는 얼마나 보관되나요?
준비되는 즉시 결과를 수집하세요. 결과는 가져올 때까지 대기하며, 전달된 후에는 페이지 콘텐츠의 사본이 보관되지 않으므로 필요한 내용은 직접 저장하세요.
크롤링이 다른 웹사이트로 연결되는 링크를 따라갈 수 있나요?
아니요. 크롤링은 하나의 도메인만 읽습니다. 다른 도메인은 요청할 경우 링크로 나타나며, 해당 URL을 읽고 싶다면 /batch으로 보낼 수 있습니다.
시작된 크롤링을 중지할 수 있나요?
v1에서는 할 수 없습니다. 작업은 limit페이지에서 멈추므로 예산에 맞는 한도를 선택하세요. 이미 읽은 페이지는 계속 청구되며, 읽지 않은 페이지는 무료입니다.