AnyCrawl

Crawl

전체 웹사이트를 크롤링해 LLM에 맞는 구조화 데이터로 변환합니다.

소개

AnyCrawl 크롤 API는 시드 URL에서 여러 페이지를 발견·처리하며, 페이지마다 /v1/scrape과 동일한 추출 파이프라인을 적용합니다. 비동기입니다: 즉시 job_id를 받은 뒤 상태를 폴링하고 결과를 페이지 단위로 가져옵니다.

핵심 기능

  • 비동기 작업: 크롤을 큐에 넣고 나중에 결과 수신
  • 다중 엔진: auto(기본), cheerio, playwright, puppeteer
  • 유연한 범위 제어: strategy, max_depth, include_paths, exclude_paths
  • 페이지별 옵션: scrape_options 아래에서 /v1/scrape 옵션 재사용
  • 페이지네이션: skip으로 결과를 나눠 페이로드 크기 제어

API 엔드포인트

POST    https://api.anycrawl.dev/v1/crawl
GET     https://api.anycrawl.dev/v1/crawl/{jobId}/status
GET     https://api.anycrawl.dev/v1/crawl/{jobId}?skip=0
DELETE  https://api.anycrawl.dev/v1/crawl/{jobId}

사용 예

크롤 작업 생성

curl -X POST "https://api.anycrawl.dev/v1/crawl" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://anycrawl.dev",
    "engine": "cheerio",
    "strategy": "same-domain",
    "max_depth": 5,
    "limit": 100,
    "exclude_paths": ["/blog/*"],
    "scrape_options": {
      "formats": ["markdown"],
      "timeout": 60000
    }
  }'

scrape_paths로 선택적 스크래핑

scrape_paths로 링크 발견만 할 페이지를 방문하고 본문 추출은 생략할 수 있습니다. 비용과 저장 공간을 줄입니다.

curl -X POST "https://api.anycrawl.dev/v1/crawl" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://shop.example.com",
    "engine": "cheerio",
    "strategy": "same-domain",
    "max_depth": 5,
    "limit": 200,
    "include_paths": ["/*"],
    "scrape_paths": ["/products/*/details", "/products/*/reviews"],
    "scrape_options": {
      "formats": ["markdown", "json"]
    }
  }'

이 예에서는:

  • /*에 맞는 모든 페이지는 링크 발견을 위해 방문됩니다.
  • /products/*/details 또는 /products/*/reviews에 맞는 페이지만 본문 추출·저장됩니다.
  • 카테고리·네비게이션 페이지는 크롤되지만 스크래핑되지 않아 크레딧과 저장을 절약합니다.
const start = await fetch("https://api.anycrawl.dev/v1/crawl", {
    method: "POST",
    headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
        url: "https://anycrawl.dev",
        engine: "cheerio",
        strategy: "same-domain",
        max_depth: 5,
        limit: 100,
        exclude_paths: ["/blog/*"],
        scrape_options: { formats: ["markdown"], timeout: 60000 },
    }),
});
const startResult = await start.json();
const jobId = startResult.data.job_id;

상태 폴링

curl -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396/status"

결과 가져오기(페이지네이션)

curl -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396?skip=0"

작업 취소

curl -X DELETE -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396"

요청 파라미터

파라미터타입필수기본값설명
urlstring-크롤을 시작할 시드 URL
template_idstring아니오-이번 크롤에 사용할 템플릿 ID
variablesobject아니오-템플릿 변수(template_id가 있을 때만 사용)
engineenum아니오auto페이지별 스크래핑 엔진: auto, cheerio, playwright, puppeteer
exclude_pathsarray of string아니오-제외할 경로 규칙(glob 유사), 예: /blog/*
include_pathsarray of string아니오-크롤에 포함할 경로 규칙(제외 이후 적용)
scrape_pathsarray of string아니오-본문 추출 경로 규칙. 일치하는 URL만 내용이 저장됩니다. 미설정 시 포함된 URL은 모두 스크래핑
max_depthnumber아니오10시드 URL 기준 최대 깊이
strategyenum아니오same-domain크롤 범위: all, same-domain, same-hostname, same-origin
limitnumber아니오100크롤할 최대 페이지 수
max_agenumber아니오-캐시 최대 수명(ms). 0이면 캐시 읽기 생략, 생략 시 서버 기본값
store_in_cacheboolean아니오true페이지별 스크래핑에 페이지 캐시를 쓸지 여부
retryboolean아니오false실패 시 재시도 여부
proxystring (URI)아니오-선택 프록시 URL
formatsarray of enum아니오["markdown"]페이지별 스크래핑 출력 형식
timeoutnumber아니오60000요청당 타임아웃(ms)
wait_fornumber아니오-추출 전 지연(ms), 브라우저 엔진만
wait_untilenum아니오-브라우저 탐색 대기: load, domcontentloaded, networkidle, commit
wait_for_selectorstring, object, or array아니오-하나 이상의 선택자 대기(브라우저 엔진만). wait_for보다 우선.
include_tagsarray of string아니오-CSS 선택자에 맞는 요소만 포함
exclude_tagsarray of string아니오-CSS 선택자에 맞는 요소 제외
only_main_contentboolean아니오true본문만 추출(헤더·푸터·내비 등 제거)
json_optionsobject아니오-구조화 JSON 추출 옵션(schema, user_prompt, schema_name, schema_description)
extract_sourceenum아니오markdownJSON 추출 소스: markdown(기본) 또는 html
ocr_optionsboolean아니오false마크다운 이미지에만 OCR 강화. 마크다운 생성에만 영향, html/rawHtml는 아님.
scrape_optionsobject아니오-페이지별 스크래프 옵션(최상위 url/engine 제외하고 /v1/scrape과 동일)

scrape_options 필드

| Field | Type | Default | Notes | | ------------------- | ------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | formats | array of enum | ["markdown"] | 출력 형식: markdown, html, text, screenshot, screenshot@fullPage, rawHtml, json, summary, links | | timeout | number | 60000 | 요청당 타임아웃(ms) | | wait_for | number | - | 추출 전 지연(ms), 브라우저만, wait_for_selector보다 낮은 우선순위 | | wait_for_selector | string, object, or array | - | 브라우저에서만. CSS 문자열, 객체 { selector: string, state?: "attached" \| "visible" \| "hidden" \| "detached", timeout?: number }, 또는 혼합 배열. 순차 대기. wait_for보다 우선. | | include_tags | array of string | - | CSS 선택자에 맞는 요소만 포함 | | exclude_tags | array of string | - | CSS 선택자에 맞는 요소 제외 | | only_main_content | boolean | true | 본문만 추출 | | proxy | string (URI) | - | 선택 프록시 URL | | json_options | object | - | 구조화 JSON 추출 옵션 | | extract_source | enum | markdown | JSON 추출 소스: markdown(기본) 또는 html | | ocr_options | boolean | false | 마크다운 이미지 OCR만. 마크다운 생성에만 영향. | | max_age | number | - | 캐시 최대 수명(ms). 0이면 캐시 읽기 생략 | | store_in_cache | boolean | true | 페이지별 스크래핑에 페이지 캐시 쓰기 여부 |

캐시 동작

  • 크롤은 현재 전체 크롤 요청에 대해 페이지 캐시를 읽지 않지만, 쓰기는 할 수 있습니다.
  • scrape_options를 넘기면 그 안에서 캐시를 제어하세요.

응답 형식

1) 생성(HTTP 200)

{
    "success": true,
    "data": {
        "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396",
        "status": "created",
        "message": "Crawl job has been queued for processing"
    }
}

가능한 오류

  • 400 유효성 검사 오류
{
    "success": false,
    "error": "Validation error",
    "message": "Invalid enum value...",
    "data": {
        "type": "validation_error",
        "issues": [
            { "field": "engine", "message": "Invalid enum value", "code": "invalid_enum_value" }
        ],
        "status": "failed"
    }
}
  • 401 인증 오류
{ "success": false, "error": "Invalid API key" }

2) 상태(HTTP 200)

{
    "success": true,
    "message": "Job status retrieved successfully",
    "data": {
        "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396",
        "status": "completed",
        "start_time": "2025-05-25T07:56:44.162Z",
        "expires_at": "2025-05-26T07:56:44.162Z",
        "credits_used": 0,
        "total": 120,
        "completed": 30,
        "failed": 2
    }
}

3) 결과 페이지(HTTP 200)

{
    "success": true,
    "status": "pending",
    "total": 120,
    "completed": 30,
    "credits_used": 12,
    "next": "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396?skip=100",
    "data": [
        {
            "url": "https://anycrawl.dev/",
            "title": "AnyCrawl",
            "markdown": "# AnyCrawl...",
            "timestamp": "2025-05-25T07:56:44.162Z"
        }
    ]
}

가능한 오류

  • 400 잘못된 작업 ID / 없음
{ "success": false, "error": "Invalid job ID", "message": "Job ID must be a valid UUID" }

4) 취소(HTTP 200)

{
    "success": true,
    "message": "Job cancelled successfully",
    "data": { "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396", "status": "cancelled" }
}

가능한 오류

  • 404 없음
  • 409 이미 종료된 작업
{
    "success": false,
    "error": "Job already finished",
    "message": "Finished jobs cannot be cancelled"
}

모범 사례

  • 단일 페이지는 비용 절감을 위해 /v1/scrape, 사이트 전체 데이터는 /v1/crawl.
  • strategy, max_depth, 경로 규칙으로 범위와 비용 조절.
  • scrape_paths로 비용 절감: 네비·카테고리는 링크만, 본문 페이지만 스크래핑.
  • 실제로 필요한 만큼만 formats로 출력 크기 제한.
  • skip으로 페이지네이션해 과도하게 큰 응답 방지.

scrape_paths로 비용 최적화

scrape_paths 없음(기본):

  • 포함된 모든 페이지 → 방문 + 본문 추출 + 저장

scrape_paths 있음:

  • include_paths에는 있으나 scrape_paths에는 없음 → 방문만(링크 발견)
  • include_pathsscrape_paths 모두에 해당 → 방문 + 본문 추출 + 저장

예:

  • 이커머스: 모든 카테고리는 크롤하되 상품 상세만 스크래핑
  • 문서: 모든 네비는 방문, 본문은 글 페이지만 추출
  • 뉴스: 모든 섹션 탐색, 전체 기사 페이지만 저장

오류 처리 예

async function fetchAllResults(jobId) {
    let skip = 0;
    while (true) {
        const res = await fetch(`https://api.anycrawl.dev/v1/crawl/${jobId}?skip=${skip}`);
        if (!res.ok) {
            const err = await res.json().catch(() => ({}));
            throw new Error(err.message || `HTTP ${res.status}`);
        }
        const page = await res.json();
        // page.data 처리
        if (!page.next) break;
        const next = new URL(page.next);
        skip = Number(next.searchParams.get("skip") || 0);
    }
}

높은 동시성 사용

크롤 큐는 동시 작업을 지원합니다. 여러 크롤 작업을 제출하고 각각 독립적으로 폴링하세요.

const seeds = ["https://site-a.com", "https://site-b.com", "https://site-c.com"];
const jobs = await Promise.all(
    seeds.map((url) =>
        fetch("https://api.anycrawl.dev/v1/crawl", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ url, engine: "cheerio" }),
        }).then((r) => r.json())
    )
);

자주 묻는 질문

/v1/crawl/v1/scrape의 차이는?

/v1/scrape은 단일 URL을 동기로 가져와 즉시 콘텐츠를 반환합니다. /v1/crawl은 시드에서 여러 페이지를 발견하며 비동기로 실행됩니다.

크롤 범위와 비용을 어떻게 제한하나요?

strategy, max_depth, limit, 경로 규칙(include_paths, exclude_paths, scrape_paths)을 사용하세요.

include_pathsscrape_paths의 차이는?

  • include_paths: 어떤 URL을 방문할지(링크 발견)
  • scrape_paths: 어떤 URL에서 본문을 추출·저장할지

scrape_paths가 없으면 포함된 URL은 모두 스크래핑됩니다(이전과 동일).

크롤 비용을 더 줄이려면?

scrape_paths로 네비·카테고리는 링크 발견만 하고 내용은 저장하지 않습니다. scrape_paths 패턴에 맞는 페이지만 추출·저장하세요.

결과를 어떻게 페이지네이션하나요?

skip 쿼리 파라미터를 사용합니다. 응답에 next가 있으면 따라가 다음 페이지를 가져옵니다.

일부 작업에 html/markdown이 없는 이유는?

scrape_options.formats에 원하는 형식이 포함되어 있고, 선택한 엔진이 지원하는지 확인하세요.

상태 값

작업 status는 작업 모델에 정의된 값을 따릅니다.

Status의미
pending큐에 있거나 진행 중(아직 끝나지 않음)
completed성공적으로 완료
failed오류로 종료
cancelled취소됨, 더 이상 처리 없음

OpenAPI(자동 생성)

자동 생성 문서는 API 레퍼런스를 참고하세요.

  • POST /v1/crawl
  • GET /v1/crawl/{jobId}/status
  • GET /v1/crawl/{jobId}
  • DELETE /v1/crawl/{jobId}