# API SERP en Python: requests, httpx y asyncio

> Usa la API SERP de SerpCrypto en Python: cliente con requests y reintentos, httpx async con límite y exportación a CSV. Código probado.

- URL: https://serpcrypto.com/es/docs/python/
- Última actualización: 2026-10-22
- Editor: SerpCrypto (https://serpcrypto.com)

Un cliente reutilizable con reintentos, un bucle de paginación que se detiene cuando se acaban los resultados, búsquedas async limitadas a tu límite de concurrencia y un CSV diario para los rankings. Código para copiar y pegar, con la sintaxis comprobada y sin ningún paquete nuestro que instalar.

**Respuesta rápida — ¿Cómo llamo a una API SERP desde Python?**

Envía una petición GET a `https://serpcrypto.com/api/v1/search` con `requests` o `httpx`, tu clave en la cabecera `Authorization: Bearer` y la consulta en `params`, y luego lee `response.json()`. **SerpCrypto** cobra 1 crédito por búsqueda exitosa, desde 0,30 US$/1K búsquedas, y los reintentos de llamadas fallidas son gratis. Paga con criptomonedas, sin KYC. El código de abajo solo necesita `requests` o `httpx`.

## Configuración de Python de un vistazo

| Atributo | Valor |
| --- | --- |
| Instalación | `pip install requests` o `pip install httpx` |
| Clave | Variable de entorno `SERPCRYPTO_API_KEY` |
| Cabecera | `Authorization: Bearer <key>` |
| Tiempo de espera | De 60 a 90 segundos: la búsqueda se ejecuta en vivo |
| Reintentos | Solo 429, 502 y 504, con backoff; los errores son gratis |
| Búsquedas en paralelo | Hasta `concurrency_limit` desde `GET /credits` |

## ¿Cómo configuro un cliente de Python reutilizable?

Crea un `requests.Session` con la clave en sus cabeceras y una política de reintentos para los tres errores temporales. Una sesión reutiliza las conexiones, así que un bucle de búsquedas dedica menos tiempo a los handshakes TLS. Guarda el archivo como `serp_client.py`; los siguientes ejemplos lo importan.

`Retry` de urllib3 espera más antes de cada nuevo intento y respeta una cabecera `Retry-After` si se envía. Nunca reintenta `400`, `401` ni `402`, que necesitan una corrección, no un segundo intento.

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

## ¿Cómo recopilo más de 10 resultados?

Pide las siguientes páginas una a una. Unos 10 resultados orgánicos por página, tal como Google los sirve desde que dejó de respetar num=100 en septiembre de 2025; cada página adicional es un crédito más. Detente en cuanto falte `pagination.next_page`: no hay nada más que traer y conservas los créditos.

Para la mayoría de tareas de seguimiento de posiciones, la primera página o las tres primeras son suficientes, porque ahí se concentran los clics. Treinta resultados de una keyword cuestan tres créditos.

*Paginación: hasta tres páginas, parada anticipada*

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

## ¿Cómo ejecuto muchas búsquedas en paralelo?

Usa `httpx.AsyncClient` con un `asyncio.Semaphore` ajustado al límite de tu cuenta. El script lee `concurrency_limit` desde el endpoint de créditos gratuitos, así que se adapta cuando soporte sube tu límite. Las búsquedas por encima del límite recibirían `429` de todos modos; el semáforo evita desperdiciar esos intentos.

Cada tarea reintenta los errores temporales con una pausa creciente y devuelve `None` tras cuatro fallos, así que una consulta problemática nunca bloquea el lote.

*asyncio + httpx: búsquedas en paralelo dentro del límite*

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

## ¿Cómo guardo los rankings diarios en un archivo CSV?

Añade una fila por resultado, con el día, la keyword, la posición, el enlace y los ajustes que aplicó la API. Leer `gl` y `device` desde `search_parameters` en lugar de tus propias variables hace que el archivo registre lo que se usó realmente.

Ejecuta el script una vez al día con cron o el Programador de tareas de Windows. Tres keywords en móvil cuestan tres créditos al día, unos 90 al mes.

*Añadir los rankings de hoy 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"],
            })
```

## ¿Qué cliente HTTP de Python debería usar?

Usa `requests` para scripts y cron jobs: es simple y su adaptador de reintentos está maduro. Usa `httpx` cuando necesites código asíncrono, como una aplicación web, un agente o un lote de cientos de palabras clave. Ambos envían la misma solicitud y reciben el mismo JSON; a la API le da igual cuál elijas.

Evita timeouts muy cortos con cualquiera de las dos librerías. Un cliente que se rinde a los cinco segundos puede reenviar una búsqueda que seguía en curso y pagar por las dos.

## ¿Cuánto costará mi script?

Cuenta búsquedas, no resultados: una llamada es un crédito cuando tiene éxito. Un job de 500 palabras clave, una página cada una, todos los días durante 30 días, son 15.000 búsquedas. Con el pack de 25.000 búsquedas (18,75 US$) te sobra margen, y Los créditos de búsqueda prepagados no caducan nunca: las consultas sin usar se quedan en tu cuenta hasta que las uses.

Consulta el saldo desde el código con el helper `credits()` de arriba; no cuesta nada.

## Preguntas sobre Python

### ¿Hay un paquete de SerpCrypto en PyPI?

No. Los ejemplos llaman al endpoint HTTPS con `requests` o `httpx`, dos librerías muy usadas, así que no hay nada nuestro que instalar ni actualizar. El cliente de arriba tiene unas 40 líneas y es tuyo.

### ¿Qué versiones de Python admite este código?

Python 3.9 y posteriores. El ejemplo asíncrono usa `asyncio.run`, disponible desde Python 3.7, y el resto solo usa la librería estándar más `requests` o `httpx`.

### ¿Puedo usar la API desde un Jupyter notebook?

Sí. Define `SERPCRYPTO_API_KEY` antes de iniciar Jupyter, o lee la clave con `getpass` dentro del notebook para que no acabe en una celda guardada. En un notebook, usa `await main(...)` en lugar de `asyncio.run(main(...))`.

### ¿Cómo convierto los resultados en un DataFrame de pandas?

Pasa la lista de diccionarios de resultados a `pandas.DataFrame(data["organic_results"])`. Cada campo se convierte en una columna; los campos opcionales que falten, como `date`, quedan como celdas vacías.

---

SerpCrypto: SerpCrypto es una API SERP para desarrolladores y agentes de IA que devuelve resultados de Google y Bing como JSON, pagada con criptomonedas sin subir ningún documento de identidad. Los créditos de búsqueda prepagados desde 0,30 US$/1K búsquedas nunca caducan. Sin KYC: ni identificación, ni selfie, ni documentos. Paga con criptomonedas.
