Docs
Anmelden

So funktioniert es

Jede Crawling-API-Anfrage nimmt eine Ziel-URL entgegen und gibt die Seite zurück, die das Ziel einem echten Browser an der richtigen Geolocation, mit dem richtigen Geräteprofil und nach dem Lösen aller Anti-Bot-Challenges geliefert hätte. Bei jedem Aufruf passieren drei Dinge nacheinander:

  1. Routing. Die Anfrage wird über einen Residential- oder Datacenter-Exit-Node gesendet — standardmäßig automatisch oder in einem bestimmten Land, wenn Sie country= übergeben. Sticky Sessions sind verfügbar, sodass eine Folge von Aufrufen dieselbe IP wiederverwendet.
  2. Rendering. Wenn Sie sich mit einem JavaScript token authentifizieren, wird die URL in einem echten Headless-Browser geladen. Page-Wait, Scroll, Click und AJAX-Idle-Steuerungen lassen Sie auf den tatsächlichen Inhalt warten, statt auf die initiale HTML-Hülle.
  3. Anti-Bot-Bypass. Cloudflare, PerimeterX, DataDome, hCaptcha und andere gängige Challenges werden serverseitig gelöst. Sie erhalten das HTML nach der Challenge, nicht die Challenge-Seite.

Derselbe Endpoint deckt alle drei ab. Übergeben Sie nur die Parameter, die Sie benötigen — es gibt keine separate „JS-Rendering-API" oder „Anti-Bot-API". Wenn Sie keine Parameter übergeben, die nur dem JS-Token vorbehalten sind, nimmt die Anfrage den günstigen, schnellen Pfad; sobald Sie es tun, wechselt die Anfrage in den Rendering-Pfad. Die Preise pro erfolgreicher Response sind in beiden Fällen gleich.

Token

Die Authentifizierung erfolgt über einen von zwei Token-Typen — beide existieren auf einem einzigen Konto, beide authentifizieren denselben Endpoint:

  • Normal Token (TCP) — für statische HTML- oder JSON-Responses, bei denen Sie keinen Browser benötigen. Schneller, günstiger, wird für die Mehrheit unkomplizierter Scrape-Ziele verwendet.
  • JavaScript Token — für SPAs, React-/Vue-/Angular-Apps, lazy-geladene Feeds und jedes Ziel, das Inhalte hinter clientseitigem Rendering versteckt. Erforderlich, um page_wait, ajax_wait, scroll und css_click_selector zu nutzen.

Wenn eine Normal-Token-Anfrage einen leeren Body oder ein 525 (Challenge konnte nicht gelöst werden) zurückgibt, ist der Standardfix der Retry mit dem JavaScript token — die meisten modernen Ziele benötigen einen Browser, selbst wenn ihr initiales HTML vollständig aussieht. Siehe Authentication für den vollständigen Token-Management-Flow.

Concurrency & Preise

Jede Anfrage, die pc_status: 200 zurückgibt, zählt auf Ihr monatliches Kontingent an. Fehlgeschlagene Anfragen (Timeouts, Blockaden, 5xx vom Ziel) sind kostenlos — Retries gegen ein wackliges Upstream überraschen Ihre Rechnung nicht. Concurrency-Limits skalieren mit Ihrem Plan; die Response enthält einen remaining-Header, mit dem Sie proaktiv zurückfahren können, bevor Sie das Cap erreichen. Lang laufende Crawls (intensives JS-Rendering, große page_wait-Werte) sollten den unten beschriebenen Async-Modus verwenden, um den Concurrency-Slot freizugeben, sobald die Anfrage in die Queue gestellt wurde.

Client-Timeouts. Die durchschnittliche Antwortzeit beträgt 4–10 Sekunden pro Anfrage, aber Tail-Latency-Anfragen (intensive SPAs, scroll_interval=60, langsame Upstream-Sites) können länger dauern. Setzen Sie Ihren Client-Timeout auf mindestens 90 Sekunden, damit legitime langsame Responses nicht abbrechen, bevor sie eintreffen.

Weitere clientseitige Empfehlungen. Senden Sie Accept-Encoding: gzip bei jeder Anfrage — die Payloads sind nicht trivial (vollständige HTML-Seiten oder Markdown), und gzip reduziert sie typischerweise auf ein Drittel der Übertragungsgröße. Wenn Sie Scrapy verwenden, deaktivieren Sie den DNS-Cache, damit der API-Host bei langlebigen Crawls auflösbar bleibt.

Endpoint

GEThttps://api.crawlbase.com/?token=YOUR_TOKEN&url=ENCODED_URL
# All requests are GET. The url parameter must be fully URL-encoded.
# Body is returned as the target page's content (HTML, JSON, image, etc).
# Metadata is returned as response headers (pc_status, original_status, url, rid).

Quickstart

curl 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fanthropic'
from crawlbase import CrawlingAPI

api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://github.com/anthropic')
print(res['body'])
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://github.com/anthropic');
console.log(res.body);
require 'crawlbase'
api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://github.com/anthropic')
puts res.body
<?php
use Crawlbase\CrawlingAPI;
$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://github.com/anthropic');
echo $res->body;
package main

import (
    "fmt"
    "github.com/crawlbase/crawlbase-go"
)

func main() {
    api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
    res, _ := api.Get("https://github.com/anthropic", nil)
    fmt.Println(res.Body)
}

Request

Jede Crawling-API-Anfrage ist ein einzelner HTTP-Aufruf an den Endpoint. Die meisten Anfragen sind GETs — übergeben Sie die Query-Parameter unten, um Rendering, Geo, Output-Format und Async-Verhalten zu steuern. Verwenden Sie POST, wenn Sie ein Formular oder einen JSON-Body senden müssen, und PUT für rohe Payload-Uploads.

Request-Parameter

Alle Parameter werden als Query-String-Werte übergeben. Nur token und url sind erforderlich.

Erforderlich

token
stringerforderlich
Ihr Normal- oder JavaScript-Token. Siehe Authentication.
url
stringerforderlich
Die vollständig URL-kodierte Ziel-URL. Muss das Schema enthalten (http:// oder https://).

Routing & Geo

Wählen Sie, wo die Anfrage ihren Ursprung hat und welches Gerät das Ziel sieht. Routing ist wichtig für Storefronts, SERPs und jede Site, die Inhalte per IP lokalisiert — der deutsche Amazon-Katalog ist von einem US-Exit aus selbst mit der richtigen URL nicht erreichbar, und Google-SERPs werden sowohl nach Geolocation als auch nach den URL-Parametern hl/gl in Kombination mit der IP lokalisiert. Setzen Sie country explizit, und die richtige Währung, Sprache und Verfügbarkeit erscheinen automatisch.

country
stringoptional
Zweistelliger ISO-Ländercode (US, GB, DE, JP, …), um den Crawl über die Exit-Nodes dieses Landes zu routen. Standard ist die automatische Geo-Auswahl.
device
desktop | tablet | mobiledesktop
Emulieren Sie den User-Agent und Viewport der gewählten Geräteklasse.
user_agent
stringoptional
Überschreiben Sie den User-Agent-Header. Sparsam einsetzen — die Defaults sind für jedes Ziel optimiert.
tor_network
booleanfalse
Routen Sie die Anfrage über das Tor-Netzwerk, damit Sie .onion-Sites crawlen können. Lassen Sie es bei Clearnet-Zielen weg — Tor-Exits sind langsamer und unzuverlässiger als der Residential-Pool.
Country kann automatisch überschrieben werden

Crawlbase kann den Parameter country überschreiben, um basierend auf der URL automatisch einen Proxy auszuwählen — das liefert auf den meisten Sites die beste Erfolgsrate. Kontaktieren Sie den Support, falls Sie die automatische Proxy-Auswahl deaktivieren müssen.

Die Angabe eines Landes kann die Anzahl erfolgreicher Anfragen reduzieren, daher nutzen Sie es nur, wenn die Geolocation für die Seite, die Sie crawlen, tatsächlich relevant ist. Manche Sites (insbesondere Amazon) werden unabhängig vom übergebenen Land über dedizierte Proxies geroutet — für diese Domains ist jedes Land erlaubt, auch wenn es nicht in der unten unterstützten Liste enthalten ist.

Sie haben Zugriff auf die folgenden Länder:

Australien (AU)Brasilien (BR)Kanada (CA)
Schweiz (CH)China (CN)Deutschland (DE)
Spanien (ES)Finnland (FI)Frankreich (FR)
Vereinigtes Königreich (GB)Indien (IN)Japan (JP)
Mexiko (MX)Niederlande (NL)Norwegen (NO)
Polen (PL)Russland (RU)Seychellen (SC)
Schweden (SE)Türkei (TR)Ukraine (UA)
Vereinigte Staaten (US)

Header & Cookies

Leiten Sie Ihre eigenen Request-Header und Cookies an die Ziel-Site weiter oder fixieren Sie eine Sticky Session, sodass Set-Cookie-Werte aus einem Aufruf beim nächsten erneut abgespielt werden. Nützlich, wenn das Ziel einen Accept-Language, ein CSRF-Cookie oder eine eingeloggte Session benötigt, die über die Anfragen eines Flows hinweg überleben muss.

request_headers
stringoptional
URL-kodierte Liste weiterzuleitender Header, durch Pipe getrennt: accept-language:en-GB|accept-encoding:gzip. Kombinieren Sie es mit get_headers=true, um auch die Response-Header des Ziels sichtbar zu machen.
set_cookies
stringoptional
An das Ziel weiterzuleitende Cookies, im Standard-Cookie-Header-Format: key1=value1; key2=value2.
cookies_session
stringoptional
Sticky-Cookie-Session — Crawlbase spielt die aus früheren Aufrufen zurückgegebenen Cookies bei jedem nachfolgenden Aufruf mit demselben Wert erneut ab. Ein beliebiger String bis zu 32 Zeichen; ein neuer Wert startet eine neue Session. Sessions laufen 300 Sekunden nach dem letzten Aufruf ab.

Erlaubte Header. Nicht jeder Header, den Sie über request_headers übergeben, erreicht die Ziel-Site — Crawlbase entfernt eine kleine Auswahl standardmäßig. Um zu prüfen, was tatsächlich rausgeht, senden Sie eine Test-Anfrage an https://postman-echo.com/headers und inspizieren Sie, was der Echo-Service empfängt. Wenn Sie einen zusätzlichen Header für Ihren Token autorisiert benötigen, kontaktieren Sie den Support mit den Headernamen.

JavaScript-Rendering

Diese Parameter erfordern einen JavaScript token. Sie steuern, wie der Headless-Browser auf Inhalte wartet, bevor das DOM erfasst wird. Wenn Sie merken, dass Sie nach mehreren auf einmal greifen, ist die Reihenfolge zum Nachdenken: zuerst page_wait (eine feste Verzögerung für vorhersehbare Animationen), dann ajax_wait (lassen Sie die feste Verzögerung weg, wenn die Seite nach dem Mount Netzwerk-Requests absetzt), dann scroll (nur wenn der gewünschte Inhalt unterhalb der Falz liegt) und schließlich css_click_selector (nur wenn ein Button oder Akkordeon die Daten gatekeept).

Eine häufige Falle: page_wait „nur zur Sicherheit" zu hoch zu setzen. Jede zusätzliche Millisekunde ist Concurrency, die Sie woanders nicht nutzen können. Beginnen Sie bei 0, erhöhen Sie nur, wenn Sie abgeschnittenen Output sehen, und betrachten Sie ajax_wait als smartere Alternative — es kehrt zurück, sobald das Netzwerk inaktiv wird, statt auf einem festen Timeout zu blockieren.

page_wait
int (ms)0
Warten Sie diese Anzahl Millisekunden nach dem Seitenladen, bevor erfasst wird. Nützlich für Inhalte, die animiert eingeblendet werden.
ajax_wait
booleanfalse
Warten, bis das Netzwerk inaktiv ist (keine Requests für ~500 ms). Am besten für SPAs, die nach dem Mount Daten nachladen.
css_click_selector
stringoptional
CSS-Selektor — Crawlbase klickt das passende Element, bevor erfasst wird. Sonderzeichen URL-kodieren.
scroll
booleanfalse
Vor dem Erfassen zum Seitenende scrollen. Triggert Lazy-Loading.
scroll_interval
int (s)10
Maximale Sekunden, die mit Scrollen verbracht werden. Kombiniert mit scroll=true.
screenshot
booleanfalse
Erfassen Sie ein JPEG der gerenderten Seite. Die URL kommt als screenshot_url in den Response-Headern zurück (oder im JSON-Body, wenn format=json) und läuft nach einer Stunde ab. Für Multi-Shot- oder Full-Page-Workflows greifen Sie stattdessen zur dedizierten Screenshots API.

Screenshot-Output-Optionen. Wenn screenshot=true, ist die Standard-Erfassung die vollständig gerenderte Seite. Um sie auf den Viewport zu beschränken, hängen Sie mode=viewport an; kombinieren Sie es mit width und height (Pixel), um die Erfassung einzugrenzen. Beide haben standardmäßig die Bildschirmabmessungen und wirken nur mit mode=viewport. Beispiel: &screenshot=true&mode=viewport&width=1200&height=800.

Wie scroll abgerechnet wird. Scroll-aktivierte Anfragen werden nach gesamter serverseitiger Verarbeitungszeit abgerechnet. Die ersten 8 Sekunden (Seitenladen + Scrollen kombiniert) zählen als 1 Request; jede weiteren 5 Sekunden darüber hinaus fügen 1 weiteren abgerechneten Request hinzu. Ein 20s-Scroll = 1 (erste 8s) + 1 (9–13s) + 1 (14–18s) + 1 (19–20s, angefangene Blöcke zählen voll) = 4 abgerechnete Requests. Wenn die Seite vor scroll_interval abgeschlossen ist, wird nur die tatsächliche Verarbeitungszeit abgerechnet.

Das maximale scroll_interval beträgt 60 Sekunden — nach 60 s stoppt das Scrollen, und die Response wird zurückgegeben. Wenn Sie scroll_interval=60 setzen, halten Sie die clientseitige Verbindung mindestens 90 Sekunden offen, damit die Response Zeit hat zurückzukommen. Die Kombination von scroll mit page_wait erhöht die gesamte Verarbeitungszeit und damit die Anzahl der abgerechneten Requests.

Der Parameter css_click_selector wirkt nur, wenn Sie den JavaScript token verwenden (er läuft im Headless-Browser, bevor das DOM erfasst wird). Er akzeptiert jeden vollständig spezifizierten, gültigen CSS-Selektor — zum Beispiel eine ID wie #some-button, eine Klasse wie .some-other-button oder einen Attributselektor wie [data-tab-item="tab1"]. URL-kodieren Sie den Wert immer, damit Sonderzeichen den Query-String unbeschadet überstehen.

Wenn der Selektor auf der Seite nicht gefunden wird, schlägt die Anfrage mit pc_status 595 fehl. Um trotzdem eine Response zu erhalten, wenn das Klick-Ziel möglicherweise fehlt, hängen Sie einen universell verfügbaren Selektor als Fallback an — kommagetrennt. Zum Beispiel fällt #some-button,body auf das Klicken von body zurück, wenn #some-button nicht existiert.

Mehrere Selektoren. Um mehrere Elemente nacheinander vor der Erfassung zu klicken, trennen Sie sie mit einem Pipe-Zeichen (|). URL-kodieren Sie den gesamten Wert, einschließlich der Pipe. Beispiel: #start-button und dann .next-page-link zu klicken sieht in Rohform wie #start-button|.next-page-link aus oder URL-kodiert wie %23start-button%7C.next-page-link. Die Klicks erfolgen in der angegebenen Reihenfolge. Fehlt ein Selektor in der Kette, gilt dieselbe pc_status 595-Regel, sodass das ,body-Fallback-Muster pro Selektor funktioniert.

Müssen Sie benutzerdefiniertes JavaScript innerhalb der Seite ausführen, bevor Crawlbase das DOM erfasst (z. B. ein synthetisches Event auslösen, State mutieren, einen Fetch erzwingen)? Das ist ein per-Account-Feature, abhängig von Ihrem Use Case — kontaktieren Sie den Support mit dem, was Sie vorhaben, und wir richten es ein.

Async & Storage

Der Async-Modus schaltet die API von „blockieren, bis ich Ihre Seite habe" auf „dies in die Queue stellen und mir Bescheid geben, wenn es fertig ist". Der Endpoint kehrt sofort mit einer rid zurück; das eigentliche Ergebnis wird an einen von Ihnen angegebenen Webhook geliefert oder in Cloud Storage gespeichert und später per derselben rid abgerufen. Das ist der richtige Modus für Batch-Jobs und langsame Ziele — Async gibt Ihren Concurrency-Slot in dem Moment frei, in dem die Anfrage in die Queue gestellt wird, sodass Sie weiter einreichen können, während Crawls noch laufen. Für Jobs mit hohem Volumen (Millionen von URLs) verwenden Sie den Enterprise Crawler, der vor derselben Async-Pipeline mit Retries, Rate-Management und Result-Delivery sitzt.

Async-Modus aktuell nur für linkedin.com

Das Flag async=true wird derzeit nur für linkedin.com-URLs unterstützt. Wenn Sie Async-Crawls für andere Domains benötigen, kontaktieren Sie den Support mit der Ziel-Domain, damit wir es für Ihren Token aktivieren können.

async
booleanfalse
Sofort mit einer rid zurückkehren statt zu blockieren. Das Ergebnis wird an callback geliefert, falls gesetzt, oder ist über Cloud Storage per rid verfügbar.
callback
URLoptional
Webhook-URL zum Empfangen des Crawl-Ergebnisses. Erforderlich bei async=true, wenn Sie nicht pollen möchten.
store
booleanfalse
Die gecrawlte Seite in Cloud Storage persistieren. Gibt zusätzlich zum Body eine rid zurück.

Output-Format

Die Standard-Response ist der rohe Seiten-Body — genau das, was ein Browser nach Rendering und Anti-Bot-Auflösung erhalten würde. Für die meisten Pipelines ist das die richtige Form (Ihr nachgelagerter Parser verarbeitet HTML direkt). Verwenden Sie format=json, wenn Sie Metadaten (Status, finale URL, RID, Header) in einer einzigen Hülle gebündelt haben möchten, statt sie auf Response-Header und Body zu verteilen. Verwenden Sie scraper= oder autoparse=true, wenn das Ziel eines ist, für das wir bereits einen Parser haben — Sie überspringen den Parsing-Schritt vollständig und erhalten saubere strukturierte Felder zurück statt rohem Markup.

format
html | json | mdhtml
Wählen Sie die Response-Hülle. html gibt die rohe Seite mit Metadaten in den Response-Headern zurück. json verpackt die Seite plus alle Metadaten in einem einzigen JSON-Objekt. md konvertiert die Seite in GitHub-Flavored Markdown — kombinieren Sie es mit md_readability=true, um Nav/Sidebar/Ads vorher zu entfernen.
md_readability
booleanfalse
Nur sinnvoll mit format=md. Bei true führt Crawlbase einen Readability-Pass über die Seite aus, bevor sie in Markdown konvertiert wird — entfernt das Chrome (Nav, Sidebar, Footer, Ad-Slots) und behält den Hauptartikel-Inhalt. Beste Wahl, um Blogposts und Artikel in sauberen LLM-Kontext zu konvertieren.
pretty
booleanfalse
Nur sinnvoll mit format=json. Druckt die JSON-Hülle hübsch mit Einrückung und Zeilenumbrüchen für menschliche Lesbarkeit; in der Produktion weglassen, um Responses klein zu halten.
scraper
stringoptional
Wenden Sie einen eingebauten Scraper an, um strukturierte Daten zu extrahieren statt HTML zurückzugeben. Beispiel: amazon-product-details.
autoparse
booleanfalse
Erkennt den Seitentyp automatisch und wendet den passenden Scraper an. Komfortable Option für „gib mir JSON, wenn du kannst".

Response-Steuerung

Diese Parameter ändern, was die Response enthält oder wie Crawlbase entscheidet, ob eine Anfrage erfolgreich war. Verwenden Sie get_headers und get_cookies, wenn Sie die Response-Header der Ziel-Site oder Set-Cookie-Werte zurückgespiegelt benötigen (sie werden standardmäßig entfernt). Verwenden Sie custom_success_codes, wenn das Ziel legitim einen Nicht-2xx-Status zurückgibt, den Ihre Pipeline als sauberen Fetch behandeln soll — ohne dies wird Crawlbase diese Responses für Sie wiederholen.

get_headers
booleanfalse
Bringt die Response-Header der Ziel-Site nach vorne. Sie kommen mit dem Präfix original_header_* als Response-Header zurück oder gruppiert unter original_headers, wenn format=json.
get_cookies
booleanfalse
Bringt die Set-Cookie-Werte der Ziel-Site nach vorne. Sie kommen als original_set_cookie in den Response-Headern zurück oder unter demselben Schlüssel, wenn format=json.
custom_success_codes
stringoptional
Kommagetrennte Liste von HTTP-Statuscodes, die als erfolgreich behandelt werden sollen — z. B. custom_success_codes=403,429,503. Crawlbase wiederholt diese nicht, und der ursprüngliche Status wird in original_status bewahrt. Verwenden Sie es, wenn das Ziel diese Codes legitim für Ihren Endpoint zurückgibt (auth-gated APIs, regional gesperrte Seiten, deren Body Sie trotzdem haben möchten).

POST-Requests

Verwenden Sie POST, wenn der Ziel-Endpoint einen Request-Body erwartet — Formular-Submissions, JSON-APIs, GraphQL, alles, was nicht in einen Query-String passt. Derselbe Endpoint, dieselben Parameter, dieselbe Response-Form wie GET; nur die HTTP-Methode und der Body ändern sich.

POST funktioniert nur mit Normal-Token

POST-Requests funktionieren nur mit dem Normal token. Der JavaScript token (und die JS-Rendering-Parameter page_wait, ajax_wait, scroll, css_click_selector) sind GET-only — wenn Sie ein Formular auf einer JS-gerenderten Seite absenden müssen, verwenden Sie den JavaScript token mit css_click_selector, um den Formular-Button zu betätigen, statt direkt auf die Formular-URL zu POSTen.

Der Standard-Content-Type ist application/x-www-form-urlencoded. Übergeben Sie die Formularfelder als Request-Body — Crawlbase leitet sie unverändert an das Ziel weiter.

curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://postman-echo.com/post' -G \
  -F 'parameter1=testing some post data' \
  -F 'parameter2=here goes some data'
import requests
from urllib.parse import quote_plus

url = quote_plus('https://postman-echo.com/post')
res = requests.post(
    f'https://api.crawlbase.com/?token=YOUR_TOKEN&url={url}',
    data={'parameter1': 'value', 'parameter2': 'another value'},
)
print(res.status_code, res.text)
const url = encodeURIComponent('https://postman-echo.com/post');
const body = new URLSearchParams({ parameter1: 'value', parameter2: 'another' });

const res = await fetch(`https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body,
});
console.log(res.status, await res.text());
require 'net/http'

uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(token: 'YOUR_TOKEN', url: 'https://postman-echo.com/post')

res = Net::HTTP.post_form(uri, 'parameter1' => 'value', 'parameter2' => 'another')
puts res.code, res.body
<?php
$url  = 'https://postman-echo.com/post';
$body = http_build_query(['parameter1' => 'value', 'parameter2' => 'another']);

$ch = curl_init('https://api.crawlbase.com/?token=YOUR_TOKEN&url=' . urlencode($url));
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "strings"
)

func main() {
    target := url.QueryEscape("https://postman-echo.com/post")
    body   := strings.NewReader("parameter1=value¶meter2=another")

    res, _ := http.Post(
        "https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target,
        "application/x-www-form-urlencoded",
        body,
    )
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}
Nicht missbrauchen

POST darf nicht zum Spammen oder anderweitigen Schädigen von Ziel-Websites verwendet werden. Crawlbase überwacht aktiv missbräuchliche Muster; Konten, die POST für Spam, Credential-Stuffing oder anderen bösartigen Traffic einsetzen, werden gesperrt und gemeldet.

POST mit JSON-Body

Überschreiben Sie den Standard-Content-Type form-urlencoded mit post_content_type. URL-kodieren Sie den Wert (z. B. wird application/json zu application%2Fjson). Der Body wird unverändert an das Ziel weitergeleitet — kodieren Sie ihn selbst als JSON.

curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://postman-echo.com/post' \
  --data-urlencode 'post_content_type=application/json;charset=UTF-8' -G \
  --request POST \
  --data '{"param1":"value","param2":"another"}'
import json, requests
from urllib.parse import quote_plus

url = quote_plus('https://postman-echo.com/post')
res = requests.post(
    f'https://api.crawlbase.com/?token=YOUR_TOKEN'
    f'&url={url}'
    f'&post_content_type=application/json',
    data=json.dumps({'param1': 'value', 'param2': 'another'}),
    headers={'Content-Type': 'application/json'},
)
print(res.status_code, res.text)
const url  = encodeURIComponent('https://postman-echo.com/post');
const ct   = encodeURIComponent('application/json;charset=UTF-8');
const body = JSON.stringify({ param1: 'value', param2: 'another' });

const res = await fetch(
  `https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}&post_content_type=${ct}`,
  { method: 'POST', headers: { 'Content-Type': 'application/json' }, body },
);
console.log(res.status, await res.text());
require 'net/http'
require 'json'

uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(
  token: 'YOUR_TOKEN',
  url: 'https://postman-echo.com/post',
  post_content_type: 'application/json'
)

req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = { param1: 'value', param2: 'another' }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.code, res.body
<?php
$url  = 'https://postman-echo.com/post';
$ct   = urlencode('application/json;charset=UTF-8');
$body = json_encode(['param1' => 'value', 'param2' => 'another']);

$ch = curl_init(
    'https://api.crawlbase.com/?token=YOUR_TOKEN'
    . '&url=' . urlencode($url)
    . '&post_content_type=' . $ct
);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    target := url.QueryEscape("https://postman-echo.com/post")
    ct     := url.QueryEscape("application/json;charset=UTF-8")
    body   := bytes.NewBufferString(`{"param1":"value","param2":"another"}`)

    res, _ := http.Post(
        "https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target+"&post_content_type="+ct,
        "application/json",
        body,
    )
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}

Hinweis: Die Zielseite entscheidet, ob sie den Body akzeptiert. Crawlbase leitet die Anfrage ehrlich weiter — wenn das Ziel wegen einer falschen Body-Form mit 4xx antwortet, erscheint das in original_status, nicht in pc_status. Siehe Errors für das Verzweigungsmuster.

PUT-Requests

PUT funktioniert genauso wie POST — selber Endpoint, selbe Parameter, selbe Body-Kodierungsregeln. Der einzige Unterschied ist die HTTP-Methode.

curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://api.example.com/resource/42' -G \
  --request PUT \
  --header 'Content-Type: application/json' \
  --data '{"name":"updated","status":"active"}'
import requests
from urllib.parse import quote_plus

url = quote_plus('https://api.example.com/resource/42')
res = requests.put(
    f'https://api.crawlbase.com/?token=YOUR_TOKEN&url={url}&post_content_type=application/json',
    data='{"name":"updated","status":"active"}',
    headers={'Content-Type': 'application/json'},
)
print(res.status_code, res.text)
const url  = encodeURIComponent('https://api.example.com/resource/42');
const ct   = encodeURIComponent('application/json');
const body = JSON.stringify({ name: 'updated', status: 'active' });

const res = await fetch(
  `https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}&post_content_type=${ct}`,
  { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body },
);
console.log(res.status, await res.text());
require 'net/http'
require 'json'

uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(
  token: 'YOUR_TOKEN',
  url: 'https://api.example.com/resource/42',
  post_content_type: 'application/json'
)

req = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json')
req.body = { name: 'updated', status: 'active' }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.code, res.body
<?php
$url  = 'https://api.example.com/resource/42';
$ct   = urlencode('application/json');
$body = json_encode(['name' => 'updated', 'status' => 'active']);

$ch = curl_init(
    'https://api.crawlbase.com/?token=YOUR_TOKEN'
    . '&url=' . urlencode($url)
    . '&post_content_type=' . $ct
);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    target := url.QueryEscape("https://api.example.com/resource/42")
    ct     := url.QueryEscape("application/json")
    body   := bytes.NewBufferString(`{"name":"updated","status":"active"}`)

    req, _ := http.NewRequest(
        "PUT",
        "https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target+"&post_content_type="+ct,
        body,
    )
    req.Header.Set("Content-Type", "application/json")

    res, _ := http.DefaultClient.Do(req)
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}

Wie POST erfordert auch PUT das Normal token. Verwenden Sie post_content_type, um den Media-Type des Bodys zu steuern, wenn er nicht form-urlencoded ist.

Verwenden Sie POST/PUT nicht für Spam

Crawlbase überwacht POST- und PUT-Traffic aktiv. Das Senden von Request-Bodys an fremde Drittanbieter-Sites — Comment-Spam, betrügerische Formularübermittlungen, skriptbasierte Account-Erstellung — führt bereits beim ersten Erkennen zur Sperrung des verursachenden Accounts. Verwenden Sie diese Verben für legitime API-Integrationen, Ihre eigenen Staging- und Produktions-Endpoints sowie ausdrücklich erlaubte Automatisierung.

Response

Erfolgreiche Responses geben die Zielseite im Body zurück. Metadaten leben in den Response-Headern.

Header

HeaderBeschreibung
pc_statusCrawlbase-Statuscode. 200 = Erfolg.
original_statusHTTP-Status der Ziel-Site.
urlFinale URL nach Redirects.
ridRequest-ID. Wird zurückgegeben, wenn async=true oder store=true.
content-typeMIME-Typ des Bodys (text/html, application/json, image/png usw.).
original_header_*Wird zurückgegeben, wenn get_headers=true. Jeder Header der Ziel-Site kommt mit einem original_header_-Präfix an (z. B. original_header_x_frame_options). Gruppiert unter original_headers, wenn format=json.
screenshot_urlWird zurückgegeben, wenn screenshot=true. Temporäre JPEG-URL für die gerenderte Seite; läuft eine Stunde nach dem Crawl ab.
original_set_cookieWird zurückgegeben, wenn get_cookies=true. Verkettete Set-Cookie-Werte aus der Response der Ziel-Site.
domain_complexity
auch X-Domain-Complexity
Die Komplexitätsstufe der gecrawlten Domain — eine von standard, moderate oder complex. Spiegelt die Ressourcen wider, die zum Umgehen der Site-Schutzmechanismen erforderlich sind, und entspricht direkt der für die Anfrage abgerechneten Preisstufe. Siehe Komplexitätsstufen unten.
storage_urlWird zurückgegeben, wenn die Anfrage mit store=true erfolgte. Pointer auf die gespeicherte Kopie der Response in Crawlbase Cloud Storage; kombinieren Sie es mit rid, um sie später abzurufen.
Content-Typetext/markdown; charset=utf-8, wenn die Anfrage mit format=md erfolgte; sonst das Standard-text/html oder application/json.
X-Markdown-FlavorMarkdown-Dialekt des Response-Bodys — derzeit GitHub Flavored Markdown (GFM). Wird nur bei format=md ausgegeben.
X-Markdown-FeaturesKommagetrennte Liste der im Body verwendeten GFM-Features (z. B. tables,lists). Lässt Sie einen Parser mit den richtigen aktivierten Erweiterungen wählen. Wird nur bei format=md ausgegeben.
X-Markdown-Base-URLHost der aufgelösten URL (nach allen Redirects). Nützlich, um relative Links im Markdown-Body aufzulösen. Wird nur bei format=md ausgegeben.
X-Markdown-GeneratorIdentifiziert den Konverter — Wert ist ProxyCrawl-API. Wird nur bei format=md ausgegeben.

HTML-Response

Der Standard. format=html (oder gar kein format) gibt den rohen Seiten-Body im HTTP-Body zurück, mit Metadaten in den Response-Headern (url, original_status, pc_status, X-Domain-Complexity, plus alle original_header_*-Einträge, die Sie über get_headers=true aktiviert haben).

GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=html'

Response:
  Headers:
    url: https://github.com/crawlbase
    original_status: 200
    pc_status: 200
    X-Domain-Complexity: standard

  Body:
    <!doctype html><html>
      <head>...</head>
      <body>... (full page HTML) ...</body>
    </html>

JSON-Response

Setzen Sie format=json, um stattdessen dieselben Daten als einzelnes JSON-Objekt zu erhalten:

GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=json'

Response:
  {
    "original_status": 200,
    "pc_status": 200,
    "url": "https://github.com/crawlbase",
    "domain_complexity": "standard",
    "body": "<!doctype html><html>... (full page HTML) ...</html>"
  }

Markdown-Response

format=md gibt die Seite bereits in GitHub Flavored Markdown konvertiert im Body zurück, mit Content-Type: text/markdown; charset=utf-8 und einem Block von X-Markdown-*-Metadaten-Headern (Flavor, Features, Base-URL, Generator) neben den üblichen url / original_status / pc_status. Kombinieren Sie es mit md_readability=true, wenn Sie vor der Konvertierung eine Hauptinhalts-Extraktion (Artikel-Body, kein Chrome) wünschen — siehe den Parameter md_readability.

GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=md'

Response:
  Headers:
    Content-Type: text/markdown; charset=utf-8
    X-Markdown-Flavor: GitHub Flavored Markdown (GFM)
    X-Markdown-Features: tables,lists
    X-Markdown-Base-URL: github.com
    X-Markdown-Generator: ProxyCrawl-API
    url: https://github.com/crawlbase
    original_status: 200
    pc_status: 200

  Body:
    # crawlbase
    ... (markdown text of the page) ...

Abrechenbare Requests

Crawlbase berechnet nur Requests, bei denen pc_status 200 ist und original_status einer der folgenden ist:

CodeBedeutung
200OK
201Created
204No Content
301Moved Permanently
302Found — nur wenn dem Redirect gefolgt wurde und Inhalt zurückgegeben wurde
404Not Found
410Gone

Jeder andere original_status ist kostenlos, ebenso jeder Nicht-200-pc_status. Verwenden Sie diese Liste, wenn Sie eine Nutzungsrechnung mit Ihren Anwendungs-Logs abgleichen.

Domain-Komplexitätsstufen

Das Feld domain_complexity (auch zurückgegeben als X-Domain-Complexity-Response-Header) sagt Ihnen, wie schwierig es war, die Ziel-Domain zu crawlen — und in welche Preisstufe der Request fiel.

  • standard — leicht zu crawlen, minimaler Schutz. Niedrigste Preisstufe.
  • moderate — moderater Anti-Bot-Schutz, der spezialisierte Behandlung erfordert. Mittlere Preisstufe.
  • complex — fortgeschrittener Schutz, der spezialisierte Ressourcen erfordert. Höchste Preisstufe.

Für tier-spezifische Preise siehe Ihren Abonnementplan oder kontaktieren Sie den Vertrieb.

Häufige Muster

JS-gerenderte SPA mit Scrolling

curl 'https://api.crawlbase.com/?token=JS_TOKEN' \
  --data-urlencode 'url=https://feed.example.com' \
  --data-urlencode 'page_wait=2000' \
  --data-urlencode 'scroll=true' \
  --data-urlencode 'scroll_interval=15' -G
from crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'JS_TOKEN'})
res = api.get('https://feed.example.com', {
    'page_wait': 2000,
    'scroll': True,
    'scroll_interval': 15,
})
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'JS_TOKEN' });

const res = await api.get('https://feed.example.com', {
  page_wait: 2000,
  scroll: true,
  scroll_interval: 15,
});
console.log(res.body);
require 'crawlbase'

api = Crawlbase::API.new(token: 'JS_TOKEN')
res = api.get('https://feed.example.com',
  page_wait: 2000,
  scroll: true,
  scroll_interval: 15
)
puts res.body
<?php
use Crawlbase\CrawlingAPI;

$api = new CrawlingAPI(['token' => 'JS_TOKEN']);
$res = $api->get('https://feed.example.com', [
    'page_wait' => 2000,
    'scroll' => true,
    'scroll_interval' => 15,
]);
echo $res->body;
package main

import (
    "fmt"
    "log"
    "github.com/crawlbase/crawlbase-go"
)

func main() {
    api, err := crawlbase.NewCrawlingAPI("JS_TOKEN")
    if err != nil {
        log.Fatal(err)
    }
    res, _ := api.Get("https://feed.example.com", map[string]string{
        "page_wait":       "2000",
        "scroll":          "true",
        "scroll_interval": "15",
    })
    fmt.Println(res.Body)
}

Geo-geroutete Anfrage

# Get the German version of a localized site
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://www.amazon.com/dp/B08N5WRWNW' \
  --data-urlencode 'country=DE' -G
from crawlbase import CrawlingAPI

api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://www.amazon.com/dp/B08N5WRWNW', {'country': 'DE'})
print(res['body'])
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });

const res = await api.get('https://www.amazon.com/dp/B08N5WRWNW', { country: 'DE' });
console.log(res.body);
require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://www.amazon.com/dp/B08N5WRWNW', country: 'DE')
puts res.body
<?php
use Crawlbase\CrawlingAPI;

$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://www.amazon.com/dp/B08N5WRWNW', ['country' => 'DE']);
echo $res->body;
package main

import (
    "fmt"
    "log"
    "github.com/crawlbase/crawlbase-go"
)

func main() {
    api, err := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
    if err != nil {
        log.Fatal(err)
    }
    res, _ := api.Get("https://www.amazon.com/dp/B08N5WRWNW", map[string]string{
        "country": "DE",
    })
    fmt.Println(res.Body)
}

Asynchrones Crawling mit Webhook

curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'async=true' \
  --data-urlencode 'callback=https://your-app.com/webhook' -G

# → returns immediately: { "rid": "a1B2c3D4e5F6" }
# → result POSTed to your callback when ready
from crawlbase import CrawlingAPI

api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://example.com', {
    'async': 'true',
    'callback': 'https://your-app.com/webhook',
})
print(res['rid'])  # → returned immediately; result POSTed to callback later
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });

const res = await api.get('https://example.com', {
  async: true,
  callback: 'https://your-app.com/webhook',
});
console.log(res.rid); // → returned immediately; result POSTed to callback later
require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://example.com',
  async: true,
  callback: 'https://your-app.com/webhook'
)
puts res.rid # → returned immediately; result POSTed to callback later
<?php
use Crawlbase\CrawlingAPI;

$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://example.com', [
    'async' => 'true',
    'callback' => 'https://your-app.com/webhook',
]);
echo $res->rid; // → returned immediately; result POSTed to callback later
package main

import (
    "fmt"
    "log"
    "github.com/crawlbase/crawlbase-go"
)

func main() {
    api, err := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
    if err != nil {
        log.Fatal(err)
    }
    res, _ := api.Get("https://example.com", map[string]string{
        "async":    "true",
        "callback": "https://your-app.com/webhook",
    })
    fmt.Println(res.RID) // → returned immediately; result POSTed to callback later
}
Wann async sinnvoll ist

Async gibt Ihren Concurrency-Slot frei, sobald die Anfrage in die Warteschlange aufgenommen wurde — ein langes Crawling blockiert also kein Budget. Verwenden Sie es für langsame Ziele (schweres JS, langes page_wait), wenn Sie hohe Volumen pushen müssen.

Proxy-Modus

Dieselbe Crawling API kann anstelle eines REST-Endpoints auch als HTTP/HTTPS-Proxy aufgerufen werden — nützlich, wenn Sie einen bestehenden Scraper, ein Browser-Automation-Skript oder einen HTTP-Client haben, der bereits Proxy-Konfiguration unterstützt, und Sie Crawlbase lieber davorschalten als die Request-Schicht neu schreiben möchten.

Richten Sie Ihren Client auf smartproxy.crawlbase.com:8001 (HTTPS, empfohlen) oder smartproxy.crawlbase.com:8000 (HTTP) und übergeben Sie Ihr Token als Proxy-Username. Alle Funktionen der Crawling API — JS-Rendering, Anti-Bot-Bypass, Geo-Routing — funktionieren identisch; der einzige Unterschied ist die Form des Requests.

Proxy-Modus vs. Smart AI Proxy

Zwei Produkte teilen sich denselben Hostnamen, nutzen aber verschiedene Ports — leicht zu verwechseln. Die Funktionen sind im Wesentlichen auf beiden gleich (Country-Routing, Geräteemulation, Sessions, Custom-Header, JS-Rendering via CrawlbaseAPI-*-Steuerungen); sie unterscheiden sich im Abonnement, das Ihnen berechnet wird, und in der Concurrency-/Thread-Stufe, die dieses Abonnement bietet:

  • Crawling API im Proxy-Modus (dieser Abschnitt) → Ports 8000 / 8001. Läuft über Ihren Crawling-API-Plan: dasselbe monatliche Kontingent, dasselbe Concurrency-Budget, dieselbe Abrechnung pro erfolgreichem Request wie bei REST-Mode-Aufrufen. Wählen Sie diese Option, wenn Sie bereits für die Crawling API zahlen und neben dem REST-Endpoint zusätzlich eine Proxy-Schnittstelle möchten.
  • Smart AI Proxy (separates Produkt, siehe Smart Proxy) → Ports 8012 / 8013. Eine eigenständige SKU mit eigenem Abonnement und eigenem Thread-/Concurrency-Modell, dimensioniert für proxy-zentrierte Scraping-Pipelines, die bereits hohe Thread-Zahlen fahren. Darunter dasselbe Netzwerk und dieselben Steuer-Header — die Wahl ist, welcher Vertrag und welche Concurrency-Form zu Ihrem Nutzungsprofil passt.

Faustregel: Wählen Sie das Produkt, dessen Abonnement Sie bereits halten (oder dessen Preismodell zu Ihrem Traffic-Profil passt). Die Capability-Surface ist identisch; die Ports leiten Sie lediglich in die passende Abrechnungs- und Concurrency-Spur.

Schnellstart

Ein erster Aufruf aus Ihrer Shell — Normal token, HTTPS-Proxy:

# HTTPS proxy (recommended)
curl -x 'https://[email protected]:8001' \
  -k 'https://httpbin.org/ip'

# HTTP alternative
curl -x 'http://[email protected]:8000' \
  -k 'https://httpbin.org/ip'
import requests

proxies = {
    'http':  'http://[email protected]:8000',
    'https': 'http://[email protected]:8000',
}
res = requests.get('https://httpbin.org/ip', proxies=proxies, verify=False)
print(res.status_code, res.text)
const { HttpsProxyAgent } = require('https-proxy-agent');

const agent = new HttpsProxyAgent('http://[email protected]:8000');
const res = await fetch('https://httpbin.org/ip', { agent });
console.log(res.status, await res.text());
require 'net/http'

uri  = URI('https://httpbin.org/ip')
http = Net::HTTP.new(uri.host, uri.port,
  'smartproxy.crawlbase.com', 8000, 'YOUR_TOKEN', '')
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

res = http.get(uri.request_uri)
puts res.code, res.body
<?php
$ch = curl_init('https://httpbin.org/ip');
curl_setopt($ch, CURLOPT_PROXY,         'smartproxy.crawlbase.com:8000');
curl_setopt($ch, CURLOPT_PROXYUSERPWD,  'YOUR_TOKEN:');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
package main

import (
    "crypto/tls"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    proxyURL, _ := url.Parse("http://[email protected]:8000")
    client := &http.Client{
        Transport: &http.Transport{
            Proxy:           http.ProxyURL(proxyURL),
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
    }
    res, _ := client.Get("https://httpbin.org/ip")
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}

Für JS-gerenderte Ziele tauschen Sie auf Ihr JavaScript token:

curl -x 'https://[email protected]:8001' \
  -k 'https://spa.example.com'
import requests

proxies = {
    'http':  'http://[email protected]:8000',
    'https': 'http://[email protected]:8000',
}
res = requests.get('https://spa.example.com', proxies=proxies, verify=False)
print(res.status_code)
const { HttpsProxyAgent } = require('https-proxy-agent');

const agent = new HttpsProxyAgent('http://[email protected]:8000');
const res = await fetch('https://spa.example.com', { agent });
console.log(res.status);
require 'net/http'

uri  = URI('https://spa.example.com')
http = Net::HTTP.new(uri.host, uri.port,
  'smartproxy.crawlbase.com', 8000, 'YOUR_JS_TOKEN', '')
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

res = http.get(uri.request_uri)
puts res.code
<?php
$ch = curl_init('https://spa.example.com');
curl_setopt($ch, CURLOPT_PROXY,         'smartproxy.crawlbase.com:8000');
curl_setopt($ch, CURLOPT_PROXYUSERPWD,  'YOUR_JS_TOKEN:');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
package main

import (
    "crypto/tls"
    "fmt"
    "net/http"
    "net/url"
)

func main() {
    proxyURL, _ := url.Parse("http://[email protected]:8000")
    client := &http.Client{
        Transport: &http.Transport{
            Proxy:           http.ProxyURL(proxyURL),
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        },
    }
    res, _ := client.Get("https://spa.example.com")
    fmt.Println(res.Status)
}

Rate-Limits

Das Standard-Rate-Limit im Proxy-Modus liegt bei 20 Requests pro Sekunde (~1,7 Mio. Req./Tag). Concurrency-basierte Clients sollten in Threads statt in RPS denken — bei typischer Crawling-API-Latenz (~4 s für eine Amazon-Produktseite) entspricht das etwa 80 gleichzeitigen Threads. Schnellere Ziele entsprechen weniger Threads.

Wenn Sie an die Obergrenze stoßen, kontaktieren Sie den Support mit Ihrem Use-Case, um höhere Concurrency zu vereinbaren.

Errors & Retries

Die Crawling API liefert in jeder Response zwei Status-Codes: original_status (was die Zielseite zurückgegeben hat) und pc_status (was Crawlbase nach Anwendung von Anti-Bot-, Redirect- und Content-Validierungsregeln daraus gemacht hat). Sie können sich unterscheiden — ein Ziel kann 200 mit leerem Body zurückgeben; in dem Fall ist original_status gleich 200, aber pc_status gleich 520. Verzweigen Sie immer auf pc_status, wenn Sie über einen Retry entscheiden.

Die häufigsten Crawling-API-spezifischen Fehler:

CodeBedeutungAktion
422url fehlt oder ist nicht URL-kodiertKodieren Sie die URL vor dem Senden. Die meisten Clients (libcurl --data-urlencode, Python requests, Node fetch) erledigen das automatisch — handgebaute Query-Strings übersehen es jedoch oft.
520Leere Response vom ZielEinmal wiederholen. Bleibt sie leer, wechseln Sie vom Normal- zum JS-Token — viele Sites liefern an Nicht-Browser-User-Agents nur eine leere Hülle und füllen sie per JS.
521Zielseite ist offline / nicht erreichbarWie einen vorübergehenden Upstream-Fehler behandeln. Backoff + Retry; bleibt es über Minuten bestehen, ist die Site tatsächlich offline.
522Connection-Timeout beim Erreichen des ZielsMit Backoff erneut versuchen. Probieren Sie ein anderes country, wenn das Ziel geo-instabil ist.
523Origin vom gewählten Exit aus nicht erreichbarOhne country wiederholen (Auto-Routing wählen lassen) oder mit einem anderen Land.
525Anti-Bot-Challenge konnte nicht gelöst werdenVom Normal- zum JS-Token wechseln. Wenn Sie bereits auf JS sind, erneut versuchen; wenn es bestehen bleibt, an den Support eskalieren — meist hat das Ziel eine neue Challenge-Variante ausgerollt.
595Selektor nicht gefunden. Die Seite wurde erfolgreich geladen, aber der CSS-Selektor, den Sie über css_click_selector übergeben haben, hat kein Element getroffen.Hängen Sie einen Fallback an den Selektor an (#start-button,body), damit der Klick trotzdem auf einem bekannten Element landet. Siehe die Hinweise zu css_click_selector für das vollständige Muster.
599Interner Crawlbase-FehlerWiederholen. Trifft eine Anfrage diesen Code dauerhaft, kontaktieren Sie den Support mit der rid.

Die vollständige Referenz für HTTP- und pc_status-Codes finden Sie in Status Codes; Error handling beschreibt die empfohlene Retry-with-Backoff-Schleife sowie die SDK-Helfer, die sie für Sie in jeder Sprache implementieren.

Beispiel zur Verankerung. Der häufigste Grund, warum pc_status von original_status abweicht, ist ein CAPTCHA: Die Zielseite gibt 200 zurück (die Captcha-Seite wurde sauber gerendert), aber Crawlbase erkennt die Response als Interstitial und liefert pc_status: 503, damit Sie es umgehen können, anstatt das Captcha-HTML als Ihre Daten zu behandeln.

Nicht-standardisierte pc_status-Codes. Codes außerhalb des üblichen HTTP-Bereichs — 601, 999 und ähnliche — sind interne Marker des Crawlbase-Engineering-Teams. Sie erscheinen in der Response nur, um Ihnen beim Debugging im Support-Kontakt zu helfen; in Anwendungscode müssen Sie sie nicht behandeln.

Retry-Strategie

Die einfache Version: Vorübergehende Fehler (5xx) mit exponentiellem Backoff bis zu einem Limit (typischerweise 3–5 Versuche) wiederholen, Client-Fehler (4xx — die lösen sich nicht von selbst) nicht wiederholen und beim ersten 520/525 einmal den Token-Typ wechseln, bevor Sie weiter retryen. Die SDK-Helfer implementieren diese Schleife mit sinnvollen Defaults; für einen eigenen Client gilt als Faustregel:

  • Erster Retry: ~1 s nach dem Fehler
  • Zweiter Retry: ~3 s nach dem Fehler
  • Dritter Retry: ~10 s nach dem Fehler
  • Danach: loggen + alarmieren; anhaltende Fehler bedeuten meist eine Änderung auf der Zielseite und kein vorübergehendes Netzwerkproblem

Alle Retries gegen diese API sind kostenlos — nur erfolgreiche Responses (pc_status: 200) zählen gegen Ihr Kontingent. Damit ist aggressives Backoff günstig; die einzigen echten Kosten eines Retrys sind die Latenz, die Sie Ihrer Pipeline hinzufügen.

Performance & Best Practices

Bei Kunden, die diese API in großem Maßstab betreiben, treten einige wiederkehrende Muster auf. Wenn Sie diese von Anfang an übernehmen, vermeiden Sie die häufigsten Support-Ticket-Kategorien.

  • Verwenden Sie das günstigste Token, das funktioniert. Greifen Sie nicht standardmäßig „zur Sicherheit" zum JavaScript token — Requests mit dem Normal token sind schneller und verbrauchen weniger Concurrency. Wechseln Sie erst dann zu JS, wenn die Normal-Antwort leer oder durch eine Challenge blockiert ist.
  • Bevorzugen Sie ajax_wait gegenüber page_wait. Feste Wartezeiten verbrauchen bei jedem Request Concurrency, auch bei schnellen. ajax_wait kehrt in dem Moment zurück, in dem die Seite Network-Idle erreicht — im Durchschnitt typischerweise schneller und nur bei wirklich langsam ladenden Seiten langsamer.
  • Leiten Sie hohes Volumen über async + Webhook. Der synchrone Modus ist die richtige Wahl für ad-hoc und interaktive Nutzung. Für Batch-Jobs mit mehr als ein paar hundert URLs hält der async-Modus (oder der Enterprise Crawler) Ihr Concurrency-Budget für neue Einreichungen frei, während laufende Crawls abgeschlossen werden.
  • Wiederverwenden Sie Sessions für zustandsbehaftete Flows. Wenn Ihr Ziel eine angemeldete Session oder Warenkorb-Cookies erfordert, halten Sie eine Session-ID und übergeben Sie diese bei nachfolgenden Requests, sodass dieselbe Exit-IP und derselbe Cookie-Jar wiederverwendet werden. Siehe Authentication für das Session-Cookie-Pattern.
  • Beobachten Sie den remaining-Header. Drosseln Sie, bevor Sie Ihr Concurrency-Limit erreichen, statt es über 429-Fehler zu entdecken — die Antwort enthält die Anzahl der verbleibenden Slots, sodass ein gesunder Client proaktiv pausiert, anstatt auf Fehler zu reagieren.