AnyCrawl

Crawl

Eine ganze Website crawlen und in LLM-taugliche strukturierte Daten verwandeln.

Einführung

Die AnyCrawl-Crawl-API entdeckt und verarbeitet mehrere Seiten ab einer Start-URL und nutzt dieselbe seitenweise Extraktionspipeline wie /v1/scrape. Sie arbeitet asynchron: Sie erhalten sofort eine job_id, danach Status per Polling und Ergebnisse seitenweise.

Kernfunktionen

  • Asynchrone Jobs: Crawl einreihen, Ergebnisse später abholen
  • Mehrere Engines: auto (Standard), cheerio, playwright, puppeteer
  • Flexibler Umfang: strategy, max_depth, include_paths, exclude_paths
  • Optionen pro Seite: /v1/scrape-Optionen unter scrape_options
  • Paginierung: Ergebnisse über skip streamen und Antwortgröße steuern

API-Endpunkte

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}

Nutzungsbeispiele

Crawl-Job erstellen

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
    }
  }'

Selektives Scrapen mit scrape_paths

Mit scrape_paths werden Seiten für die Link-Discovery besucht, ohne Inhalt zu extrahieren – das senkt Kosten und Speicher:

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"]
    }
  }'

In diesem Beispiel:

  • Alle Treffer auf /* werden besucht, um Links zu finden
  • Nur Seiten unter /products/*/details oder /products/*/reviews erhalten extrahierten und gespeicherten Inhalt
  • Kategorie- und Navigationsseiten werden gecrawlt, aber nicht gescrapet – Credits und Speicher werden gespart
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;

Status abfragen

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

Ergebnisse abrufen (paginiert)

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

Job abbrechen

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

Anfrageparameter

ParameterTypErforderlichStandardBeschreibung
urlstringJa-Start-URL (Seed) für den Crawl
template_idstringNein-Template-ID für diesen Crawl
variablesobjectNein-Template-Variablen (nur mit template_id)
engineenumNeinautoEngine pro Seite: auto, cheerio, playwright, puppeteer
exclude_pathsarray of stringNein-Auszuschließende Pfade (glob-ähnlich), z. B. /blog/*
include_pathsarray of stringNein-Einzuschließende Pfade (nach Ausschluss)
scrape_pathsarray of stringNein-Pfade für Content-Extraktion; nur Treffer speichern Inhalt. Ohne: alle eingeschlossenen URLs werden gescrapet
max_depthnumberNein10Maximale Tiefe ab der Seed-URL
strategyenumNeinsame-domainCrawl-Umfang: all, same-domain, same-hostname, same-origin
limitnumberNein100Maximale Seitenanzahl
max_agenumberNein-Cache-Maximalalter (ms). 0 überspringt Lesen; weglassen = Server-Standard
store_in_cachebooleanNeintruePage Cache pro Seite schreiben
retrybooleanNeinfalseBei Fehler erneut versuchen
proxystring (URI)Nein-Optionaler Proxy
formatsarray of enumNein["markdown"]Ausgabeformate pro Seite
timeoutnumberNein60000Timeout pro Anfrage (ms)
wait_fornumberNein-Verzögerung vor Extraktion (ms); nur Browser-Engines
wait_untilenumNein-Navigations-Wartebedingung: load, domcontentloaded, networkidle, commit
wait_for_selectorstring, object, or arrayNein-Auf Selektor(en) warten (nur Browser-Engines). Vorrang vor wait_for.
include_tagsarray of stringNein-Nur Elemente mit diesen CSS-Selektoren
exclude_tagsarray of stringNein-Elemente mit diesen CSS-Selektoren ausschließen
only_main_contentbooleanNeintrueNur Hauptinhalt (Header/Footer/Navigation entfernen)
json_optionsobjectNein-Strukturierte JSON-Extraktion (schema, user_prompt, schema_name, schema_description)
extract_sourceenumNeinmarkdownJSON-Quelle: markdown (Standard) oder html
ocr_optionsbooleanNeinfalseOCR nur für Markdown-Bilder; beeinflusst nicht html/rawHtml.
scrape_optionsobjectNein-Optionen pro Seite (wie /v1/scrape, ohne Top-Level url/engine)

Felder in scrape_options

FeldTypStandardHinweise
formatsarray of enum["markdown"]Ausgabeformate: markdown, html, text, screenshot, screenshot@fullPage, rawHtml, json, summary, links
timeoutnumber60000Timeout pro Anfrage (ms)
wait_fornumber-Verzögerung vor Extraktion (ms); nur Browser; niedrigere Priorität als wait_for_selector
wait_for_selectorstring, object, or array-Warten auf Selektor(en) (nur Browser). CSS-String, Objekt { selector, state?, timeout? } oder Array; nacheinander; Vorrang vor wait_for.
include_tagsarray of string-Nur passende CSS-Selektoren
exclude_tagsarray of string-Ausschließen per CSS-Selektoren
only_main_contentbooleantrueNur Hauptinhalt
proxystring (URI)-Optionaler Proxy
json_optionsobject-Strukturierte JSON-Extraktion (schema, user_prompt, schema_name, schema_description)
extract_sourceenummarkdownJSON-Quelle: markdown oder html
ocr_optionsbooleanfalseOCR nur für Markdown-Bilder
max_agenumber-Cache (ms). 0 = kein Lesen; weglassen = Standard
store_in_cachebooleantruePage Cache pro Seite schreiben

Cache-Verhalten

  • Crawl liest derzeit keinen Page Cache, kann aber schreiben.
  • Cache-Steuerung bei gesetztem scrape_options dort konfigurieren.

Antwortformat

1) Erstellung (HTTP 200)

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

Mögliche Fehler

  • 400 Validierungsfehler
{
    "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 Authentifizierungsfehler
{ "success": false, "error": "Invalid API key" }

2) Status (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) Ergebnisseite (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"
        }
    ]
}

Mögliche Fehler

  • 400 Ungültige Job-ID / nicht gefunden
{ "success": false, "error": "Invalid job ID", "message": "Job ID must be a valid UUID" }

4) Abbruch (HTTP 200)

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

Mögliche Fehler

  • 404 Nicht gefunden
  • 409 Job bereits beendet
{
    "success": false,
    "error": "Job already finished",
    "message": "Finished jobs cannot be cancelled"
}

Best Practices

  • Einzelseiten: /v1/scrape zur Kostenminimierung; gesamte Site: /v1/crawl.
  • strategy, max_depth und Pfadregeln abstimmen, um Umfang und Kosten zu steuern.
  • scrape_paths nutzen: Navigation/Kategorien nur für Links crawlen, Inhalt nur auf Zielseiten scrapen.
  • formats auf das Nötige beschränken.
  • Ergebnisse per skip paginieren, um große Antworten zu vermeiden.

Kostenoptimierung mit scrape_paths

Ohne scrape_paths (Standard):

  • Alle eingeschlossenen Seiten → besucht + Inhalt extrahiert + gespeichert

Mit scrape_paths:

  • In include_paths, aber nicht in scrape_paths → nur besucht (Link-Discovery)
  • In include_paths und scrape_paths → besucht + extrahiert + gespeichert

Beispiele:

  • E-Commerce: Kategorieseiten crawlen, nur Produktdetailseiten scrapen
  • Dokumentation: Navigation besuchen, nur Artikel extrahieren
  • News: Rubriken durchlaufen, nur Vollartikel speichern

Fehlerbehandlung (Beispiel)

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();
        // handle page.data here
        if (!page.next) break;
        const next = new URL(page.next);
        skip = Number(next.searchParams.get("skip") || 0);
    }
}

Hohe Parallelität

Die Crawl-Warteschlange unterstützt parallele Jobs. Mehrere Crawls einreichen und unabhängig pollen:

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())
    )
);

FAQ

Worin unterscheiden sich /v1/crawl und /v1/scrape?

/v1/scrape lädt eine URL synchron und liefert sofort Inhalt. /v1/crawl findet mehrere Seiten ab einer Start-URL und läuft asynchron.

Wie begrenze ich Umfang und Kosten?

Mit strategy, max_depth, limit und Pfadregeln (include_paths, exclude_paths, scrape_paths).

Unterschied include_paths vs. scrape_paths?

  • include_paths: welche URLs besucht werden (Link-Discovery)
  • scrape_paths: wo Inhalt extrahiert und gespeichert wird

Ohne scrape_paths werden alle eingeschlossenen URLs gescrapet (abwärtskompatibel).

Wie senke ich Crawl-Kosten?

Mit scrape_paths Navigation/Kategorien nur für Links besuchen, Inhalt nur bei passenden Pfaden speichern.

Wie paginiere ich Ergebnisse?

Query-Parameter skip; bei next in der Antwort der Link zur nächsten Seite.

Warum fehlen html/markdown?

Prüfen Sie scrape_options.formats und ob die Engine die Formate unterstützt.

Statuswerte

status entspricht dem Job-Modell:

StatusBedeutung
pendingIn Warteschlange oder laufend (noch nicht fertig)
completedErfolgreich abgeschlossen
failedMit Fehler beendet
cancelledAbgebrochen; keine weitere Verarbeitung

OpenAPI (autogeneriert)

Siehe API-Referenz:

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