Saltar al contenido
Empezar gratis
Volver al blog

Guía de la API de extracción de datos de SERP: Google, Bing, DuckDuckGo, Yandex

Suciu DanÚltima actualización el 25 min read
Guía de la API de extracción de datos de SERP: Google, Bing, DuckDuckGo, Yandex
En resumen: Para usar la SERP Scraping API, envía una solicitud GET a https://serpapi.webscrapingapi.com/v2 con api_key, engine y tu consulta (q para la mayoría de los motores, text para Yandex). Google y Bing devuelven JSON ya analizado, mientras que DuckDuckGo y Yandex devuelven el HTML de la página de resultados para que tu código lo extraiga, así que planifica el análisis y las pruebas en consecuencia. Esta guía también cubre la localización, los trabajos asíncronos, los errores y los precios.

Necesitas resultados de búsqueda para un conjunto de palabras clave, en países e idiomas concretos, en dispositivos específicos, como datos que tu código pueda almacenar y comparar semana tras semana. Utilizar tu propio rastreador de búsquedas implica lidiar con bloqueos y reescribir los analizadores sintácticos cada vez que cambia una página de resultados, por lo que esta guía utiliza, en su lugar, la API SERP de WebScrapingAPI. Al final, enviarás una primera solicitud con tu clave en una variable de entorno, elegirás el motor adecuado, definirás el mercado que estás analizando, analizarás el JSON de Google y Bing de forma defensiva, extraerás tú mismo el HTML de DuckDuckGo y Yandex, realizarás búsquedas en Google de forma asíncrona, volverás a intentarlo según el contrato de errores documentado y elegirás un plan.

Inicio rápido

  • Punto final: https://serpapi.webscrapingapi.com/v2 (GET)
  • Requisitos: api_key, engine, y la consulta (q para la mayoría de los motores, text para Yandex)
  • Resultado: Google y Bing devuelven JSON analizado. DuckDuckGo y Yandex devuelven el HTML de la página de resultados dentro de una envoltura JSON
  • Prueba gratuita: 100 búsquedas en Google

¿Qué es la API de extracción de SERP y qué devuelve?

La API SERP de WebScrapingAPI es un punto final del lado del servidor que toma una consulta de búsqueda, un motor de búsqueda y el contexto de mercado, y a continuación devuelve la página de resultados en forma de datos. Google y Bing devuelven colecciones JSON analizadas. DuckDuckGo y Yandex devuelven una envoltura JSON cuyo search_results campo contiene el código HTML de la SERP para que tu código lo extraiga.

Esa distinción es lo más importante que debes comprender antes de escribir cualquier código. Con Google y Bing, lees colecciones con nombre como organic[] y related[]. Con DuckDuckGo y Yandex, recibes la página sin procesar y decides qué extraer de ella. Tu modelo de almacenamiento, tus pruebas y tu trabajo de mantenimiento dependen de en qué lado de esa línea se sitúe cada motor de búsqueda.

La API abarca cuatro familias de motores: Google, Bing, DuckDuckGo y Yandex. Google también cuenta con siete motores especializados además de la búsqueda estándar: búsqueda asíncrona, Maps, Reseñas de Maps, Hoteles, Vuelos, Tendencias y Búsqueda inversa de imágenes. Los equipos los utilizan para supervisar el posicionamiento y las funciones de la SERP, la presencia de marcas y editores, los anuncios pagados que aparecen en los resultados y la investigación de mercado en distintos temas y editores.

  • WebScrapingAPI se encarga de: la validación de solicitudes y la ejecución de los motores, la infraestructura de acceso a la búsqueda, la recopilación de resultados analizados allí donde están documentados, la entrega del envoltorio JSON para los motores HTML y el mantenimiento del comportamiento de los puntos finales compatibles.
  • Tu equipo es responsable de: grupos de consultas, mercados, idiomas y dispositivos, además de la programación, los reintentos a nivel de API, la extracción de los envoltorios HTML, el almacenamiento, los cálculos de posicionamiento, las alertas y la política de uso y retención de datos.

Hay otro aspecto importante para el análisis. La API no define una clasificación universal. Cada resultado describe una consulta, en un mercado, en un dispositivo y en un momento concreto, así que mantén ese contexto asociado a cada fila que almacenes. ¿Lo comparas con otras API en tiempo de solicitud? Descubre cómo encaja la API SERP junto a las demás API de acceso web.

¿Cómo envías tu primera solicitud a la API de SERP?

Crea una cuenta, suscríbete a la API de SERP en el panel de control y copia tu clave de API en una variable de entorno. A continuación, envía una solicitud GET a https://serpapi.webscrapingapi.com/v2 con api_key, engine=google, y q establece tu palabra clave. Deja que tu biblioteca HTTP codifique los parámetros y mantén la clave en el servidor.

  1. Regístrate en WebScrapingAPI.
  2. Suscríbete a la API de SERP en el panel de control. La versión de prueba incluye 100 búsquedas en Google.
  3. Guarda la clave en tu shell o en tu gestor de secretos: export WSA_API_KEY=...
  4. Envía la solicitud con uno de los fragmentos de código que aparecen a continuación.

La solicitud tiene el mismo formato para todos los motores. Solo enginecambian el campo de consulta y los parámetros de contexto, por lo que, una vez que esta llamada funcione, el resto de motores solo será cuestión de ajustar los parámetros.

curl. Los --data-urlencode opciones codifican cada valor, y --max-time 120 establece un tiempo de espera explícito para el cliente:

# Google, parsed JSON, US English, mobile
EP="https://serpapi.webscrapingapi.com/v2"
curl --get --fail-with-body --max-time 120 "$EP" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "engine=google" \
  --data-urlencode "q=running shoes" \
  --data-urlencode "gl=us" --data-urlencode "hl=en" --data-urlencode "device=mobile"

Python. Este pequeño cliente lee la clave del entorno, pasa cada valor a través del params argumento para que Requests lo codifique por ti, vuelve a intentarlo según el contrato de error y registra los encabezados de uso. Las secciones de Bing y de gestión de errores que aparecen a continuación lo reutilizan.

"""Minimal WebScrapingAPI SERP API client: env-var key, jittered retries, usage headers.

Usage:
  export WSA_API_KEY=...                       # never hardcode the key
  python wsa_serp.py google "running shoes" gl=us hl=en device=mobile
  python wsa_serp.py bing "coffee roasters" mkt=en-US cc=us lang=en
  python wsa_serp.py yandex "pizza" lr=213 p=0
  python wsa_serp.py google "running shoes" gl=us --dry-run   # print the request, no call
"""
import os
import random
import sys
import time

import requests

ENDPOINT = "https://serpapi.webscrapingapi.com/v2"
# Most engines take their main input in q. These don't (see the engine table).
QUERY_FIELD = {"yandex": "text", "google_maps_reviews": "fid", "google_reverse_image": "url"}
RETRYABLE = {429, 500, 502, 503}   # back off with jitter, then retry
FINAL = {400, 401}                 # fix the key, plan, or parameters; never retry


def build_params(engine, query, **extra):
    params = {"engine": engine, QUERY_FIELD.get(engine, "q"): query}
    params.update({k: str(v) for k, v in extra.items()})
    return params


def search(engine, query, api_key, max_attempts=4, **extra):
    params = {"api_key": api_key, **build_params(engine, query, **extra)}
    for attempt in range(1, max_attempts + 1):
        resp = requests.get(ENDPOINT, params=params, timeout=120)
        usage = {k: v for k, v in resp.headers.items() if k.lower().startswith("searchdata-")}
        if resp.ok:
            return resp.json(), usage
        if resp.status_code in FINAL:
            # 401: missing, invalid, or unentitled key. 400: "Parameter '<name>' not allowed." etc.
            raise SystemExit(f"{resp.status_code}: {resp.text[:300]}")
        retry_422 = resp.status_code == 422 and attempt == 1   # target-side failure: one retry only
        if resp.status_code in RETRYABLE or retry_422:
            time.sleep(random.uniform(0, min(2 ** attempt, 30)))   # full jitter
            continue
        raise SystemExit(f"Unexpected {resp.status_code}: {resp.text[:300]}")
    raise SystemExit(f"Gave up after {max_attempts} attempts")


def organic(data):
    """Product pages document organic[]; some docs pages show organic_results[]. Accept both."""
    return data.get("organic") or data.get("organic_results") or []


def main():
    args = [a for a in sys.argv[1:] if a != "--dry-run"]
    if len(args) < 2:
        sys.exit(__doc__)
    engine, query = args[0], args[1]
    extra = dict(a.split("=", 1) for a in args[2:])

    if "--dry-run" in sys.argv:
        prepared = requests.Request(
            "GET", ENDPOINT, params={"api_key": "REDACTED", **build_params(engine, query, **extra)}
        ).prepare()
        print(prepared.url)
        return

    api_key = os.environ.get("WSA_API_KEY")
    if not api_key:
        sys.exit("Set WSA_API_KEY first (dashboard -> SERP API).")

    data, usage = search(engine, query, api_key, **extra)
    print("usage headers:", usage)
    if "search_results" in data:          # DuckDuckGo and Yandex: SERP HTML inside a JSON envelope
        print("search_parameters:", data.get("search_parameters"))
        print("html chars:", len(data["search_results"]))
    else:                                  # Google and Bing: parsed JSON
        for row in organic(data)[:10]:
            print(row.get("rank") or row.get("position"), row.get("title"), row.get("link"))


if __name__ == "__main__":
    main()

Ejecuta python wsa_serp.py google "running shoes" gl=us hl=en device=mobile --dry-run primero. Muestra la URL exacta de la solicitud con la clave sustituida por REDACTED, para que puedas comprobar los parámetros antes de realizar una búsqueda.

Node.js (versión 18 o posterior). URLSearchParams se encarga de la codificación, y AbortSignal.timeout establece el mismo límite de 120 segundos:

// Build and (optionally) send a SERP API request in Node 18+.
// URLSearchParams handles encoding; the key comes from the environment.
const ENDPOINT = "https://serpapi.webscrapingapi.com/v2";
const QUERY_FIELD = { yandex: "text", google_maps_reviews: "fid", google_reverse_image: "url" };

export function buildSerpUrl(apiKey, engine, query, extra = {}) {
  const url = new URL(ENDPOINT);
  url.searchParams.set("api_key", apiKey);
  url.searchParams.set("engine", engine);
  url.searchParams.set(QUERY_FIELD[engine] ?? "q", query);
  for (const [k, v] of Object.entries(extra)) url.searchParams.set(k, String(v));
  return url;
}

export async function search(engine, query, extra = {}) {
  const apiKey = process.env.WSA_API_KEY;
  if (!apiKey) throw new Error("WSA_API_KEY is required");
  const res = await fetch(buildSerpUrl(apiKey, engine, query, extra), {
    signal: AbortSignal.timeout(120_000),
  });
  if (!res.ok) throw new Error(`SERP API ${res.status}: ${(await res.text()).slice(0, 300)}`);
  const data = await res.json();
  return { data, organic: data.organic ?? data.organic_results ?? [] };
}

// Dry run: show the encoded URL and prove the query survives the round trip.
if (process.argv[1]?.endsWith("build_serp_request.mjs")) {
  const q = 'site:example.com "running shoes" & socks?';
  const u = buildSerpUrl("REDACTED", "google", q, { gl: "us", hl: "en", device: "mobile" });
  console.log(u.toString());
  console.log("round trip ok:", new URL(u).searchParams.get("q") === q);
  const y = buildSerpUrl("REDACTED", "yandex", "pizza", { lr: 213, p: 0 });
  console.log(y.toString());
}

La ejecución de prueba muestra por qué es importante la codificación. La consulta site:example.com "running shoes" & socks? se convierte q=site%3Aexample.com+%22running+shoes%22+%26+socks%3F y se decodifica sin cambios (round trip ok: true). Si pegas esa consulta en una URL a mano, el & acaba con tu q y da inicio a un nuevo parámetro denominado socks?. El /v2 punto final valida los parámetros según el motor y rechaza los nombres que no reconoce, por lo que una URL creada manualmente falla de formas confusas. Deja que la biblioteca aplique la codificación porcentual en todo momento.

Trata la clave como si fuera una contraseña. Guárdala en variables de entorno del lado del servidor o en un gestor de secretos, y nunca la incluyas en el código front-end, en repositorios públicos, en registros ni en capturas de pantalla. Cualquiera que tenga la clave puede agotar tus búsquedas.

Documentación: Acceder a la API

¿A qué motores de búsqueda y motores de Google se puede realizar consultas?

La referencia actual de la API enumera once motores: google, google_async, google_maps, google_maps_reviews, google_flights, google_hotels, google_trends, google_reverse_image, bing, duckduckgo, y yandex. Cada uno tiene sus propios datos de entrada obligatorios y parámetros permitidos. Considera que solo estos son compatibles, aunque en páginas de documentación más antiguas se describan otros motores de Google.

Motor

Datos de entrada obligatorios

Se obtiene

Precio

google

q

JSON analizado (general, input, navigation[], organic[], módulos opcionales)

Planes de Google

google_async

q

response_id, y a continuación la carga útil final de la SERP

Planes de Google

google_maps

type=search + q, o type=place + data

Resultados de lugares analizados

Planes de Google

google_maps_reviews

fid

Reseñas analizadas

Planes de Google

google_hotels

type=search + q, o type=place + pid; fechas de entrada y salida

overview, organic[]

Planes de Google

google_flights

q (token codificado de Google Flights)

search_parameters, search_results[]

Planes de Google

google_trends

q

widgets[] (TIMESERIES, GEO_MAP)

Planes de Google

google_reverse_image

url

general, images[], exact_matches[]

Planes de Google

bing

q

JSON analizado (general, organic[], related[])

Cita

duckduckgo

q

Envolvente JSON: search_parameters + search_results (HTML)

Cita

yandex

text

Envolvente JSON: search_parameters + search_results (HTML)

Cita

Tres motores no aceptan su entrada principal en q. Yandex toma la consulta en text, Google Maps Reviews toma la fid, y Google Reverse Image toma una imagen url. Los clientes de Python y Node mencionados anteriormente ya asignan esos campos por ti. Google Maps y Google Hotels también necesitan type=search o type=place, lo que cambia el otro campo obligatorio.

«Planes de Google» se refiere a los planes mensuales publicados de Google que se describen en la sección de precios. Los precios de Bing, DuckDuckGo y Yandex se establecen mediante presupuesto.

Es posible que encuentres páginas de documentación sobre otros motores de Google, como autocompletado, eventos, finanzas, empleo, servicios locales, fotos de Maps, Play, productos y Scholar. No figuran en la lista de servicios compatibles, así que no construyas un proceso de trabajo basado en ellos.

Documentación: Referencia de las API SearchData y SERP

¿Cómo se localizan los resultados de Google por país, idioma y dispositivo?

Establece gl el código de país de dos letras, hl al idioma de la interfaz y device a desktop, mobile, o tablet. Añade domain para un servidor específico de Google y uule para una ubicación codificada cuando el contexto a nivel de ciudad sea relevante. Almacena estos valores con cada resultado, ya que la misma consulta puede posicionarse de forma diferente en cada mercado.

El propio Google afirma que los resultados dependen del lugar desde el que se realice la búsqueda, por lo que una posición en los resultados sin indicar su mercado es solo una verdad a medias. Esta búsqueda canadiense, en inglés y desde un ordenador de sobremesa, del término «historia» en google.ca se ejecuta a través del cliente de Python desde la sección de primera solicitud:

python wsa_serp.py google history gl=ca hl=en domain=google.ca device=desktop

Si necesitas una ciudad concreta, como Londres (Ontario), añade su uule valor como un argumento más uule=... argumento. Utiliza únicamente los parámetros que especifique tu motor. Los ejemplos más antiguos que ordenan por fecha o filtran por periodo de tiempo no son aplicables a /v2 Búsqueda de Google, y el punto final rechaza los parámetros que no permite.

El tipo de resultados y la paginación también influyen en lo que se obtiene:

  • Superficies: tbm cambia el tipo de resultados: isch para imágenes, vid para vídeos, nws para noticias y shop para compras. Para los resultados de Google Jobs, utiliza ibp=htl;jobs.
  • Paginación: start establece cuántos resultados se deben omitir, y num establece cuántos resultados se mostrarán por página.

El hábito más útil aquí es el diseño de mercado. Antes de recopilar nada, elige una cuadrícula pequeña y fija de consulta × país × idioma × dispositivo, y no la cambies entre ejecuciones. Si la ejecución de una semana utiliza gl=us&device=mobile y la siguiente utiliza gl=us&device=desktop, una «caída en el ranking» puede deberse simplemente a un cambio de dispositivo. Cada respuesta es una observación de un mercado concreto, no una verdad universal, por lo que las comparaciones solo tienen sentido dentro de la misma celda de la cuadrícula.

La lista completa de campos de solicitud y bloques de respuesta se encuentra en la página de controles de la API de Búsqueda de Google.

Motor

Ubicación / mercado

Idioma

Dispositivo

Paginación / hora

Filtros y superficies

Google

gl (país), domain (servidor de Google), uule (ubicación codificada)

hl

device = ordenador, móvil, tableta

start (omitir), num (por página)

tbm = isch, vid, nws, shop; ibp=htl;jobs

Bing

mkt (p. ej., en-US), cc, location, lat, lon

lang

—

first (desplazamiento), count

safesearch = desactivado, moderado, estricto

DuckDuckGo

kl (p. ej., en_US)

kad (interfaz)

device

df = d, w, m, y

kp = 1, -1, -2

Yandex

lr (ID de región, p. ej., 213), yandex_domain

lang

device

p (con base en cero), within = 77 (24 h), 1 (2 semanas)

—

Documentación: Parámetros de la API de búsqueda de Google

¿Cómo se leen y almacenan los resultados analizados de Google y Bing?

Lee la matriz «organic» para obtener los resultados clasificados, cada uno con un título, un enlace, un enlace de visualización, una descripción y una posición en el ranking. Considera opcionales todos los demás módulos, como las búsquedas relacionadas, la navegación, los anuncios o los resultados locales. Almacena cada fila con la consulta, el mercado, el dispositivo y la hora de captura para que las comparaciones de posicionamiento posteriores sigan siendo significativas.

Una respuesta de Google contiene varios bloques de nivel superior. general describe la solicitud (motor, idioma, ubicación, indicador de móvil y tipo de búsqueda), input contiene la URL de búsqueda original, navigation[] enumera otras superficies de Google para la consulta, y organic[] contiene los resultados clasificados. Una respuesta de Bing contiene general, organic[], y related[].

Todo lo demás es condicional. Los anuncios, los resultados locales, las compras, los medios de comunicación y las búsquedas relacionadas solo aparecen cuando la página de resultados los muestra, y las funciones de la SERP que muestra Google varían según la consulta, la ubicación y el dispositivo. Por lo tanto, distingue dos casos en tu código: una solicitud que ha fallado (un error HTTP) y una solicitud que se ha realizado correctamente pero a la que le falta un módulo. Lo segundo es una observación normal, no un error.

Escribe el analizador de forma defensiva. Algunas páginas de documentación muestran la lista ordenada como organic_results[] con position en lugar de organic[] con rank, así que lee cualquiera de las dos. Este script desglosa una respuesta en filas de clasificación. El archivo de entrada es una muestra etiquetada, no una salida real:

"""Flatten a Google or Bing SERP API response into rank rows.

Organic results may arrive as organic[] (product pages) or organic_results[] (some docs pages),
and modules such as related[] or navigation[] are optional, so read them defensively.
"""
import json
import sys
from datetime import datetime, timezone


def rank_rows(data, query, market):
    rows = []
    captured = datetime.now(timezone.utc).isoformat(timespec="seconds")
    for item in data.get("organic") or data.get("organic_results") or []:
        rows.append({
            "captured_at": captured,
            "engine": data.get("general", {}).get("search_engine"),
            "query": query,
            "market": market,
            "rank": item.get("rank") or item.get("position"),
            "title": item.get("title"),
            "link": item.get("link"),
        })
    return rows


if __name__ == "__main__":
    data = json.load(open(sys.argv[1] if len(sys.argv) > 1 else "sample_google.json"))
    for row in rank_rows(data, "running shoes", "us/en/mobile"):
        print(row)
    print("optional modules present:", [k for k in ("navigation", "related", "related_questions") if k in data])

En el archivo de muestra, muestra una fila por cada resultado orgánico y enumera qué módulos opcionales estaban presentes:

{'captured_at': '2026-10-29T12:03:56+00:00', 'engine': 'google', 'query': 'running shoes', 'market': 'us/en/mobile', 'rank': 1, 'title': 'Example result one', 'link': 'https://example.com/a'}
{'captured_at': '2026-10-29T12:03:56+00:00', 'engine': 'google', 'query': 'running shoes', 'market': 'us/en/mobile', 'rank': 2, 'title': 'Example result two', 'link': 'https://example.org/b'}
optional modules present: ['navigation']

Una fila de posicionamiento debe incluir, como mínimo, captured_at: motor, consulta, mercado, dispositivo, posición, título y enlace. Con esos campos, puedes comparar ejecuciones, trazar gráficos de evolución y alertar ante caídas. Ese es el núcleo del seguimiento de las posiciones y las características de la SERP a lo largo del tiempo. Si tus enlaces almacenados incluyen redireccionamientos de Google, infórmate sobre los enlaces de redireccionamiento /goto de Google en las URL de los resultados antes de eliminar duplicados por URL.

¿Cómo se realiza una búsqueda en Bing con la API de SERP?

Establece engine=bing y pasa la consulta en q. Controla el mercado con mkt, como en-US, además de cc el país y lang para el idioma. Añade location, o lat y lon, para un origen preciso, safesearch para filtrar, y first con count para navegar por los resultados. Las respuestas de Bing se reciben como JSON analizado.

La versión con curl para el mercado de EE. UU., primera página de 10 resultados:

curl --get --fail-with-body --max-time 120 "https://serpapi.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "engine=bing" \
  --data-urlencode "q=coffee roasters" \
  --data-urlencode "mkt=en-US" --data-urlencode "cc=us" --data-urlencode "lang=en" \
  --data-urlencode "first=0" --data-urlencode "count=10"

Con el cliente de Python de la sección «primera solicitud», la misma llamada se realiza en una sola línea:

python wsa_serp.py bing "coffee roasters" mkt=en-US cc=us lang=en first=0 count=10

Bing devuelve general, organic[], y related[]. Los anuncios y las imágenes son módulos opcionales, por lo que se aplican las mismas reglas de seguridad: lee organic[], trata todo lo demás como presente o ausente, y almacena cada fila con su mercado. safesearch toma off, moderate, o strict.

mantener mkt, cc, y lang consistente entre ejecuciones, tal y como harías con el gl y hl. El precio de Bing se establece mediante presupuesto, y la versión de prueba de 100 búsquedas cubre Google. Los campos de solicitud y los bloques de respuesta se encuentran en la página de la API de búsqueda de Bing.

Documentación: Solicitudes básicas de la API de Bing

¿Cómo se gestionan los resultados HTML de DuckDuckGo y Yandex?

Ambos motores devuelven JSON con search_parameters, que describe la solicitud, y search_results, una cadena que contiene el código HTML original de la SERP. WebScrapingAPI no analiza ese HTML, por lo que tu aplicación extrae los campos que necesita. Vincula tus selectores a una captura real, asigne versiones a cada uno, almacena la envoltura sin procesar y avisa cuando un selector deje de coincidir.


Google, Bing

DuckDuckGo, Yandex

Respuesta

Metadatos JSON + colecciones analizadas

Metadatos JSON + código HTML original de la SERP en search_results

Análisis a cargo de

WebScrapingAPI, donde se documenta

Tu aplicación

Tu código hace lo siguiente

Lee las colecciones de forma defensiva; almacena filas

Selecciona y elige selectores de versión; extrae; avisa en caso de desviación

Almacena

Filas + contexto de la solicitud

Envolvente sin procesar + filas extraídas + versión del selector

Campos de solicitud de DuckDuckGo: q para la consulta, kl para la región y el idioma (como en_US), kad para el idioma de la interfaz, kp para la búsqueda segura (1 activada, -1 moderada, -2 desactivada), df para la hora (d, w, m, y) y device.

campos de solicitud de Yandex: text para la consulta, lr para el ID de la región (por ejemplo, 213), lang, device, yandex_domain, p para la página (con numeración a partir de cero, por lo que p=0 es la primera página), y within para la hora (77 de las últimas 24 horas, 1 de las últimas dos semanas).

# DuckDuckGo, SERP HTML in a JSON envelope
curl --get --fail-with-body --max-time 120 "https://serpapi.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "engine=duckduckgo" \
  --data-urlencode "q=privacy research" \
  --data-urlencode "kl=en_US" --data-urlencode "df=w"

# Yandex, SERP HTML in a JSON envelope (query parameter: text)
curl --get --fail-with-body --max-time 120 "https://serpapi.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "engine=yandex" \
  --data-urlencode "text=pizza" \
  --data-urlencode "lr=213" --data-urlencode "p=0"

El patrón de extracción es el mismo para ambos motores: validar el contenedor, analizar el HTML, seleccionar los enlaces de resultados, descartar los enlaces de navegación propios del motor, etiquetar cada fila con una versión del selector, indicar claramente un error cuando no haya coincidencias y conservar el HTML sin procesar para poder volver a procesarlo más tarde. Este script utiliza Beautiful Soup con selectores CSS:

"""Pull result links out of a DuckDuckGo or Yandex SERP API envelope.

These engines return {"search_parameters": {...}, "search_results": "<html>...</html>"}.
Your application owns extraction: pin the selector after inspecting a real capture,
version it, and alert when it stops matching. The selectors below fit the synthetic
fixtures only.
"""
import json
import sys
from urllib.parse import urlparse

from bs4 import BeautifulSoup

SELECTORS = {"duckduckgo": "div.result a.result-link", "yandex": "li.serp-item a.link"}
ENGINE_HOSTS = ("duckduckgo.com", "yandex.com", "yandex.ru")


def extract(envelope, selector_version="fixture-v1"):
    params = envelope.get("search_parameters", {})
    engine = params.get("search_engine")
    html = envelope.get("search_results")
    if not isinstance(html, str) or "<html" not in html.lower():
        raise ValueError("search_results is missing or is not HTML; store the raw envelope and alert")
    soup = BeautifulSoup(html, "html.parser")
    rows = []
    for pos, a in enumerate(soup.select(SELECTORS[engine]), start=1):
        host = urlparse(a.get("href", "")).hostname or ""
        if host.endswith(ENGINE_HOSTS):
            continue  # skip the engine's own navigation links
        rows.append({"engine": engine, "query": params.get("query"), "position": pos,
                     "title": a.get_text(strip=True), "link": a["href"],
                     "selector_version": selector_version})
    if not rows:
        raise ValueError(f"selector matched nothing for {engine}; markup may have changed")
    return rows


if __name__ == "__main__":
    for path in sys.argv[1:] or ["sample_ddg_envelope.json", "sample_yandex_envelope.json"]:
        for row in extract(json.load(open(path))):
            print(row)

Los selectores de SELECTORS solo se ajustan a los archivos de muestra sintéticos. El marcado real de DuckDuckGo y Yandex es diferente y cambia. Antes de ejecutar esto en producción, guarda una «envelope» real, inspecciona su HTML, define tus propios selectores y actualiza selector_version cada vez que los modifiques. Cuando el marcado cambie, el ValueError se activa en lugar de que tu proceso almacene silenciosamente cero resultados, y como has conservado el HTML sin procesar, puedes volver a extraer las ejecuciones afectadas una vez que se hayan corregido los selectores.

Los campos específicos de cada motor se encuentran en las páginas de la API de búsqueda de DuckDuckGo y de la API de búsqueda de Yandex.

Documentación: Parámetros de la API de DuckDuckGo · Parámetros de la API de Yandex

Utiliza los motores especializados de Google cuando la respuesta se encuentre fuera de los resultados web habituales. Google Maps busca lugares, Maps Reviews muestra las reseñas de un lugar por fid, Trends muestra gráficos de evolución del interés a lo largo del tiempo, Hotels y Flights ofrecen resultados de viajes, y Reverse Image busca coincidencias de una imagen por URL. Cada motor tiene sus propios datos de entrada y parámetros obligatorios.

Todos utilizan el mismo punto final, la misma clave y las mismas reglas de codificación que la búsqueda estándar. Solo cambia el engine valor y los campos cambian:

  • google_maps: envía type=search con q, o type=place con data. ll=@lat,lon,zoom establece la vista del mapa, y place_type limita la búsqueda a hoteles o alojamientos vacacionales. No se ha documentado ninguna paginación.
  • google_maps_reviews: envía el fid, que se obtiene del knowledge.fid de una respuesta de búsqueda de Google. Ordena con sort (qualityScore, newestFirst, ratingHigh, ratingLow) y la paginación con start y num.
  • google_trends: envía q con geo, date (today 12-m, now 1-H, o un YYYY-MM-DD YYYY-MM-DD rango), cat, y gprop (images, news, froogle, youtube). La respuesta contiene widgets[], como TIMESERIES y GEO_MAP.
  • google_hotels: enviar type=search con q, o type=place con pid, además de check_in_date y check_out_date. Los filtros de ocupación, moneda y cancelación gratuita son opcionales.
  • google_flights: q es un token de consulta codificado de Google Flights, no texto sin formato. Añade curr, hl, y gl para la moneda y el mercado.
  • google_reverse_image: envía la imagen url, y utiliza lens (products, visual_matches, exact_matches, homework) para elegir el tipo de coincidencia.

Si estás dudando entre la búsqueda inversa de imágenes y tbm=isch los resultados de imágenes, consulta la comparación de las API de búsqueda de imágenes de Google.

Documentación: Motores de búsqueda de Google

¿Cómo se realizan búsquedas en Google de forma asíncrona?

Envía la búsqueda con engine=google_async. La API la pone en cola y devuelve un response_id de inmediato. Guarda ese ID junto con tu tarea y, a continuación, consulta el mismo /v2 punto final con snapshot_id establecido en el ID y type=serp cada 10 o 30 segundos hasta que llegue la respuesta completa de la SERP. Limita el tiempo total de espera.

Async es adecuado para lotes grandes y trabajadores que no pueden mantener abierta una solicitud de 120 segundos. Acepta los campos habituales de Google, además de device y response=json. Es exclusivo de Google: Bing, DuckDuckGo y Yandex no disponen de un motor asíncrono, por lo que no se debe diseñar una cola asíncrona compartida en torno a ellos.

"""Submit a google_async job, then poll for the finished SERP payload.

Usage:
  export WSA_API_KEY=...
  python google_async.py "events in new york city"
  python google_async.py "events in new york city" --dry-run
"""
import os
import random
import sys
import time

import requests

ENDPOINT = "https://serpapi.webscrapingapi.com/v2"


def submit(api_key, query, **extra):
    r = requests.get(ENDPOINT, params={"api_key": api_key, "engine": "google_async", "q": query, **extra}, timeout=60)
    r.raise_for_status()
    return r.json()["response_id"]          # store this id with your job record


def poll(api_key, response_id, max_wait=600):
    deadline = time.monotonic() + max_wait
    while time.monotonic() < deadline:
        r = requests.get(ENDPOINT, params={"api_key": api_key, "snapshot_id": response_id, "type": "serp"}, timeout=60)
        if r.ok:
            body = r.json()
            if body.get("status") in (None, "finished"):
                return body                  # persist it; don't re-poll old ids later
        elif r.status_code not in (429, 500, 502, 503):
            raise SystemExit(f"{r.status_code}: {r.text[:300]}")
        time.sleep(random.uniform(10, 30))   # docs: poll every 10 to 30 seconds
    raise SystemExit(f"No result for {response_id} after {max_wait} s")


if __name__ == "__main__":
    query = sys.argv[1] if len(sys.argv) > 1 else "events in new york city"
    if "--dry-run" in sys.argv:
        for p in ({"api_key": "REDACTED", "engine": "google_async", "q": query},
                  {"api_key": "REDACTED", "snapshot_id": "<response_id>", "type": "serp"}):
            print(requests.Request("GET", ENDPOINT, params=p).prepare().url)
        sys.exit(0)
    key = os.environ.get("WSA_API_KEY") or sys.exit("Set WSA_API_KEY first.")
    rid = submit(key, query)
    print("response_id:", rid)
    print(str(poll(key, rid))[:500])

Almacena de forma persistente cada resultado completado tan pronto como llegue, y no vuelvas más tarde a consultar ID antiguos. El bucle tiene un plazo límite estricto y trata cualquier estado inesperado como una parada, por lo que un trabajo atascado no puede retener a un trabajador indefinidamente.

Documentación: API asíncronas

¿Cómo debería gestionar tu código los errores y los reintentos de la API de SERP?

No vuelvas a intentar con errores 401 ni de validación. Corrige la clave, el plan o los parámetros. Reintenta los códigos de error 429, 500, 502 y 503 varias veces con retroceso exponencial y fluctuación, y reduce la frecuencia en el caso del 429. Un 422 significa que el motor de búsqueda ha devuelto un error, un tiempo de espera agotado o un desafío, por lo que solo debes reintentarlo una vez si el fallo parece transitorio.

Estado

Significado

Acción

401

Sin clave, clave no válida o clave sin autorización para la API de SERP

Corrija la clave o el plan. No vuelva a intentarlo.

Error de validación

Parámetro desconocido, ausente o fuera de rango para el motor seleccionado

Corrija la solicitud. No vuelva a intentarlo.

422 (error_type: "website")

El motor de búsqueda ha devuelto un cuerpo no válido, un desafío, un tiempo de espera agotado o un estado de error

Vuelve a intentarlo una vez con un intervalo de espera si parece un problema pasajero; a continuación, regístralo y omítelo

429

Demasiadas solicitudes simultáneas

Reducir la velocidad, aplicar un retroceso con variación de tiempo y volver a intentarlo

500, 503

Error inesperado del controlador o fallo transitorio del backend de autenticación

Reintentar varias veces con retrasos

502

El envío asíncrono ha recibido una respuesta no válida

Reintentar el envío con retardo

Los errores de autenticación son fáciles de reconocer. Una solicitud sin clave devuelve un 401 con el mensaje «Acceso denegado. No se ha proporcionado ninguna clave api_key proporcionada», y una clave incorrecta o sin derechos devuelve un 401 con Authentication failure: Invalid API key…. Volver a intentar cualquiera de los dos casos solo repite el fallo.

Los mensajes de validación indican el problema directamente: Parameter '<name>' not allowed., Parameter '<name>' is required., Parameter '<name>' must be in range …, y Parameter '<a>' cannot be used with parameter '<b>'. Por eso los parámetros copiados de ejemplos antiguos fallan en /v2: cada motor solo acepta sus propios campos documentados.

Un error 429 «Too Many Requests» significa que estás enviando demasiadas solicitudes a la vez. Reduce la concurrencia y espera un tiempo antes de volver a intentarlo. Para los códigos 500, 502 y 503, bastan unos pocos reintentos con retroceso exponencial y jitter completo, y los retrasos aleatorios evitan que un grupo de trabajadores vuelva a intentarlo al unísono. La search() función del cliente de Python implementa toda esta política: se detiene ante un 401, vuelve a intentar los códigos que admiten reintentos con pausas aleatorias, vuelve a intentar un 422 una vez y desiste tras cuatro intentos.

Registra dos encabezados de respuesta en cada llamada para el seguimiento de costes: Searchdata-Call-Credit-Usage (créditos utilizados por esta llamada) y Searchdata-Current-Credit-Usage (uso actual de la cuenta). El cliente los devuelve como usage. Registra el estado, los encabezados y el contexto de la solicitud, pero nunca la clave.

Separa los errores HTTP y los módulos vacíos en tus métricas. Una respuesta 200 sin related[] bloque supone una búsqueda correcta y no debería activar un reintento.

Documentación: Errores y encabezados

¿Cuánto cuesta la API de SERP?

Los planes de Google se clasifican según el número de búsquedas mensuales: Starter cuesta 28 $ por 10 000, Grow 130 $ por 50 000, Business 550 $ por 250 000 y Pro 1 600 $ por 1 000 000. La versión de prueba incluye 100 búsquedas en Google por 0 $. Bing, DuckDuckGo y Yandex tienen un precio que se establece mediante presupuesto en función de tu volumen de trabajo, a partir de octubre de 2026.

Plan

Precio mensual

Búsquedas en Google

Precio por cada 1 000

Prueba

0

100

—

Inicial

28 $

10 000

2,80 $

Crecimiento

130 $

50 000

2,60

Empresa

550 $

250 000

2,20

Pro

1.600 $

1 000 000

1,60 $

Bing, DuckDuckGo, Yandex

Presupuesto

Basado en la carga de trabajo

—

Los precios están en USD, a fecha de octubre de 2026. Todos los planes, incluido el de prueba, incluyen datos de productos de Google, ubicaciones globales, análisis de SERP, procesamiento masivo, todos los dispositivos, salida en JSON, CSV y HTML, y búsqueda de imágenes. Los planes difieren en el límite mensual de búsquedas.

Para calcular el tamaño de un plan, multiplica los componentes de tu cuadrícula de mercado:

Búsquedas mensuales = palabras clave × mercados × dispositivos × tipos de resultados × ejecuciones de actualización al mes

A continuación, añade un margen para las pruebas y para las palabras clave que añadas más adelante. Por ejemplo, 500 palabras clave × 2 países × 2 dispositivos × 4 ejecuciones semanales = 8.000 búsquedas al mes, lo que encaja perfectamente en el plan Starter con margen de sobra. Si pasas el mismo proyecto a ejecuciones diarias, el volumen asciende a unas 60 000 búsquedas, lo que te lleva al plan «Business». La frecuencia de actualización multiplica todos los demás factores, así que configúrala en función de la periodicidad con la que se toman realmente las decisiones que dependen de los datos.

Aprovecha el periodo de prueba para validar tus consultas representativas, mercados, dispositivos y campos de salida antes de comprometerte con un volumen determinado. Si también estás barajando otros proveedores, compara los precios de la API de SERP entre los distintos proveedores.

¿Qué es lo que no hace la API de SERP y en qué casos es mejor utilizar otra herramienta?

La API de SERP devuelve páginas de resultados de búsqueda, no las páginas a las que conducen cada uno de los enlaces, y no define una clasificación universal. La extracción de datos de DuckDuckGo y Yandex es tarea de tu código; la función asíncrona es exclusiva de Google, y las respuestas del modo IA de Google son un producto independiente. Utiliza la API Scraper, la API Data o un feed gestionado cuando esas limitaciones sean relevantes.

  • Se limita a la página de resultados. Para recuperar las páginas a las que conducen los enlaces, utiliza la API Scraper. Consulta cómo utilizar la API Scraper.
  • El modo IA es independiente. Para las respuestas, citas y menciones de marcas del modo IA de Google, utiliza los datos del modo IA de Google procedentes de la API de datos.
  • Se trata de una solicitud en tiempo real, no de un feed. Si necesitas registros recurrentes y controlados que se entreguen según un calendario en lugar de realizar las llamadas tú mismo, consulta los feeds de datos de resultados de búsqueda.
  • Solo se admiten los motores que figuran en la lista. Basa tu trabajo en los once motores de la referencia, no en páginas de documentación más antiguas.
  • Los motores HTML requieren mantenimiento. Los selectores de DuckDuckGo y Yandex deben ser mantenidos por ti cuando cambie su marcado.
  • El modo asíncrono es exclusivo de Google. Planifica trabajadores síncronos para Bing, DuckDuckGo y Yandex.
  • Un resultado equivale a una observación. Una clasificación describe una consulta, un mercado, un dispositivo y un momento concretos. Que una ausencia tenga relevancia comercial es decisión tuya, no de la API.
  • El uso está regulado. Recopila los resultados públicos elegibles de acuerdo con el Acuerdo de servicio de WebScrapingAPI y los requisitos que te sean aplicables.

Preguntas frecuentes

¿Qué es una API SERP?

Una API SERP es un servicio web que realiza una búsqueda por ti y devuelve la página de resultados del motor de búsqueda en forma de datos. Usted envía una consulta, un motor de búsqueda y datos de contexto como el país, el idioma y el dispositivo. La API SERP de WebScrapingAPI devuelve JSON analizado para Google y Bing, y HTML en un contenedor JSON para DuckDuckGo y Yandex.

¿Cómo se utiliza una API SERP?

Guarda tu clave API en una variable de entorno y, a continuación, envía una solicitud GET a https://serpapi.webscrapingapi.com/v2 con la clave API, el motor de búsqueda y tu consulta. Añade controles de mercado como «gl» y «hl» para Google, lee los resultados orgánicos de la respuesta JSON y guarda cada fila con su consulta, mercado, dispositivo y hora de captura.

¿Cómo consigo una clave de la API de SERP?

Regístrate en WebScrapingAPI y, a continuación, suscríbete a la API de SERP desde el panel de control para obtener tu clave. La versión de prueba incluye 100 búsquedas en Google. Guarda la clave en una variable de entorno o en un gestor de secretos en tu servidor, y nunca la incluyas en el código del navegador, en repositorios públicos, en registros ni en capturas de pantalla.

¿Hay una prueba gratuita de la API de SERP?

Sí. La versión de prueba de la API de SERP cuesta 0 $ e incluye 100 búsquedas en Google, suficientes para probar tus palabras clave, mercados, dispositivos y resultados antes de elegir un plan. Los planes de pago de Google empiezan en 28 $ al mes por 10 000 búsquedas. Los precios de Bing, DuckDuckGo y Yandex se establecen mediante presupuesto según tu carga de trabajo específica.

¿Qué es el «scraping» de SERP?

El «scraping» de SERP consiste en recopilar los resultados que muestra un motor de búsqueda para una consulta, como títulos, enlaces, posiciones y funciones como las búsquedas relacionadas. Hacerlo por tu cuenta implica gestionar bloqueos y cambios de diseño. Una API de SERP traslada ese trabajo al proveedor, de modo que tu código envía una solicitud y recibe los resultados en forma de datos.

¿Puedo utilizar la API de SERP con Python?

Sí. Instala Requests, lee tu clave desde una variable de entorno y pasa api_key, engine y q en el argumento params para que Requests los codifique. Establece un tiempo de espera de unos 120 segundos, reintenta las respuestas 429 y 5xx con retardo progresivo, y lee la lista de resultados orgánicos de la respuesta JSON de Google o Bing.

¿Qué motores de búsqueda admite la API SERP?

Es compatible con Google, Bing, DuckDuckGo y Yandex. Google también cuenta con motores especializados para la búsqueda asíncrona, Maps, reseñas de Maps, hoteles, vuelos, tendencias y búsqueda inversa de imágenes. Google y Bing devuelven JSON analizado. DuckDuckGo y Yandex devuelven el HTML de la página de resultados dentro de un contenedor JSON, que tu aplicación analiza.

¿Resuelve la API SERP los CAPTCHA?

Ninguna página de la API SERP lo garantiza. WebScrapingAPI gestiona la infraestructura de acceso a la búsqueda que hay detrás de cada solicitud y, cuando un motor de búsqueda devuelve una página de verificación, un estado de error o un tiempo de espera agotado, la API responde con un error 422 del sitio web en lugar de transmitir datos erróneos. Vuelve a intentarlo una vez si el fallo parece temporal y, a continuación, regístralo.

¿Por qué DuckDuckGo y Yandex devuelven HTML en lugar de resultados analizados?

En el caso de estos dos motores, la API SERP devuelve la página de resultados original en formato HTML dentro de un contenedor JSON, en lugar de colecciones analizadas. Tu aplicación elige qué extraer. Examina una respuesta real, asigna selectores CSS a ella, versiona dichos selectores, conserva el HTML sin procesar y avisa cuando un selector deje de coincidir tras un cambio en el diseño.

¿Cómo puedo realizar un seguimiento de las posiciones de las palabras clave con una API de SERP?

Elige un conjunto fijo de palabras clave, países, idiomas y dispositivos, y luego realiza consultas según una programación. Guarda la posición, el título y el enlace de cada resultado orgánico junto con la hora de captura y el mercado. Compara las ejecuciones solo dentro del mismo mercado y dispositivo, y trata las características de la SERP que falten como una observación, no como un error.

Empieza con 100 búsquedas gratuitas en Google

La API de SERP devuelve los resultados de Google y Bing como JSON analizado, y las páginas de resultados de DuckDuckGo y Yandex como HTML en un contenedor JSON, con controles de mercado, idioma y dispositivo en cada solicitud. Cuando conozcas tus palabras clave, mercados y calendario de actualización, compara los planes de la API de SERP y elige la cuota que mejor se adapte a tus necesidades.

Acerca del autor

Suciu Dan, Cofundador @ WebScrapingAPI

Suciu Dan

Cofundador

Suciu Dan es cofundador de WebScrapingAPI y escribe guías prácticas dirigidas a desarrolladores sobre el scraping web con Python, el scraping web con Ruby y las infraestructuras de proxy.

Extracción de datos web con AWS Lambda: guía para Python y Java 2026
Guías

Extracción de datos web con AWS Lambda: guía para Python y Java 2026

TL;DR: En resumen: el web scraping con AWS Lambda funciona mejor cuando cada invocación es breve, está delimitada y se puede reintentar de forma independiente. Empieza con HTTP directo, AWS SAM y S3, y añade SQS, contenedores, renderización en navegador, proxies o una capa de recuperación gestionada solo cuando la carga de trabajo demuestre que los necesita.

Suciu Dan33 min read
Leer artículo
Cómo utilizar GoSpider: rastrear, depurar URL y extraer datos
Guías

Cómo utilizar GoSpider: rastrear, depurar URL y extraer datos

TL;DR: En resumen: GoSpider es un rastreador de línea de comandos diseñado para descubrir URL, no un extractor completo de datos estructurados. Esta guía sobre cómo utilizar GoSpider muestra cómo realizar un rastreo limitado, gestionar correctamente los resultados, pasar los datos de Colly a CSV y seguir una ruta de diagnóstico para respuestas 403 o páginas que requieren renderización con JavaScript.

Suciu Dan24 min read
Leer artículo
Cómo raspar Redfin: Guía Python de Datos Inmobiliarios
Guías

Cómo raspar Redfin: Guía Python de Datos Inmobiliarios

TL;DR: Redfin expone puntos finales de API ocultos que devuelven JSON estructurado para los listados de propiedades, lo que permite omitir por completo el frágil análisis HTML. Esta guía te guía a través de la construcción de un raspador de Python que extrae datos de alquiler y venta, busca por ubicación, supervisa los nuevos listados a través de mapas de sitio XML y exporta resultados limpios a CSV o JSON.

Suciu Dan14 min read
Leer artículo

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.