Sans KYC — pas de pièce d'identité, pas de selfie, pas de documents. Payez en crypto : BTC, USDT, USDC, ETH, SOL, LTC
Langue: Français
Obtenir une clé API

Docs · jobs

API batch et crawl : lire de nombreuses URLs ou un site entier

Deux endpoints lancent des jobs en arrière-plan : /batch pour une liste de 100 URLs maximum, /crawl pour un site web. Les deux renvoient un job ID ; vous récupérez les pages depuis /jobs/{job_id}, une page de résultats à la fois.

Dernière mise à jour: · 8 min de lecture

Réponse rapide

Comment crawler un site web vers du Markdown avec WebReaderAPI ?

Envoyez l'URL de départ à POST https://webreaderapi.com/api/v1/crawl avec une page limit. WebReaderAPI lit le site en arrière-plan, même domaine uniquement, en suivant son sitemap, robots.txt et Crawl-delay, jusqu'à 10 000 pages par job. Récupérez les pages Markdown du job, une page de résultats à la fois. Chaque page lue coûte 1 crédit (2 avec un navigateur), à partir de 0,07 $US/1K pages depuis octobre 2026. Sans KYC ; payez en crypto.

Les jobs en un coup d'œil

BatchJusqu'à 100 URLs par job batch : POST https://webreaderapi.com/api/v1/batch
CrawlCrawl d'un seul site web par job, jusqu'à 10 000 pages, en suivant son sitemap, son robots.txt et son Crawl-delay : POST https://webreaderapi.com/api/v1/crawl
RésultatsGET https://webreaderapi.com/api/v1/jobs/{job_id}, puis ?cursor=… pour la page de résultats suivante
FacturationChaque page lue est facturée comme un appel /read ; les pages ignorées par robots.txt et les pages en échec ne coûtent rien
Périmètre d'un crawlUn seul domaine ; les liens vers d'autres domaines sont listés mais non lus
Annuler un jobNon disponible en v1 : réglez limit sur le nombre maximum de pages que vous êtes prêt à payer

Lancer un job batch

POST/api/v1/batch

Lit jusqu'à 100 URLs en arrière-plan avec les mêmes options. Les options utilisent les noms et valeurs de /read. La réponse est le job ID ; les valeurs sont illustratives.

Authentification : clé API dans le header Authorization: Bearer API_KEY.

Corps de la requête

ChampTypeObligatoireDescription
urlsarray of stringsOuiDe 1 à 100 URLs http ou https. Les doublons ne sont lus qu'une fois.
optionsobjectNonTout paramètre /read sauf url : format, main_content, render, include_links, include_images, country, max_chars, timeout.
Exemple: three articles as Markdown
{
  "urls": [
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2",
    "https://example.org/news/launch"
  ],
  "options": {
    "format": "markdown",
    "max_chars": 50000
  }
}

Réponse 202 Accepted

{
  "job_id": "job_5c1e8a90d2"
}

Exemples de code

curl
curl -s "https://webreaderapi.com/api/v1/batch" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}}'
Python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/batch",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"urls": ["https://example.com/blog/post-1", "https://example.com/blog/post-2"], "options": {"format": "markdown"}},
    timeout=30,
)
print(resp.json()["job_id"])
Node.js
const res = await fetch('https://webreaderapi.com/api/v1/batch', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ urls: ['https://example.com/blog/post-1', 'https://example.com/blog/post-2'], options: { format: 'markdown' } }),
});
console.log((await res.json()).job_id);

Démarrer un job de crawl

POST/api/v1/crawl

Lit un site web à partir d'une URL de départ, en largeur d'abord. Le crawler reste sur le domaine de l'URL de départ, utilise le sitemap indiqué dans robots.txt ou trouvé à /sitemap.xml, et respecte robots.txt et Crawl-delay. Les valeurs sont illustratives.

Authentification : clé API dans le header Authorization: Bearer API_KEY.

Corps de la requête

ChampTypeObligatoireDescription
urlstringOuiLa page de départ, comme la page d'accueil d'un site de documentation.
limitintegerNonNombre maximal de pages à lire, jusqu'à 10 000. Le job s'arrête lorsqu'il atteint ce nombre.
max_depthintegerNonNombre maximal de sauts de lien depuis l'URL de départ.
include_pathsarray of stringsNonMotifs de chemin simples à conserver, comme /docs/*.
exclude_pathsarray of stringsNonMotifs de chemin simples à ignorer, comme /blog/tag/*.
optionsobjectNonTout paramètre /read sauf url, appliqué à chaque page.
Exemple: the docs section of a site, 500 pages at most
{
  "url": "https://docs.example.com/",
  "limit": 500,
  "max_depth": 3,
  "include_paths": [
    "/docs/*"
  ],
  "exclude_paths": [
    "/docs/archive/*"
  ],
  "options": {
    "format": "markdown"
  }
}

Réponse 202 Accepted

{
  "job_id": "job_8b27f41c0e"
}

Exemples de code

curl
curl -s "https://webreaderapi.com/api/v1/crawl" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]}'
Python
import os
import requests

resp = requests.post(
    "https://webreaderapi.com/api/v1/crawl",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    json={"url": "https://docs.example.com/", "limit": 500, "include_paths": ["/docs/*"]},
    timeout=30,
)
print(resp.json()["job_id"])
Node.js
const res = await fetch('https://webreaderapi.com/api/v1/crawl', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://docs.example.com/', limit: 500, include_paths: ['/docs/*'] }),
});
console.log((await res.json()).job_id);

Obtenir le statut et les résultats d'un job

GET/api/v1/jobs/{job_id}

Renvoie le statut du job et les prochains résultats prêts. Chaque élément de results est soit une réponse /read complète, soit {url, error} pour une page qui n'a pas pu être lue. Quand next_cursor est présent, rappelez avec cursor défini à cette valeur ; quand il est absent, aucun autre résultat n'est encore prêt. Les valeurs sont illustratives.

Authentification : clé API dans le header Authorization: Bearer API_KEY.

Paramètres

NomEmplacementTypeObligatoireDescription
job_idpathstringOuiL'ID renvoyé par /batch ou /crawl.
cursorquerystringNonLa valeur next_cursor de l'appel précédent, pour obtenir les résultats suivants.

Réponse 200

{
  "job_id": "job_8b27f41c0e",
  "status": "running",
  "results": [
    {
      "url": "https://docs.example.com/docs/install",
      "final_url": "https://docs.example.com/docs/install",
      "status_code": 200,
      "rendered": false,
      "credits_used": 1,
      "title": "Install",
      "markdown": "# Install\n\nRun the installer...",
      "word_count": 418
    },
    {
      "url": "https://docs.example.com/docs/private",
      "error": {
        "code": "blocked_by_robots",
        "message": "robots.txt disallows this page for WebReaderAPI."
      }
    }
  ],
  "next_cursor": "c_000002",
  "credits_used": 1
}

Exemples de code

curl
curl -sG "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e" \
  -H "Authorization: Bearer $WEBREADER_API_KEY" \
  -d cursor=c_000002
Python
import os
import requests

resp = requests.get(
    "https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e",
    headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
    params={"cursor": "c_000002"},
    timeout=30,
)
job = resp.json()
print(job["status"], len(job["results"]), job.get("next_cursor"))
Node.js
const res = await fetch('https://webreaderapi.com/api/v1/jobs/job_8b27f41c0e?cursor=c_000002', {
  headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
});
const job = await res.json();
console.log(job.status, job.results.length, job.next_cursor);

Comment attendre la fin d'un job ?

Interrogez /jobs/{job_id} et conservez le curseur. Traitez les résultats reçus, puis rappelez avec cursor défini à next_cursor. Quand next_cursor est absent et que status vaut done, vous avez tout ; quand il est absent et que le job est encore running, attendez et rappelez avec le même curseur.

Commencez à interroger toutes les 2 secondes et doublez l'attente jusqu'à 30 secondes tant que rien de nouveau n'arrive. Un crawl qui respecte le Crawl-delay d'un site peut prendre du temps : une longue attente est normale et ne coûte rien.

Python : interroger un job et enregistrer chaque page dans un fichier .md
import os
import pathlib
import re
import time

import requests

JOBS = "https://webreaderapi.com/api/v1/jobs"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}


def save_job(job_id, folder="pages"):
    out = pathlib.Path(folder)
    out.mkdir(exist_ok=True)
    cursor, wait, saved = None, 2, 0
    while True:
        params = {"cursor": cursor} if cursor else {}
        job = requests.get(f"{JOBS}/{job_id}", headers=HEADERS, params=params, timeout=30).json()
        for item in job["results"]:
            if "error" in item:
                print("skipped", item["url"], item["error"]["code"])
                continue
            name = re.sub(r"[^a-z0-9]+", "-", item["final_url"].lower()).strip("-")[:120]
            (out / f"{name}.md").write_text(item["markdown"], encoding="utf-8")
            saved += 1
        if job.get("next_cursor"):
            cursor, wait = job["next_cursor"], 2
            continue
        if job["status"] == "done":
            return saved
        time.sleep(wait)
        wait = min(wait * 2, 30)


if __name__ == "__main__":
    print(save_job("job_8b27f41c0e"), "pages saved")

Quelles pages un crawl lit-il ?

Un crawl démarre à votre URL et progresse vers l'extérieur, un saut de lien à la fois, en largeur d'abord, jusqu'à max_depth. Il lit aussi le sitemap du site, à l'adresse indiquée dans robots.txt ou à /sitemap.xml, pour trouver les pages vers lesquelles peu de liens pointent.

  • Même domaine uniquement. Les liens vers d'autres domaines sont listés dans les résultats quand vous demandez include_links, mais ils ne sont pas lus.
  • Filtres de chemin. include_paths et exclude_paths acceptent des motifs simples comme /blog/*.
  • Une copie par page. Quand un site déclare une URL canonique, une page atteinte via plusieurs adresses n'est lue qu'une fois.
  • Un arrêt net. Le job se termine quand il a lu limit pages.

Comment un crawl respecte-t-il le site ?

Chaque requête porte le User-Agent WebReaderAPI/1.0 (+https://webreaderapi.com/bot/), et le crawler suit les règles robots.txt pour WebReaderAPI, ou pour tous les robots quand aucun ne le nomme. Les pages interdites sont ignorées et apparaissent dans results avec l'erreur blocked_by_robots ; elles ne coûtent rien.

Quand robots.txt définit un Crawl-delay, le crawler attend ce délai entre deux requêtes vers le site. Si un site déclare Content-Signal: ai-input=no dans son robots.txt, nous ne lisons pas ses pages pour vous. Si un site refuse notre requête, nous renvoyons sa réponse : nous ne résolvons jamais de CAPTCHA, ne nous connectons pas à des comptes et ne cherchons pas à contourner un blocage ou un paywall.

Combien coûte un crawl ?

Chaque page lue coûte ce que coûterait un appel /read : 1 crédit, 2 si la page a nécessité un navigateur, plus 10 avec country. Les pages ignorées et en échec sont gratuites. Le champ credits_used du job totalise ce qui a été facturé jusqu'ici.

Un site de documentation de 500 pages lu sans navigateur utilise environ 500 crédits, que le pack 10 000 pages (3,75 $US) couvre largement. Comme un job ne peut pas être annulé en v1, limit est votre plafond de dépense : fixez-le au nombre maximal de pages que vous êtes prêt à payer.

Batch et crawl : questions fréquentes

Faut-il utiliser /batch ou plusieurs appels /read ?

Utilisez /batch quand vous avez déjà les URLs et n'avez pas besoin de chaque réponse immédiatement. Cela vous évite de gérer la concurrence et les tentatives. Utilisez /read quand un agent a besoin d'une page tout de suite.

Combien de temps les résultats d'un job sont-ils conservés ?

Récupérez-les dès qu'ils sont prêts. Les résultats attendent que vous les récupériez ; une fois livrés, aucune copie du contenu de la page n'est conservée, stockez donc ce dont vous avez besoin de votre côté.

Un crawl peut-il suivre des liens vers d'autres sites web ?

Non. Un crawl lit un seul domaine. Les autres domaines apparaissent comme liens quand vous les demandez, et vous pouvez envoyer ces URLs à /batch si vous voulez qu'elles soient lues.

Puis-je arrêter un crawl déjà démarré ?

Pas en v1. Le job s'arrête à limit pages, choisissez donc une limite adaptée à votre budget. Les pages déjà lues restent facturées ; les pages jamais lues ne coûtent rien.

À partir de 0,07 $US/1K pages · sans KYC

Obtenir une clé API