Docs
Se connecter

Fonctionnement

Chaque requête au Crawling API prend une URL cible et retourne la page que cette cible aurait servie à un véritable navigateur dans la bonne zone géographique, avec le bon profil d'appareil, après résolution des défis anti-bot. Trois étapes s'enchaînent à chaque appel :

  1. Routage. La requête est envoyée via un nœud de sortie résidentiel ou datacenter - automatiquement par défaut, ou dans un pays spécifique si vous passez country=. Des sessions persistantes sont disponibles afin qu'une séquence d'appels réutilise la même IP.
  2. Rendu. Si vous vous authentifiez avec un JavaScript token, l'URL est chargée dans un véritable navigateur headless. Les contrôles de page-wait, scroll, click et AJAX-idle vous permettent d'attendre le contenu réel plutôt que la coquille HTML initiale.
  3. Contournement anti-bot. Cloudflare, PerimeterX, DataDome, hCaptcha et autres défis courants sont résolus côté serveur. Vous obtenez le HTML post-défi, pas la page de défi.

Le même point de terminaison couvre les trois cas. Ne passez que les paramètres dont vous avez besoin : il n'y a pas d'« API de rendu JS » ni d'« API anti-bot » séparée. Si vous ne passez pas de paramètres réservés au JS token, la requête emprunte le chemin économique et rapide ; dès que vous le faites, la requête bascule sur le chemin de rendu. La tarification est la même par réponse réussie dans les deux cas.

Tokens

L'authentification utilise l'un des deux types de tokens : tous deux résident sur un même compte et authentifient le même point de terminaison :

  • Normal Token (TCP) : pour les réponses HTML ou JSON statiques où vous n'avez pas besoin d'un navigateur. Plus rapide, moins coûteux, utilisé pour la majorité des cibles de scraping simples.
  • JavaScript Token : pour les SPA, les applications React/Vue/Angular, les flux en lazy-loading et toute cible qui masque le contenu derrière un rendu côté client. Requis pour utiliser page_wait, ajax_wait, scroll et css_click_selector.

Si une requête avec le Normal token retourne un corps vide ou un 525 (défi non résolu), la solution standard est de réessayer avec le JavaScript token : la plupart des cibles modernes ont besoin d'un navigateur même quand leur HTML initial paraît complet. Voir Authentication pour le flux complet de gestion des tokens.

Concurrence & tarification

Chaque requête qui retourne pc_status: 200 est décomptée de votre quota mensuel. Les requêtes échouées (timeouts, blocages, 5xx provenant de la cible) sont gratuites : les nouvelles tentatives contre un upstream instable ne font pas grimper votre facture. Les limites de concurrence évoluent avec votre plan ; la réponse inclut un en-tête remaining que vous pouvez utiliser pour ralentir proactivement avant d'atteindre la limite. Les crawls de longue durée (rendu JS lourd, page_wait élevé) devraient utiliser le mode async ci-dessous pour libérer le créneau de concurrence dès que la requête est mise en file d'attente.

Timeouts côté client. Le temps de réponse moyen est de 4 à 10 secondes par requête, mais les requêtes en queue de latence (SPAs lourdes, scroll_interval=60, sites cibles lents) peuvent prendre plus de temps. Réglez votre timeout client à au moins 90 secondes pour que les réponses lentes mais légitimes ne dépassent pas le délai avant d'arriver.

Autres recommandations côté client. Envoyez Accept-Encoding: gzip sur chaque requête : les charges utiles sont non triviales (pages HTML complètes ou markdown) et gzip les réduit généralement à un tiers de leur taille sur le réseau. Si vous utilisez Scrapy, désactivez le cache DNS pour que l'hôte de l'API reste résoluble sur des crawls de longue durée.

Point de terminaison

GETPOSTPUThttps://api.crawlbase.com/?token=YOUR_TOKEN&url=ENCODED_URL
  • Méthodes : GET (query uniquement), POST (corps form ou JSON), PUT (payload brut).
  • Le paramètre url doit être entièrement URL-encodé.
  • Le body renvoyé correspond au contenu de la page cible (HTML, JSON, image, etc).
  • Les métadonnées sont renvoyées sous forme d'en-têtes de réponse (pc_status, original_status, url, rid).

Démarrage rapide

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

Requête

Chaque requête au Crawling API est un seul appel HTTP vers le point de terminaison. La plupart des requêtes sont des GET : passez les paramètres de requête ci-dessous pour contrôler le rendu, la géolocalisation, le format de sortie et le comportement async. Utilisez POST lorsque vous devez envoyer un formulaire ou un corps JSON, et PUT pour les uploads de charges utiles brutes.

Paramètres de requête

Tous les paramètres sont passés en valeurs de query string. Seuls token et url sont requis.

Requis

token
stringrequis
Votre Normal token ou JavaScript token. Voir Authentication.
url
stringrequis
L'URL cible entièrement URL-encodée. Doit inclure le schéma (http:// ou https://).

Routage & géolocalisation

Choisissez l'origine de la requête et le type d'appareil que voit la cible. Le routage importe pour les boutiques en ligne, les SERP et tout site qui localise son contenu par IP : le catalogue Amazon allemand n'est pas accessible depuis une sortie américaine même avec la bonne URL, et les SERP de Google sont localisées à la fois par géographie et par les paramètres d'URL hl/gl combinés à l'IP. Définissez country explicitement et la bonne devise, langue et offre apparaissent automatiquement.

country
stringoptionnel
Code pays ISO à deux lettres (US, GB, DE, JP, …) pour acheminer le crawl via les nœuds de sortie de ce pays. Par défaut, sélection géographique automatique.
device
desktop | tablet | mobiledesktop
Émule le User-Agent et la fenêtre d'affichage de la classe d'appareil choisie.
user_agent
stringoptionnel
Remplace l'en-tête User-Agent. À utiliser avec parcimonie : les valeurs par défaut sont calibrées pour chaque cible.
tor_network
booleanfalse
Route la requête via le réseau Tor pour vous permettre de crawler des sites .onion. À laisser désactivé pour toute cible sur le clearnet : les sorties Tor sont plus lentes et plus bruyantes que le pool résidentiel.
Le pays peut être surchargé automatiquement

Crawlbase peut écraser le paramètre country pour sélectionner automatiquement un proxy en fonction de l'URL : cela donne le meilleur taux de succès sur la plupart des sites. Contactez le support si vous devez désactiver la sélection automatique de proxy.

Spécifier un pays peut réduire le nombre de requêtes réussies, ne l'utilisez donc que lorsque la géolocalisation importe réellement pour la page que vous crawlez. Certains sites (notamment Amazon) sont routés via des proxies dédiés indépendamment du pays que vous passez : tous les pays sont autorisés pour ces domaines même s'ils ne figurent pas dans la liste prise en charge ci-dessous.

Vous avez accès aux pays suivants :

Australie (AU)Brésil (BR)Canada (CA)
Suisse (CH)Chine (CN)Allemagne (DE)
Espagne (ES)Finlande (FI)France (FR)
Royaume-Uni (GB)Inde (IN)Japon (JP)
Mexique (MX)Pays-Bas (NL)Norvège (NO)
Pologne (PL)Russie (RU)Seychelles (SC)
Suède (SE)Turquie (TR)Ukraine (UA)
États-Unis (US)

En-têtes & cookies

Transférez vos propres en-têtes de requête et cookies vers le site cible, ou épinglez une session persistante pour que les valeurs Set-Cookie d'un appel soient rejouées au suivant. Utile lorsque la cible a besoin d'un Accept-Language, d'un cookie CSRF ou d'une session connectée qui doit survivre à travers les requêtes d'un flux.

request_headers
stringoptionnel
Liste d'en-têtes URL-encodée à transférer, séparée par des barres verticales : accept-language:en-GB|accept-encoding:gzip. À combiner avec get_headers=true pour faire également remonter les en-têtes de réponse de la cible.
set_cookies
stringoptionnel
Cookies à transférer vers la cible, au format standard de l'en-tête Cookie : key1=value1; key2=value2.
cookies_session
stringoptionnel
Session de cookies persistante : Crawlbase rejoue les cookies retournés par les appels précédents sur chaque appel suivant partageant la même valeur. N'importe quelle chaîne jusqu'à 32 caractères ; une nouvelle valeur démarre une nouvelle session. Les sessions expirent 300 secondes après le dernier appel.

En-têtes autorisés. Tous les en-têtes que vous passez via request_headers n'atteindront pas forcément le site cible : Crawlbase en supprime un petit ensemble par défaut. Pour vérifier ce qui est réellement envoyé, envoyez une requête de test à https://postman-echo.com/headers et inspectez ce que le service d'écho reçoit. Si vous avez besoin qu'un en-tête supplémentaire soit autorisé pour votre token, contactez le support avec le ou les noms d'en-têtes.

Rendu JavaScript

Ces paramètres requièrent un JavaScript token. Ils contrôlent comment le navigateur headless attend le contenu avant de capturer le DOM. Si vous vous retrouvez à en utiliser plusieurs à la fois, l'ordre à garder en tête est : page_wait en premier (un délai fixe pour les animations prévisibles), puis ajax_wait (laissez tomber le délai fixe si la page émet des requêtes réseau après le mount), puis scroll (uniquement si le contenu dont vous avez besoin se trouve sous la ligne de flottaison), puis css_click_selector (uniquement si un bouton ou un accordéon verrouille les données).

Un piège fréquent : régler page_wait trop haut « au cas où ». Chaque milliseconde supplémentaire représente de la concurrence que vous ne pouvez pas utiliser ailleurs. Commencez à 0, n'augmentez que lorsque vous constatez une sortie tronquée, et considérez ajax_wait comme une alternative plus intelligente : il retourne dès que le réseau devient inactif plutôt que de bloquer sur un délai fixe.

page_wait
int (ms)0
Attend ce nombre de millisecondes après le chargement de la page avant la capture. Utile pour le contenu qui apparaît avec une animation.
ajax_wait
booleanfalse
Attend que le réseau soit inactif (aucune requête pendant ~500 ms). Idéal pour les SPAs qui récupèrent des données après le mount.
css_click_selector
stringoptionnel
Sélecteur CSS : Crawlbase cliquera sur l'élément correspondant avant la capture. URL-encodez les caractères spéciaux.
scroll
booleanfalse
Défile jusqu'en bas de la page avant la capture. Déclenche le lazy-load.
scroll_interval
int (s)10
Nombre maximum de secondes à passer à défiler. À combiner avec scroll=true.
screenshot
booleanfalse
Capture un JPEG de la page rendue. L'URL est renvoyée dans screenshot_url dans les en-têtes de réponse (ou dans le corps JSON quand format=json) et expire après une heure. Pour les workflows multi-captures ou pleine page, utilisez plutôt l'API Screenshots dédiée.

Options de sortie de la capture d'écran. Quand screenshot=true, la capture par défaut est la page rendue complète. Pour la limiter au seul viewport, ajoutez mode=viewport ; combinez-le avec width et height (en pixels) pour contraindre la capture. Les deux ont par défaut les dimensions de l'écran et ne prennent effet qu'avec mode=viewport. Exemple : &screenshot=true&mode=viewport&width=1200&height=800.

Comment scroll est facturé. Les requêtes avec scroll activé sont facturées au temps total de traitement côté serveur. Les 8 premières secondes (chargement de page + défilement combinés) comptent pour 1 requête ; chaque tranche de 5 secondes supplémentaires ajoute 1 requête facturée. Un défilement de 20 s = 1 (les 8 premières secondes) + 1 (9–13 s) + 1 (14–18 s) + 1 (19–20 s, les blocs partiels comptent comme entiers) = 4 requêtes facturées. Si la page se termine avant scroll_interval, seul le temps de traitement réel est facturé.

Le scroll_interval maximum est de 60 secondes : au-delà de 60s, le défilement s'arrête et la réponse est renvoyée. Lorsque vous définissez scroll_interval=60, gardez la connexion côté client ouverte pendant au moins 90 secondes pour que la réponse ait le temps de revenir. Combiner scroll avec page_wait augmente le temps total de traitement et donc le nombre de requêtes facturées.

Le paramètre css_click_selector ne prend effet que lorsque vous utilisez le JavaScript token (il s'exécute à l'intérieur du navigateur headless avant la capture du DOM). Il accepte tout sélecteur CSS valide et entièrement spécifié : par exemple un ID comme #some-button, une classe comme .some-other-button, ou un sélecteur d'attribut comme [data-tab-item="tab1"]. Encodez toujours la valeur en URL pour que les caractères spéciaux survivent intacts à la query string.

Si le sélecteur n'est pas trouvé sur la page, la requête échoue avec pc_status 595. Pour recevoir tout de même une réponse lorsque la cible du clic peut être absente, ajoutez un sélecteur universellement présent en repli, séparé par une virgule. Par exemple #some-button,body retombe sur un clic sur body lorsque #some-button n'existe pas.

Sélecteurs multiples. Pour cliquer sur plusieurs éléments en séquence avant la capture, séparez-les par une barre verticale (|). URL-encodez la valeur entière, y compris la barre. Par exemple, cliquer sur #start-button puis sur .next-page-link ressemble à #start-button|.next-page-link en forme brute, ou %23start-button%7C.next-page-link URL-encodé. Les clics ont lieu dans l'ordre indiqué. Si un sélecteur de la chaîne est absent, la même règle de pc_status 595 s'applique, donc le pattern de fallback ,body fonctionne par sélecteur.

Besoin d'exécuter du JavaScript personnalisé dans la page avant que Crawlbase ne capture le DOM (par ex. dispatcher un événement synthétique, modifier un état, forcer un fetch) ? C'est une fonctionnalité activable au cas par cas selon votre usage : contactez le support en décrivant ce que vous cherchez à faire et nous le mettrons en place.

Async & stockage

Le mode async fait passer l'API de « bloque jusqu'à ce que ma page soit prête » à « mets ça en file et préviens-moi quand c'est terminé ». Le point de terminaison retourne immédiatement un rid ; le résultat réel est livré à un webhook que vous spécifiez, ou stocké dans Cloud Storage et récupéré plus tard via le même rid. C'est le bon mode pour les jobs par lots et les cibles lentes : l'async libère votre créneau de concurrence dès que la requête est mise en file, vous pouvez donc continuer à soumettre pendant que les crawls tournent encore. Pour les jobs à très haut volume (millions d'URLs), utilisez l'Enterprise Crawler qui s'appuie sur ce même pipeline async avec retries, gestion du rythme et livraison des résultats.

Le mode async est actuellement réservé à linkedin.com

Le flag async=true n'est actuellement pris en charge que pour les URLs linkedin.com. Si vous avez besoin de crawls async sur d'autres domaines, contactez le support en indiquant le domaine cible afin que nous l'activions pour votre token.

async
booleanfalse
Renvoie immédiatement un rid au lieu de bloquer. Le résultat est livré au callback s'il est défini, ou disponible via Cloud Storage par rid.
callback
URLoptionnel
URL du webhook qui recevra le résultat du crawl. Requis quand async=true si vous ne voulez pas faire de polling.
store
booleanfalse
Persiste la page crawlée dans Cloud Storage. Renvoie un rid en plus du corps.

Format de sortie

La réponse par défaut est le corps brut de la page : exactement ce qu'un navigateur recevrait après rendu et résolution anti-bot. Pour la plupart des pipelines, c'est la bonne forme (votre parseur en aval gère directement le HTML). Utilisez format=json lorsque vous voulez les métadonnées (statut, URL finale, RID, en-têtes) regroupées dans une seule enveloppe plutôt que réparties entre les en-têtes de réponse et le corps. Utilisez scraper= ou autoparse=true lorsque la cible est l'une de celles pour lesquelles nous avons déjà un parseur : vous sautez complètement l'étape d'analyse et recevez des champs structurés propres au lieu du balisage brut.

format
html | json | mdhtml
Choisissez l'enveloppe de réponse. html retourne la page brute avec les métadonnées dans les en-têtes de réponse. json emballe la page plus toutes les métadonnées dans un seul objet JSON. md convertit la page en GitHub-Flavored Markdown : associez-le à md_readability=true pour retirer d'abord la navigation, la barre latérale et les pubs.
md_readability
booleanfalse
N'a de sens qu'avec format=md. Lorsque la valeur est true, Crawlbase effectue une passe de lisibilité sur la page avant la conversion en Markdown : supprime les éléments de chrome (navigation, barre latérale, pied de page, emplacements publicitaires) et conserve le contenu principal de l'article. Idéal pour convertir des billets de blog et des articles en contexte LLM propre.
pretty
booleanfalse
N'a de sens qu'avec format=json. Pretty-print l'enveloppe JSON avec indentation et retours à la ligne pour la lecture humaine ; à laisser désactivé en production pour garder des réponses compactes.
scraper
stringoptionnel
Applique un scraper intégré pour extraire des données structurées au lieu de renvoyer du HTML. Exemple : amazon-product-details.
autoparse
booleanfalse
Détecte automatiquement le type de page et applique le scraper correspondant. Pratique pour « donne-moi du JSON quand tu peux ».

Contrôle de la réponse

Ces paramètres modifient ce que contient la réponse ou la façon dont Crawlbase décide qu'une requête a réussi. Utilisez get_headers et get_cookies lorsque vous avez besoin de récupérer les en-têtes de réponse du site cible ou les valeurs Set-Cookie (ils sont supprimés par défaut). Utilisez custom_success_codes lorsque la cible retourne légitimement un statut non-2xx que votre pipeline doit traiter comme une récupération propre : sans cela, Crawlbase réessaiera ces réponses à votre place.

get_headers
booleanfalse
Fait remonter les en-têtes de réponse du site cible. Ils reviennent préfixés en tant qu'en-têtes de réponse original_header_*, ou regroupés sous original_headers quand format=json.
get_cookies
booleanfalse
Fait remonter les valeurs Set-Cookie du site cible. Elles reviennent en tant que original_set_cookie dans les en-têtes de réponse, ou sous la même clé quand format=json.
custom_success_codes
stringoptionnel
Liste séparée par des virgules de codes de statut HTTP à traiter comme réussis, par ex. custom_success_codes=403,429,503. Crawlbase ne réessaiera pas ces requêtes, et le statut d'origine est conservé dans original_status. À utiliser lorsque la cible retourne légitimement ces codes pour votre point de terminaison (API protégées par authentification, pages bloquées géographiquement dont vous voulez quand même le corps).

Requêtes POST

Utilisez POST lorsque le point de terminaison cible attend un corps de requête : soumissions de formulaire, API JSON, GraphQL, tout ce qui ne tient pas dans une query string. Même point de terminaison, mêmes paramètres, même forme de réponse que GET ; seuls la méthode HTTP et le corps changent.

POST fonctionne uniquement avec le Normal token

Les requêtes POST fonctionnent uniquement avec le Normal token. Le JavaScript token (et les paramètres de rendu JS page_wait, ajax_wait, scroll, css_click_selector) sont réservés à GET : lorsque vous devez soumettre un formulaire sur une page rendue en JS, utilisez le JavaScript token avec css_click_selector pour piloter le bouton du formulaire plutôt que de POSTer directement sur l'URL du formulaire.

Le Content-Type par défaut est application/x-www-form-urlencoded. Passez les champs du formulaire comme corps de la requête : Crawlbase les transmet à la cible inchangés.

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))
}
N'en abusez pas

POST ne peut pas être utilisé pour spammer ni nuire de quelque manière que ce soit aux sites cibles. Crawlbase surveille activement les schémas abusifs ; les comptes pris à utiliser POST pour du spam, du credential stuffing ou tout autre trafic malveillant seront suspendus et signalés.

POST avec un corps JSON

Remplacez le Content-Type form-urlencoded par défaut avec post_content_type. URL-encodez la valeur (par ex. application/json devient application%2Fjson). Le corps est transmis à la cible inchangé : encodez-le en JSON vous-même.

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

Note : c'est le site cible qui décide d'accepter ou non le corps. Crawlbase transmet la requête honnêtement : si la cible retourne 4xx parce que la forme du corps est incorrecte, cela apparaît dans original_status, pas dans pc_status. Voir Errors pour le schéma de branchement.

Requêtes PUT

PUT fonctionne de la même façon que POST : même point de terminaison, mêmes paramètres, mêmes règles d'encodage du corps. La seule différence est la méthode HTTP.

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

Comme POST, PUT nécessite le Normal token. Utilisez post_content_type pour contrôler le type de média du corps s'il n'est pas en form-urlencoded.

N'utilisez pas POST/PUT pour spammer

Crawlbase surveille activement le trafic POST et PUT. Envoyer des corps de requête ciblant des sites tiers qui ne vous appartiennent pas - spam de commentaires, soumissions de formulaire frauduleuses, création de comptes scriptée - entraîne la suspension du compte d'origine dès la première détection. Utilisez ces verbes pour des intégrations API légitimes, vos propres environnements de staging et de production, ainsi que l'automatisation explicitement autorisée.

Réponse

Les réponses réussies retournent la page cible dans le corps. Les métadonnées se trouvent dans les en-têtes de réponse.

En-têtes

En-têteDescription
pc_statusCode de statut Crawlbase. 200 = succès.
original_statusStatut HTTP du site cible.
urlURL finale après redirections.
ridID de requête. Retourné quand async=true ou store=true.
content-typeType MIME du corps (text/html, application/json, image/png, etc).
original_header_*Retourné quand get_headers=true. Chaque en-tête du site cible arrive avec un préfixe original_header_ (par exemple original_header_x_frame_options). Regroupés sous original_headers quand format=json.
screenshot_urlRetourné quand screenshot=true. URL JPEG temporaire de la page rendue ; expire une heure après le crawl.
original_set_cookieRetourné quand get_cookies=true. Valeurs Set-Cookie concaténées issues de la réponse du site cible.
domain_complexity
également X-Domain-Complexity
Le niveau de complexité du domaine crawlé : l'une des valeurs standard, moderate ou complex. Reflète les ressources nécessaires pour contourner les protections du site et correspond directement au niveau tarifaire facturé pour la requête. Voir les niveaux de complexité ci-dessous.
storage_urlRetourné quand la requête a été faite avec store=true. Pointeur vers la copie stockée de la réponse dans Crawlbase Cloud Storage ; à associer avec rid pour la récupérer plus tard.
Content-Typetext/markdown; charset=utf-8 quand la requête a été faite avec format=md ; text/html ou application/json standard sinon.
X-Markdown-FlavorDialecte Markdown du corps de réponse : actuellement GitHub Flavored Markdown (GFM). Émis uniquement lorsque format=md.
X-Markdown-FeaturesListe séparée par des virgules des fonctionnalités GFM utilisées dans le corps (par exemple tables,lists). Vous permet de choisir un parser avec les bonnes extensions activées. Émis uniquement quand format=md.
X-Markdown-Base-URLHôte de l'URL résolue (après d'éventuelles redirections). Utile pour résoudre les liens relatifs dans le corps markdown. Émis uniquement quand format=md.
X-Markdown-GeneratorIdentifie le convertisseur : la valeur est ProxyCrawl-API. Émis uniquement lorsque format=md.

Réponse HTML

Le comportement par défaut. format=html (ou aucun format du tout) retourne le corps brut de la page dans le corps HTTP, avec les métadonnées dans les en-têtes de réponse (url, original_status, pc_status, X-Domain-Complexity, plus toutes les entrées original_header_* auxquelles vous avez souscrit via get_headers=true).

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>

Réponse JSON

Définissez format=json pour récupérer les mêmes données sous forme d'un seul objet JSON :

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

Réponse Markdown

format=md retourne la page déjà convertie en GitHub Flavored Markdown dans le corps, avec Content-Type: text/markdown; charset=utf-8 et un bloc d'en-têtes de métadonnées X-Markdown-* (Flavor, Features, Base-URL, Generator) à côté des habituels url / original_status / pc_status. Associez-le à md_readability=true lorsque vous voulez une extraction du contenu principal (corps de l'article, sans chrome) avant la conversion : voir le paramètre 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) ...

Requêtes facturables

Crawlbase ne facture que les requêtes où pc_status est 200 etoriginal_status fait partie de :

CodeSignification
200OK
201Créé
204Pas de contenu
301Déplacé définitivement
302Found - uniquement lorsque la redirection a été suivie et a retourné du contenu
404Introuvable
410Disparu

Tout autre original_status est gratuit, et il en va de même pour tout pc_status autre que 200. Utilisez cette liste lors du rapprochement d'une facture d'utilisation avec les logs de votre application.

Niveaux de complexité de domaine

Le champ domain_complexity (également retourné sous forme d'en-tête de réponse X-Domain-Complexity) vous indique la difficulté du crawl sur le domaine cible, et le niveau tarifaire dans lequel la requête est tombée.

  • standard : facile à crawler, protection minimale. Niveau tarifaire le plus bas.
  • moderate : protection anti-bot modérée nécessitant un traitement spécialisé. Niveau tarifaire intermédiaire.
  • complex : protection avancée nécessitant des ressources spécialisées. Niveau tarifaire le plus élevé.

Pour la tarification spécifique à chaque palier, consultez votre plan d'abonnement ou contactez le service commercial.

Modèles courants

Recettes de bout en bout qui combinent les paramètres de requête ci-dessus dans les workflows que les équipes utilisent le plus souvent.

SPA rendue en JS avec défilement

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

Requête routée géographiquement

# Route through Germany; the echoed JSON confirms the exit country
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://ipinfo.io/json' \
  --data-urlencode 'country=DE' -G
from crawlbase import CrawlingAPI

api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://ipinfo.io/json', {'country': 'DE'})
print(res['body'])
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });

const res = await api.get('https://ipinfo.io/json', { country: 'DE' });
console.log(res.body);
require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://ipinfo.io/json', country: 'DE')
puts res.body
<?php
use Crawlbase\CrawlingAPI;

$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://ipinfo.io/json', ['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://ipinfo.io/json", map[string]string{
        "country": "DE",
    })
    fmt.Println(res.Body)
}

Crawl asynchrone avec webhook

Quand utiliser le mode async

Le mode async libère votre slot de concurrence dès que la requête est mise en file d'attente, ce qui évite qu'un long crawl monopolise votre budget. Utilisez-le pour les cibles lentes (JS lourd, page_wait long) lorsque vous devez traiter un volume élevé.

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
}

Mode proxy

Le même Crawling API peut être invoqué en tant que proxy HTTP/HTTPS plutôt qu'en tant que point de terminaison REST : utile lorsque vous avez un scraper existant, un script d'automatisation de navigateur ou un client HTTP qui prend déjà en charge la configuration de proxy et que vous préférez glisser Crawlbase devant plutôt que de réécrire la couche de requête.

Pointez votre client vers smartproxy.crawlbase.com:8001 (HTTPS, recommandé) ou smartproxy.crawlbase.com:8000 (HTTP) et passez votre token comme nom d'utilisateur du proxy. Toutes les fonctionnalités du Crawling API - rendu JS, contournement anti-bot, routage géographique - s'appliquent à l'identique ; seule la forme de la requête diffère.

Mode proxy vs. Smart AI Proxy

Deux produits partagent le même nom d'hôte mais utilisent des ports différents : facile à confondre. Les capacités sont essentiellement les mêmes des deux côtés (routage par pays, émulation d'appareil, sessions, en-têtes personnalisés, rendu JS via les contrôles CrawlbaseAPI-*) ; ils diffèrent par l'abonnement sur lequel vous êtes facturé et le niveau de concurrence / threads fourni par cet abonnement :

  • Crawling API en mode proxy (cette section) → ports 8000 / 8001. Passe par votre forfait Crawling API : même quota mensuel, même budget de concurrence, même facturation par succès que les appels en mode REST. Choisissez cette option si vous payez déjà pour le Crawling API et souhaitez disposer d'une interface de type proxy en complément du point de terminaison REST.
  • Smart AI Proxy (produit séparé, voir Smart AI Proxy) → ports 8012 / 8013. Un SKU distinct avec son propre abonnement et son propre modèle de threads / concurrence, dimensionné pour les pipelines de scraping proxy-first qui font déjà tourner un grand nombre de threads. Même réseau et mêmes en-têtes de contrôle en sous-couche : le choix porte sur le contrat et le profil de concurrence qui correspondent à votre usage.

Règle générale : choisissez le produit dont vous détenez déjà l'abonnement (ou dont le modèle tarifaire correspond à la forme de votre trafic). La surface fonctionnelle est la même ; les ports vous orientent simplement vers la bonne voie de facturation et de concurrence.

Démarrage rapide

Un premier appel depuis votre shell : Normal token, proxy HTTPS :

# 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))
}

Pour les cibles rendues en JS, utilisez votre 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)
}

Limites de débit

La limite de débit par défaut en mode proxy est de 20 requêtes par seconde (~1,7 M req/jour). Les clients basés sur la concurrence devraient raisonner en threads plutôt qu'en RPS : à la latence typique du Crawling API (~4 s pour une page produit Amazon), cela représente environ 80 threads concurrents. Les cibles plus rapides se traduisent par moins de threads.

Si vous atteignez le plafond, contactez le support en décrivant votre cas d'usage pour négocier une concurrence plus élevée.

Erreurs & tentatives

Le Crawling API expose deux codes de statut sur chaque réponse : original_status (ce que le site cible a retourné) et pc_status (ce que Crawlbase en a fait après application des règles anti-bot, de redirection et de validation du contenu). Ils peuvent diverger : une cible peut retourner 200 avec un corps vide, auquel cas original_status vaut 200 mais pc_status vaut 520. Branchez-vous toujours sur pc_status pour décider de réessayer ou non.

Les échecs spécifiques au Crawling API les plus fréquents :

CodeSignificationAction
422url manquant ou non URL-encodéEncodez l'URL avant l'envoi. La plupart des clients (libcurl --data-urlencode, Python requests, Node fetch) le font automatiquement : mais les query strings construites à la main omettent souvent cette étape.
520Réponse vide de la cibleRéessayez une fois. Si toujours vide, passez du Normal au JS token : de nombreux sites servent une coquille vide aux user-agents non-navigateur et s'appuient sur JS pour remplir la page.
521Site cible hors service / inaccessibleTraitez-le comme une erreur amont transitoire. Backoff + nouvelle tentative ; si cela persiste sur plusieurs minutes, le site est réellement hors service.
522Délai de connexion dépassé en atteignant la cibleRéessayez avec backoff. Essayez un autre country si la cible est instable selon la zone géographique.
523Origine inaccessible depuis la sortie choisieRéessayez sans country (laissez le routage automatique choisir) ou avec un autre pays.
525Le défi anti-bot n'a pas pu être résoluPassez du Normal au JS token. Si vous êtes déjà sur JS, réessayez ; si persistant, escaladez au support : cela signifie généralement que la cible a déployé une nouvelle variante de défi.
595Sélecteur introuvable. La page s'est chargée correctement mais le sélecteur CSS passé via css_click_selector n'a correspondu à aucun élément.Ajoutez un repli au sélecteur (#start-button,body) pour que le clic atteigne tout de même un élément connu. Voir les notes sur css_click_selector pour le modèle complet.
599Erreur interne CrawlbaseRéessayez. Si une requête rencontre cette erreur de manière constante, contactez le support avec le rid.

La référence complète HTTP + pc_status se trouve dans Codes de statut ; Gestion des erreurs couvre la boucle recommandée de retry-with-backoff et les helpers de SDK qui l'implémentent pour vous dans chaque langage.

Exemple d'ancrage. La raison la plus fréquente pour laquelle pc_status diverge de original_status est un CAPTCHA : le site cible renvoie un 200 (la page captcha s'est rendue correctement) mais Crawlbase reconnaît la réponse comme une page d'interstitiel et expose pc_status: 503 afin que vous puissiez la contourner plutôt que de traiter le HTML du captcha comme vos données.

Codes pc_status non standard. Les codes hors de la plage HTTP habituelle - 601, 999 et similaires - sont des marqueurs internes utilisés par l'équipe d'ingénierie Crawlbase. Ils n'apparaissent dans la réponse que pour vous aider à débuguer lorsque vous contactez le support ; vous n'avez pas besoin de les gérer dans votre code applicatif.

Stratégie de tentatives

La version simple : réessayez les erreurs transitoires (5xx) avec un backoff exponentiel jusqu'à un plafond (typiquement 3 à 5 tentatives), ne réessayez pas les erreurs client (4xx : elles ne se corrigeront pas toutes seules), et changez de type de token une seule fois sur le premier 520/525 avant de poursuivre les nouvelles tentatives. Les helpers des SDK implémentent cette boucle avec des valeurs par défaut raisonnables ; pour un client personnalisé, la règle empirique est :

  • Première tentative : ~1 s après l'échec
  • Deuxième tentative : ~3 s après l'échec
  • Troisième tentative : ~10 s après l'échec
  • Au-delà : journaliser + alerter ; les échecs persistants signalent généralement un changement côté cible plutôt qu'un problème réseau transitoire

Toutes les nouvelles tentatives contre cette API sont gratuites : seules les réponses réussies (pc_status: 200) sont décomptées de votre quota. Cela rend le backoff agressif peu coûteux ; le seul vrai coût des retries est la latence ajoutée à votre pipeline.

Performance & bonnes pratiques

Quelques modèles reviennent chez les clients qui exploitent cette API à grande échelle. Les adopter d'emblée évite les catégories de tickets de support les plus fréquentes.

  • Utilisez le token le moins cher qui fonctionne. Ne basculez pas sur le JavaScript token « au cas où » : les requêtes avec le Normal token sont plus rapides et utilisent moins de concurrence. Passez au JS uniquement lorsque la réponse Normal est vide ou bloquée par un défi.
  • Préférez ajax_wait à page_wait. Les délais fixes consomment de la concurrence sur chaque requête, même les rapides. ajax_wait retourne dès que la page passe en network-idle : typiquement plus rapide en moyenne et plus lent uniquement sur les pages dont le chargement traîne réellement.
  • Faites passer les volumes élevés par async + webhook. Le mode synchrone est le bon choix par défaut pour un usage ponctuel et interactif. Pour les traitements par lot dépassant quelques centaines d'URLs, le mode async (ou l'Enterprise Crawler) garde votre budget de concurrence libre pour de nouvelles soumissions pendant que les crawls en cours se terminent.
  • Réutilisez les sessions pour les flux avec état. Si votre cible nécessite une session connectée ou des cookies de panier, conservez un identifiant de session et transmettez-le sur les requêtes suivantes afin de réutiliser la même IP de sortie et le même cookie jar. Voir Authentication pour le modèle de cookie de session.
  • Surveillez l'en-tête remaining. Réduisez la cadence avant d'atteindre votre plafond de concurrence plutôt que de le découvrir via des 429 : la réponse indique le nombre de slots restants, donc un client en bonne santé temporise de manière proactive au lieu de réagir aux erreurs.