Crawling API
Rufen Sie jede URL über das Residential-Proxy-Netzwerk von Crawlbase ab — mit optionalem JavaScript-Rendering, Lösen von Bot-Challenges und Geo-Routing. Der universelle Endpoint, der alles andere antreibt.
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:
- 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. - 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.
- 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,scrollundcss_click_selectorzu 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
# 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
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.
US, GB, DE, JP, …), um den Crawl über die Exit-Nodes dieses Landes zu routen. Standard ist die automatische Geo-Auswahl..onion-Sites crawlen können. Lassen Sie es bei Clearnet-Zielen weg — Tor-Exits sind langsamer und unzuverlässiger als der Residential-Pool.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.
accept-language:en-GB|accept-encoding:gzip. Kombinieren Sie es mit get_headers=true, um auch die Response-Header des Ziels sichtbar zu machen.Cookie-Header-Format: key1=value1; key2=value2.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.
scroll=true.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.
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.
rid zurückkehren statt zu blockieren. Das Ergebnis wird an callback geliefert, falls gesetzt, oder ist über Cloud Storage per rid verfügbar.async=true, wenn Sie nicht pollen möchten.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.
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.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.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.amazon-product-details.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.
original_header_* als Response-Header zurück oder gruppiert unter original_headers, wenn format=json.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=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-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))
}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.
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
| Header | Beschreibung |
|---|---|
pc_status | Crawlbase-Statuscode. 200 = Erfolg. |
original_status | HTTP-Status der Ziel-Site. |
url | Finale URL nach Redirects. |
rid | Request-ID. Wird zurückgegeben, wenn async=true oder store=true. |
content-type | MIME-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_url | Wird zurückgegeben, wenn screenshot=true. Temporäre JPEG-URL für die gerenderte Seite; läuft eine Stunde nach dem Crawl ab. |
original_set_cookie | Wird zurückgegeben, wenn get_cookies=true. Verkettete Set-Cookie-Werte aus der Response der Ziel-Site. |
domain_complexityauch 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_url | Wird 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-Type | text/markdown; charset=utf-8, wenn die Anfrage mit format=md erfolgte; sonst das Standard-text/html oder application/json. |
X-Markdown-Flavor | Markdown-Dialekt des Response-Bodys — derzeit GitHub Flavored Markdown (GFM). Wird nur bei format=md ausgegeben. |
X-Markdown-Features | Kommagetrennte 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-URL | Host 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-Generator | Identifiziert 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:
| Code | Bedeutung |
|---|---|
200 | OK |
201 | Created |
204 | No Content |
301 | Moved Permanently |
302 | Found — nur wenn dem Redirect gefolgt wurde und Inhalt zurückgegeben wurde |
404 | Not Found |
410 | Gone |
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' -Gfrom 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' -Gfrom 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 readyfrom 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 laterconst { 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 laterrequire '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 laterpackage 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
}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:
| Code | Bedeutung | Aktion |
|---|---|---|
422 | url fehlt oder ist nicht URL-kodiert | Kodieren 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. |
520 | Leere Response vom Ziel | Einmal 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. |
521 | Zielseite ist offline / nicht erreichbar | Wie einen vorübergehenden Upstream-Fehler behandeln. Backoff + Retry; bleibt es über Minuten bestehen, ist die Site tatsächlich offline. |
522 | Connection-Timeout beim Erreichen des Ziels | Mit Backoff erneut versuchen. Probieren Sie ein anderes country, wenn das Ziel geo-instabil ist. |
523 | Origin vom gewählten Exit aus nicht erreichbar | Ohne country wiederholen (Auto-Routing wählen lassen) oder mit einem anderen Land. |
525 | Anti-Bot-Challenge konnte nicht gelöst werden | Vom 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. |
595 | Selektor 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. |
599 | Interner Crawlbase-Fehler | Wiederholen. 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_waitgegenüberpage_wait. Feste Wartezeiten verbrauchen bei jedem Request Concurrency, auch bei schnellen.ajax_waitkehrt 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.

