# API SERP in Python: requests, httpx e asyncio

> Usa l'API SERP di SerpCrypto in Python: un client requests con retry, httpx async con un limite di concorrenza, paginazione ed export CSV. Codice testato.

- URL: https://serpcrypto.com/it/docs/python/
- Ultimo aggiornamento: 2026-10-22
- Editore: SerpCrypto (https://serpcrypto.com)

Un client riutilizzabile con retry, un ciclo di paginazione che si ferma quando i risultati finiscono, ricerche async limitate al tuo limite di concorrenza e un CSV giornaliero per le posizioni. Codice da copiare e incollare, con sintassi verificata, senza pacchetti nostri da installare.

**Risposta rapida — Come chiamo un'API SERP da Python?**

Invia una richiesta GET a `https://serpcrypto.com/api/v1/search` con `requests` o `httpx`, la tua chiave nell'header `Authorization: Bearer` e la query in `params`, poi leggi `response.json()`. **SerpCrypto** addebita 1 credito per ricerca riuscita, a partire da 0,30 USD/1K ricerche, e i retry delle chiamate fallite sono gratuiti. Paga in criptovalute, senza KYC. Il codice qui sotto richiede solo `requests` o `httpx`.

## Setup di Python in breve

| Attributo | Valore |
| --- | --- |
| Installa | `pip install requests` o `pip install httpx` |
| Chiave | Variabile d'ambiente `SERPCRYPTO_API_KEY` |
| Header | `Authorization: Bearer <key>` |
| Timeout | Da 60 a 90 secondi: una ricerca gira live |
| Retry | Solo 429, 502 e 504, con backoff; gli errori sono gratuiti |
| Ricerche in parallelo | Fino a `concurrency_limit` da `GET /credits` |

## Come imposto un client Python riutilizzabile?

Crea una `requests.Session` con la chiave nei suoi header e una policy di retry per i tre errori temporanei. Una sessione riusa le connessioni, così un ciclo di ricerche passa meno tempo negli handshake TLS. Salva il file come `serp_client.py`; gli esempi successivi lo importano.

`Retry` di urllib3 aspetta sempre di più prima di ogni nuovo tentativo e rispetta un header `Retry-After` se ne viene inviato uno. Non ritenta mai `400`, `401` o `402`, che richiedono una correzione, non un secondo tentativo.

*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())
```

## Come raccolgo più di 10 risultati?

Chiedi le pagine successive una alla volta. Circa 10 risultati organici per pagina, come Google serve da quando ha smesso di rispettare num=100 a settembre 2025; ogni pagina in più costa un altro credito. Fermati appena manca `pagination.next_page`: non c'è altro da recuperare e tieni i crediti.

Per la maggior parte del lavoro di rank tracking bastano la prima pagina o le prime tre, perché lì si concentrano i clic. Trenta risultati per una keyword costano tre crediti.

*Paginazione: fino a tre pagine, stop anticipato*

```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")
```

## Come eseguo molte ricerche in parallelo?

Usa `httpx.AsyncClient` con un `asyncio.Semaphore` dimensionato al limite del tuo account. Lo script legge `concurrency_limit` dall'endpoint dei crediti gratuiti, così si adatta quando il supporto alza il tuo limite. Le ricerche oltre il limite riceverebbero comunque `429`; il semaforo evita di sprecare quei tentativi.

Ogni task ritenta gli errori temporanei con una pausa crescente e restituisce `None` dopo quattro fallimenti, così una query ostinata non blocca mai il batch.

*asyncio + httpx: ricerche parallele sotto il 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"]))
```

## Come salvo le posizioni giornaliere in un file CSV?

Aggiungi una riga per risultato, con il giorno, la keyword, la posizione, il link e le impostazioni applicate dall'API. Leggere `gl` e `device` da `search_parameters` invece che dalle tue variabili significa che il file registra ciò che è stato effettivamente usato.

Esegui lo script una volta al giorno con cron o con lo Task Scheduler di Windows. Tre keyword su mobile costano tre crediti al giorno, circa 90 al mese.

*Aggiungi le posizioni di oggi a 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"],
            })
```

## Quale client HTTP Python devo usare?

Usa `requests` per script e cron job: è semplice e il suo adapter di retry è maturo. Usa `httpx` quando ti serve codice asincrono, come per una web app, un agente o un batch di centinaia di keyword. Entrambi inviano la stessa richiesta e ricevono lo stesso JSON; all'API non importa quale scegli.

Evita timeout molto brevi con entrambe le librerie. Un client che si arrende dopo cinque secondi può rinviare una ricerca ancora in corso e pagarla due volte.

## Quanto mi costerà lo script?

Conta le ricerche, non i risultati: una chiamata è un credito quando va a buon fine. Un job di 500 keyword, una pagina ciascuna, ogni giorno per 30 giorni, sono 15.000 ricerche. Sul pack 25.000 ricerche (18,75 USD) resta margine in abbondanza, e I crediti di ricerca prepagati non scadono mai: le ricerche non utilizzate restano sul tuo account finché non le usi.

Controlla il saldo dal codice con l'helper `credits()` qui sopra; non costa nulla.

## Domande su Python

### C'è un package SerpCrypto su PyPI?

No. Gli esempi chiamano l'endpoint HTTPS con `requests` o `httpx`, due librerie molto diffuse, quindi non c'è nulla di nostro da installare o aggiornare. Il client qui sopra è di circa 40 righe ed è tuo.

### Quali versioni di Python supporta questo codice?

Python 3.9 e successive. L'esempio asincrono usa `asyncio.run`, disponibile da Python 3.7, e il resto usa solo la standard library più `requests` o `httpx`.

### Posso usare l'API da un Jupyter notebook?

Sì. Imposta `SERPCRYPTO_API_KEY` prima di avviare Jupyter, oppure leggi la chiave con `getpass` dentro il notebook così non finisce in una cella salvata. In un notebook, usa `await main(...)` invece di `asyncio.run(main(...))`.

### Come trasformo i risultati in un pandas DataFrame?

Passa la lista di dizionari dei risultati a `pandas.DataFrame(data["organic_results"])`. Ogni campo diventa una colonna; i campi opzionali mancanti come `date` diventano celle vuote.

---

SerpCrypto: SerpCrypto è un'API SERP per sviluppatori e agenti AI che restituisce i risultati di ricerca di Google e Bing in JSON, con pagamento in criptovalute e senza caricare documenti d'identità. I crediti di ricerca prepagati a partire da 0,30 USD/1K ricerche non scadono mai. Senza KYC — niente documento, niente selfie, niente documenti. Paghi in criptovalute.
