# SERP API en Python : requests, httpx et asyncio

> API SERP SerpCrypto en Python : client requests avec retries, httpx async avec limite de concurrence, pagination et export CSV. Code testé.

- URL: https://serpcrypto.com/fr/docs/python/
- Dernière mise à jour: 2026-10-22
- Éditeur: SerpCrypto (https://serpcrypto.com)

Un client réutilisable avec retries, une boucle de pagination qui s'arrête quand les résultats se terminent, des recherches async plafonnées à votre limite de concurrence, et un CSV quotidien pour les classements. Du code à copier-coller, vérifié syntaxiquement, sans aucun package de notre part à installer.

**Réponse rapide — Comment appeler une API SERP depuis Python ?**

Envoyez une requête GET à `https://serpcrypto.com/api/v1/search` avec `requests` ou `httpx`, votre clé dans l'en-tête `Authorization: Bearer` et la requête dans `params`, puis lisez `response.json()`. **SerpCrypto** facture 1 crédit par recherche réussie, à partir de 0,30 $US/1K requêtes, et les retries des appels échoués sont gratuits. Payez en crypto, sans KYC. Le code ci-dessous ne nécessite que `requests` ou `httpx`.

## Configuration Python en un coup d'œil

| Attribut | Valeur |
| --- | --- |
| Installation | `pip install requests` ou `pip install httpx` |
| Clé | Variable d'environnement `SERPCRYPTO_API_KEY` |
| En-tête | `Authorization: Bearer <key>` |
| Délai d'expiration | 60 à 90 secondes : une recherche s'exécute en direct |
| Retry | 429, 502 et 504 uniquement, avec backoff ; les erreurs sont gratuites |
| Recherches en parallèle | Jusqu'à `concurrency_limit` depuis `GET /credits` |

## Comment configurer un client Python réutilisable ?

Créez un `requests.Session` avec la clé dans ses en-têtes et une politique de retry pour les trois erreurs temporaires. Une session réutilise les connexions, donc une boucle de recherches passe moins de temps sur les handshakes TLS. Enregistrez le fichier sous `serp_client.py` ; les exemples suivants l'importent.

`Retry` de urllib3 attend plus longtemps avant chaque nouvelle tentative et respecte un en-tête `Retry-After` s'il en est envoyé. Il ne réessaie jamais `400`, `401` ou `402`, qui demandent une correction, pas une seconde tentative.

*serp_client.py*

```python
"""serp_client.py: a small, reusable client for the SerpCrypto SERP API (requests)."""
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

API_URL = "https://serpcrypto.com/api/v1/search"
CREDITS_URL = "https://serpcrypto.com/api/v1/credits"


def make_session():
    session = requests.Session()
    session.headers["Authorization"] = "Bearer " + os.environ["SERPCRYPTO_API_KEY"]
    retry = Retry(
        total=4,
        backoff_factor=1.0,  # waits about 1 s, 2 s, 4 s between attempts
        status_forcelist=(429, 502, 504),
        allowed_methods=("GET",),
        respect_retry_after_header=True,
    )
    session.mount("https://", HTTPAdapter(max_retries=retry))
    return session


SESSION = make_session()


def search(q, **params):
    """Run one search (1 credit if it succeeds) and return the parsed JSON."""
    r = SESSION.get(API_URL, params={"q": q, **params}, timeout=90)
    r.raise_for_status()
    return r.json()


def credits():
    """Free: credits left, used today and the concurrency limit."""
    r = SESSION.get(CREDITS_URL, timeout=30)
    r.raise_for_status()
    return r.json()


if __name__ == "__main__":
    data = search("project management software", gl="us", hl="en")
    for item in data.get("organic_results", []):
        print(item["position"], item["link"])
    print(credits())
```

## Comment récupérer plus de 10 résultats ?

Demandez les pages suivantes une par une. Environ 10 résultats organiques par page, comme Google les sert depuis qu'il n'honore plus num=100 en septembre 2025 ; chaque page supplémentaire coûte un crédit de plus. Arrêtez-vous dès que `pagination.next_page` est absent : il n'y a plus rien à récupérer, et vous gardez les crédits.

Pour la plupart des travaux de suivi de classement, la première page ou les trois premières pages suffisent, car les clics s'y concentrent. Trente résultats pour un mot-clé coûtent trois crédits.

*Pagination : jusqu'à trois pages, arrêt anticipé*

```python
from serp_client import search


def top_results(q, pages=3, **params):
    """Collect organic results over several pages: each page is 1 credit."""
    rows = []
    for page in range(1, pages + 1):
        data = search(q, page=page, **params)
        for item in data.get("organic_results", []):
            rows.append({"page": page, "position": item["position"], "title": item["title"], "link": item["link"]})
        if not data.get("pagination", {}).get("next_page"):
            break  # no further page: stop early and save credits
    return rows


rows = top_results("crm for small business", pages=3, gl="gb", hl="en")
print(len(rows), "results")
```

## Comment lancer de nombreuses recherches en parallèle ?

Utilisez `httpx.AsyncClient` avec un `asyncio.Semaphore` dimensionné à la limite de votre compte. Le script lit `concurrency_limit` depuis l'endpoint des crédits gratuits, il s'adapte donc quand le support augmente votre limite. Les recherches au-delà de la limite recevraient de toute façon `429` ; le sémaphore évite de gaspiller ces tentatives.

Chaque tâche réessaie les erreurs temporaires avec une pause croissante et renvoie `None` après quatre échecs, ainsi une requête récalcitrante ne bloque jamais le lot.

*asyncio + httpx : recherches parallèles sous la limite*

```python
"""Run many searches in parallel without exceeding the account's concurrency limit."""
import asyncio
import os

import httpx

API_URL = "https://serpcrypto.com/api/v1/search"
CREDITS_URL = "https://serpcrypto.com/api/v1/credits"
HEADERS = {"Authorization": "Bearer " + os.environ["SERPCRYPTO_API_KEY"]}


async def search_one(client, gate, q):
    async with gate:  # at most N searches at the same time
        for attempt in range(4):
            r = await client.get(API_URL, params={"q": q, "gl": "us", "hl": "en"})
            if r.status_code in (429, 502, 504):
                await asyncio.sleep(2 ** attempt)
                continue
            r.raise_for_status()
            return q, r.json()
        return q, None


async def main(queries):
    async with httpx.AsyncClient(headers=HEADERS, timeout=90) as client:
        limits = (await client.get(CREDITS_URL)).json()
        gate = asyncio.Semaphore(limits["concurrency_limit"])
        results = await asyncio.gather(*(search_one(client, gate, q) for q in queries))
    for q, data in results:
        first = (data or {}).get("organic_results", [{}])[0].get("link")
        print(f"{q!r}: {first}")


asyncio.run(main(["best vpn router", "home espresso machine", "4k monitor for coding"]))
```

## Comment enregistrer les classements quotidiens dans un fichier CSV ?

Ajoutez une ligne par résultat, avec le jour, le mot-clé, la position, le lien et les paramètres appliqués par l'API. Lire `gl` et `device` depuis `search_parameters` plutôt que depuis vos propres variables garantit que le fichier consigne ce qui a réellement été utilisé.

Lancez le script une fois par jour avec cron ou le Planificateur de tâches Windows. Trois mots-clés sur mobile coûtent trois crédits par jour, environ 90 par mois.

*Ajouter les classements du jour à rankings.csv*

```python
import csv
from datetime import date

from serp_client import search

KEYWORDS = ["standing desk", "ergonomic chair", "monitor arm"]
FIELDS = ["day", "keyword", "position", "title", "link", "gl", "device"]

with open("rankings.csv", "a", newline="", encoding="utf-8") as fh:
    writer = csv.DictWriter(fh, fieldnames=FIELDS)
    if fh.tell() == 0:
        writer.writeheader()
    for kw in KEYWORDS:
        data = search(kw, gl="gb", hl="en", device="mobile")
        applied = data["search_parameters"]
        for item in data.get("organic_results", []):
            writer.writerow({
                "day": date.today().isoformat(),
                "keyword": kw,
                "position": item["position"],
                "title": item["title"],
                "link": item["link"],
                "gl": applied["gl"],
                "device": applied["device"],
            })
```

## Quel client HTTP Python choisir ?

Utilisez `requests` pour les scripts et les tâches cron : il est simple et son adaptateur de retry est mature. Utilisez `httpx` quand vous avez besoin de code async, comme une web app, un agent ou un lot de centaines de mots-clés. Les deux envoient la même requête et reçoivent le même JSON ; l'API ne se soucie pas de celui que vous choisissez.

Évitez les timeouts très courts avec l'une ou l'autre bibliothèque. Un client qui abandonne après cinq secondes peut renvoyer une recherche encore en cours et payer les deux.

## Combien va coûter mon script ?

Comptez les recherches, pas les résultats : un appel réussi vaut un crédit. Un traitement de 500 mots-clés, une page chacun, chaque jour pendant 30 jours, représente 15 000 recherches. Sur le pack 25 000 recherches (18,75 $US), il reste de la marge, et Les crédits de recherche prépayés n'expirent jamais : les requêtes inutilisées restent sur votre compte jusqu'à ce que vous les utilisiez.

Vérifiez le solde depuis le code avec le helper `credits()` ci-dessus ; cela ne coûte rien.

## Questions Python

### Existe-t-il un package SerpCrypto sur PyPI ?

Non. Les exemples appellent l'endpoint HTTPS avec `requests` ou `httpx`, deux bibliothèques très répandues, donc il n'y a rien de notre côté à installer ni à mettre à jour. Le client ci-dessus fait environ 40 lignes et il est à vous.

### Quelles versions de Python ce code prend-il en charge ?

Python 3.9 et versions ultérieures. L'exemple async utilise `asyncio.run`, disponible depuis Python 3.7, et le reste n'utilise que la bibliothèque standard plus `requests` ou `httpx`.

### Puis-je utiliser l'API depuis un notebook Jupyter ?

Oui. Définissez `SERPCRYPTO_API_KEY` avant de lancer Jupyter, ou lisez la clé avec `getpass` dans le notebook pour qu'elle ne finisse pas dans une cellule sauvegardée. Dans un notebook, utilisez `await main(...)` au lieu de `asyncio.run(main(...))`.

### Comment transformer les résultats en DataFrame pandas ?

Passez la liste des dictionnaires de résultats à `pandas.DataFrame(data["organic_results"])`. Chaque champ devient une colonne ; les champs optionnels absents comme `date` deviennent des cellules vides.

---

SerpCrypto: SerpCrypto est une API SERP pour les développeurs et les agents IA qui renvoie les résultats de recherche Google et Bing en JSON, payable en crypto sans envoi de pièce d'identité. Les crédits de recherche prépayés à partir de 0,30 $US/1K requêtes n'expirent jamais. Sans KYC — pas de pièce d'identité, pas de selfie, pas de documents. Payer en crypto.
