Szybka odpowiedź
Jak wywołać API SERP z Pythona?
Wyślij zapytanie GET do https://serpcrypto.com/api/v1/search z requests lub httpx, kluczem w nagłówku Authorization: Bearer i zapytaniem w params, a następnie odczytaj response.json(). SerpCrypto pobiera 1 kredyt za udane wyszukiwanie, od 0,30 USD/1K wyszukiwań, a ponowienia nieudanych wywołań są bezpłatne. Płatność kryptowalutą, bez KYC. Poniższy kod wymaga tylko requests lub httpx.
Konfiguracja Pythona w skrócie
| Instalacja | pip install requests lub pip install httpx |
|---|---|
| Klucz | Zmienna środowiskowa SERPCRYPTO_API_KEY |
| Nagłówek | Authorization: Bearer <key> |
| Limit czasu | 60 do 90 sekund: wyszukiwanie odbywa się na żywo |
| Ponawianie | Tylko 429, 502 i 504, z wykładniczym opóźnieniem; błędy są bezpłatne |
| Równoległe wyszukiwania | Do concurrency_limit od GET /credits |
Jak skonfigurować klienta wielokrotnego użytku w Pythonie?
Utwórz jeden requests.Session z kluczem w nagłówkach i polityką ponawiania dla trzech błędów tymczasowych. Sesja ponownie wykorzystuje połączenia, więc pętla wyszukiwań zużywa mniej czasu na uzgadnianie TLS. Zapisz plik jako serp_client.py; kolejne przykłady go importują.
Retry z urllib3 czeka dłużej przed każdą nową próbą i respektuje nagłówek Retry-After, jeśli zostanie wysłany. Nigdy nie ponawia 400, 401 ani 402, które wymagają poprawki, a nie drugiej próby.
"""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())Jak zebrać więcej niż 10 wyników?
Poproś o kolejne strony jedna po drugiej. Około 10 wyników organicznych na stronę, tak jak Google serwuje od czasu, gdy we wrześniu 2025 przestało respektować num=100; każda dodatkowa strona to jeden kredyt więcej. Zatrzymaj się, gdy tylko brakuje pagination.next_page: nie ma już czego pobierać, a kredyty zachowujesz.
W większości prac związanych ze śledzeniem pozycji wystarczy pierwsza strona lub pierwsze trzy strony, bo tam koncentrują się kliknięcia. Trzydzieści wyników dla jednego słowa kluczowego kosztuje trzy kredyty.
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")Jak uruchomić wiele wyszukiwań równolegle?
Użyj httpx.AsyncClient z asyncio.Semaphore dopasowanym do limitu Twojego konta. Skrypt odczytuje concurrency_limit z endpointu bezpłatnych kredytów, więc dostosowuje się, gdy support podniesie Twój limit. Wyszukiwania powyżej limitu i tak dostałyby 429; semafor pozwala uniknąć marnowania tych prób.
Każde zadanie ponawia błędy tymczasowe z rosnącą pauzą i zwraca None po czterech niepowodzeniach, więc jedno uparte zapytanie nigdy nie blokuje całej partii.
"""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"]))Jak zapisać dzienne pozycje do pliku CSV?
Dodaj jeden wiersz na wynik, z dniem, słowem kluczowym, pozycją, linkiem i ustawieniami zastosowanymi przez API. Odczyt gl i device z search_parameters, a nie z własnych zmiennych, sprawia, że plik zapisuje to, co faktycznie zostało użyte.
Uruchamiaj skrypt raz dziennie przez cron lub Windows Task Scheduler. Trzy słowa kluczowe na mobile kosztują trzy kredyty dziennie, około 90 miesięcznie.
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"],
})Którego klienta HTTP w Pythonie powinienem użyć?
Użyj requests do skryptów i zadań cron: jest prosty, a jego adapter ponawiania jest dojrzały. Użyj httpx, gdy potrzebujesz kodu asynchronicznego, np. aplikacji webowej, agenta lub partii setek słów kluczowych. Oba wysyłają to samo zapytanie i otrzymują ten sam JSON; API nie dba o to, który wybierzesz.
Unikaj bardzo krótkich limitów czasu w obu bibliotekach. Klient, który poddaje się po pięciu sekundach, może ponownie wysłać wyszukiwanie, które wciąż trwało, i zapłacić za oba.
Ile będzie kosztował mój skrypt?
Licz wyszukiwania, nie wyniki: jedno wywołanie to jeden kredyt, gdy się powiedzie. Zadanie 500 słów kluczowych, po jednej stronie każdego, codziennie przez 30 dni, to 15 000 wyszukiwań. Na pakiecie 25 000 wyszukiwań (18,75 USD) zostaje zapas, a Prepaid kredyty wyszukiwania nigdy nie wygasają: niewykorzystane wyszukiwania pozostają na Twoim koncie, dopóki ich nie wykorzystasz.
Sprawdź saldo z kodu za pomocą helpera credits() powyżej; nic to nie kosztuje.
Pytania o Python
Czy jest pakiet SerpCrypto na PyPI?
Nie. Przykłady wywołują endpoint HTTPS za pomocą requests lub httpx, dwóch powszechnie używanych bibliotek, więc nie ma niczego naszego do zainstalowania ani aktualizacji. Powyższy klient ma około 40 linii i należy do Ciebie.
Które wersje Pythona obsługuje ten kod?
Python 3.9 i nowsze. Przykład asynchroniczny używa asyncio.run, dostępnego od Pythona 3.7, a reszta korzysta tylko z biblioteki standardowej plus requests lub httpx.
Czy mogę używać API z notebooka Jupyter?
Tak. Ustaw SERPCRYPTO_API_KEY przed uruchomieniem Jupyter lub odczytaj klucz za pomocą getpass w notebooku, żeby nie trafił do zapisanej komórki. W notebooku użyj await main(...) zamiast asyncio.run(main(...)).
Jak zamienić wyniki w pandas DataFrame?
Przekaż listę słowników wyników do pandas.DataFrame(data["organic_results"]). Każde pole staje się kolumną; brakujące pola opcjonalne, takie jak date, stają się pustymi komórkami.