Resposta rápida
Como chamo uma API de SERP em Python?
Envie uma requisição GET para https://serpcrypto.com/api/v1/search com requests ou httpx, sua chave no header Authorization: Bearer e a consulta em params, depois leia response.json(). SerpCrypto cobra 1 crédito por pesquisa bem-sucedida, a partir de US$ 0,30/1K pesquisas, e as retentativas de chamadas com falha são gratuitas. Pague com cripto, sem KYC. O código abaixo precisa apenas de requests ou httpx.
Configuração do Python em resumo
| Instalação | pip install requests ou pip install httpx |
|---|---|
| Chave | Variável de ambiente SERPCRYPTO_API_KEY |
| Header | Authorization: Bearer <key> |
| Tempo limite | 60 a 90 segundos: a pesquisa é executada ao vivo |
| Retentativa | Apenas 429, 502 e 504, com backoff; erros são gratuitos |
| Pesquisas em paralelo | Até concurrency_limit a partir de GET /credits |
Como configuro um cliente Python reutilizável?
Crie um requests.Session com a chave nos headers e uma política de retentativa para os três erros temporários. Uma sessão reutiliza conexões, então um loop de pesquisas gasta menos tempo em handshakes TLS. Salve o arquivo como serp_client.py; os próximos exemplos o importam.
Retry do urllib3 espera mais antes de cada nova tentativa e respeita um header Retry-After se ele for enviado. Ele nunca repete 400, 401 ou 402, que precisam de uma correção, não de uma segunda tentativa.
"""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())Como coleto mais de 10 resultados?
Peça as próximas páginas uma a uma. Cerca de 10 resultados orgânicos por página, como o Google vem entregando desde que deixou de respeitar num=100 em setembro de 2025; cada página extra é mais um crédito. Pare assim que pagination.next_page estiver ausente: não há mais nada a buscar, e você mantém os créditos.
Para a maior parte do trabalho de rastreamento de ranking, a primeira página ou as três primeiras páginas bastam, porque os cliques se concentram ali. Trinta resultados para uma palavra-chave custam três créditos.
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")Como executo muitas pesquisas em paralelo?
Use httpx.AsyncClient com um asyncio.Semaphore dimensionado para o limite da sua conta. O script lê concurrency_limit do endpoint de créditos gratuitos, então ele se adapta quando o suporte aumenta seu limite. Pesquisas acima do limite receberiam 429 de qualquer forma; o semáforo evita desperdiçar essas tentativas.
Cada tarefa repete erros temporários com uma pausa crescente e retorna None após quatro falhas, para que uma consulta teimosa nunca bloqueie o lote.
"""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"]))Como salvo os rankings diários em um arquivo CSV?
Acrescente uma linha por resultado, com o dia, a palavra-chave, a posição, o link e as configurações que a API aplicou. Ler gl e device de search_parameters em vez das suas próprias variáveis faz o arquivo registrar o que foi realmente usado.
Execute o script uma vez por dia com cron ou com o Agendador de Tarefas do Windows. Três palavras-chave no mobile custam três créditos por dia, cerca de 90 por mês.
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"],
})Qual cliente HTTP em Python devo usar?
Use requests para scripts e cron jobs: é simples e seu adaptador de retry é maduro. Use httpx quando precisar de código assíncrono, como um web app, um agente ou um lote de centenas de palavras-chave. Ambos enviam a mesma requisição e recebem o mesmo JSON; a API não se importa com qual deles você escolhe.
Evite timeouts muito curtos com qualquer uma das bibliotecas. Um cliente que desiste depois de cinco segundos pode reenviar uma pesquisa que ainda estava rodando e pagar pelas duas.
Quanto vai custar meu script?
Conte pesquisas, não resultados: uma chamada é um crédito quando dá certo. Um job de 500 palavras-chave, uma página cada, todos os dias por 30 dias, são 15.000 pesquisas. No pack 25.000 pesquisas (US$ 18,75) isso deixa folga de sobra, e Os créditos de pesquisa pré-pagos nunca expiram: as pesquisas não utilizadas ficam na sua conta até você usá-las.
Confira o saldo pelo código com o helper credits() acima; isso não custa nada.
Perguntas sobre Python
Existe um pacote SerpCrypto no PyPI?
Não. Os exemplos chamam o endpoint HTTPS com requests ou httpx, duas bibliotecas amplamente usadas, então não há nada nosso para instalar ou atualizar. O cliente acima tem cerca de 40 linhas e é seu.
Quais versões do Python este código suporta?
Python 3.9 e superiores. O exemplo assíncrono usa asyncio.run, disponível desde o Python 3.7, e o restante usa apenas a biblioteca padrão mais requests ou httpx.
Posso usar a API a partir de um notebook Jupyter?
Sim. Defina SERPCRYPTO_API_KEY antes de iniciar o Jupyter, ou leia a chave com getpass dentro do notebook para que ela não acabe em uma célula salva. Em um notebook, use await main(...) em vez de asyncio.run(main(...)).
Como transformo os resultados em um DataFrame do pandas?
Passe a lista de dicionários de resultado para pandas.DataFrame(data["organic_results"]). Cada campo vira uma coluna; campos opcionais ausentes, como date, viram células vazias.