Szybka odpowiedź
Jak używać API SERP w Node.js?
Wywołaj fetch("https://serpcrypto.com/api/v1/search?" + new URLSearchParams({ q })) z nagłówkiem Authorization: Bearer <key> i odczytaj await res.json(). Node.js 18+ nie wymaga żadnego pakietu. SerpCrypto zwraca wyniki Google lub Bing jako JSON za 1 kredyt za udane wyszukiwanie, od 0,30 USD/1K wyszukiwań, i nigdy nie nalicza za błędy. Płatność kryptowalutą, bez KYC.
Konfiguracja Node.js w skrócie
| Środowisko uruchomieniowe | Node.js 18 lub nowszy (wbudowany fetch, AbortSignal.timeout) |
|---|---|
| Moduły | Moduły ES: pliki .mjs lub "type": "module" w package.json |
| Klucz | process.env.SERPCRYPTO_API_KEY |
| Limit czasu | 90 sekund z AbortSignal.timeout(90_000) |
| Równoległe wyszukiwania | Pula o rozmiarze concurrency_limit |
| Typy | Typedefy JSDoc, odczytywane przez VS Code i tsc --checkJs |
Jak napisać mały klient z typami?
Umieść endpoint, header i obsługę błędów w jednym module. Wiersze JSDoc @typedef opisują odpowiedź, więc edytory autouzupełniają organic_results i sprawdzają typy w Twoim kodzie bez kroku budowania. Użytkownicy TypeScript mogą skopiować te same kształty do interfejsu.
Funkcja search rzuca Error ze statusem HTTP i komunikatem błędu code z API. Twój kod może wtedy zdecydować o ponowieniu 429, 502 i 504 oraz o zatrzymaniu przy pozostałych, jak wyjaśnia strona błędów].
// serp-client.mjs — Node.js 18+, no dependency
const API_URL = "https://serpcrypto.com/api/v1/search";
const CREDITS_URL = "https://serpcrypto.com/api/v1/credits";
const headers = { Authorization: `Bearer ${process.env.SERPCRYPTO_API_KEY}` };
/**
* @typedef {Object} OrganicResult
* @property {number} position
* @property {string} title
* @property {string} link
* @property {string} [snippet]
* @property {string} [date]
*/
/**
* @typedef {Object} SearchResponse
* @property {Object} search_metadata
* @property {string} search_metadata.id
* @property {string} search_metadata.status
* @property {number} search_metadata.credits_used
* @property {OrganicResult[]} [organic_results]
* @property {Object} [pagination]
* @property {number} pagination.current
* @property {number} [pagination.next_page]
*/
/**
* Run one search (1 credit when it succeeds).
* @param {string} q
* @param {Record<string, string | number>} [params]
* @returns {Promise<SearchResponse>}
*/
export async function search(q, params = {}) {
const query = new URLSearchParams({ q, ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])) });
const res = await fetch(`${API_URL}?${query}`, { headers, signal: AbortSignal.timeout(90_000) });
if (!res.ok) {
const body = await res.json().catch(() => ({}));
const err = new Error(`${res.status} ${body.error?.code ?? "error"}: ${body.error?.message ?? ""}`);
err.status = res.status;
throw err;
}
return res.json();
}
/** Free: credits left, used today and the concurrency limit. */
export async function credits() {
const res = await fetch(CREDITS_URL, { headers });
if (!res.ok) throw new Error(`credits: HTTP ${res.status}`);
return res.json();
}Jak uruchamiać wyszukiwania równolegle bez trafiania na 429?
Uruchom tyle workerów, ile pozwala Twoje konto, i pozwól każdemu pobierać kolejne słowo kluczowe ze współdzielonej listy. Pula odczytuje concurrency_limit z endpointu darmowych kredytów, więc nigdy nie wysyła naraz więcej wyszukiwań, niż akceptuje konto.
Nieudane słowo kluczowe jest zapisywane, a worker przechodzi dalej. Uruchom ponownie nieudane później: błędy nic nie kosztują, więc druga próba jest darmowa, dopóki się nie powiedzie.
// batch.mjs — parallel searches, never more than the account's limit at once
import { search, credits } from "./serp-client.mjs";
const keywords = ["password manager", "note taking app", "time tracking software", "invoice software"];
const { concurrency_limit: limit } = await credits();
let next = 0;
const results = new Map();
async function worker() {
while (next < keywords.length) {
const kw = keywords[next++];
try {
const data = await search(kw, { gl: "us", hl: "en" });
results.set(kw, data.organic_results?.[0]?.link ?? null);
} catch (err) {
results.set(kw, `failed: ${err.message}`);
}
}
}
await Promise.all(Array.from({ length: Math.min(limit, keywords.length) }, worker));
console.table(Object.fromEntries(results));Jak pobrać kilka stron wyników?
Iteruj po page i zatrzymaj się, gdy tylko zabraknie pagination.next_page. 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. Sprawdzenie trzech stron jednego słowa kluczowego kosztuje trzy kredyty; słowo kluczowe z tylko jedną stroną wyników kosztuje jeden.
// pages.mjs — up to three pages, stop when results end
import { search } from "./serp-client.mjs";
async function topResults(q, pages = 3, params = {}) {
const rows = [];
for (let page = 1; page <= pages; page++) {
const data = await search(q, { ...params, page });
for (const r of data.organic_results ?? []) rows.push({ page, position: r.position, link: r.link });
if (!data.pagination?.next_page) break; // nothing more: save the credits
}
return rows;
}
console.log(await topResults("headless cms", 3, { gl: "de", hl: "en" }));Czy mogę wywoływać API z przeglądarki lub funkcji edge?
Z serwera lub funkcji edge — tak: Cloudflare Workers, Vercel i Deno zapewniają to samo fetch. Ze strony w przeglądarce — nie: klucz byłby widoczny dla każdego odwiedzającego. Wstaw między nimi małą trasę serwerową, która trzyma klucz i przekazuje tylko dozwolone parametry.
Ta sama zasada dotyczy aplikacji mobilnych. Wszystko, co trafia na urządzenie użytkownika, można odczytać, więc klucze zostają na Twoich serwerach.
Jak utrzymać przewidywalne koszty w Node.js?
Licz wykonywane wywołania: jedno udane wywołanie to jeden kredyt, po 0,56 USD/1K wyszukiwań w packu 100 000 wyszukiwań. Sprawdź credits_remaining przed dużym batchem i zatrzymaj pulę, gdy zbliża się do zera, zamiast zbierać serię odpowiedzi 402. Prepaid kredyty wyszukiwania nigdy nie wygasają: niewykorzystane wyszukiwania pozostają na Twoim koncie, dopóki ich nie wykorzystasz, więc niewykorzystana reszta zostaje na następne zadanie.
Pytania o Node.js
Czy istnieje pakiet npm dla SerpCrypto?
Nie. Powyższy klient to około 40 linii standardowego JavaScriptu korzystającego z fetch, więc nie ma niczego naszego do instalacji. Skopiuj go do swojego projektu i dostosuj.
Czy to działa z CommonJS (require)?
Przykłady używają modułów ES do await na najwyższym poziomie. W pliku CommonJS opakuj wywołania w funkcję async i używaj fetch w ten sam sposób; jest globalne w Node.js 18+.
Jak używać tego z TypeScript?
Skopiuj kształty JSDoc do interface SearchResponse i typuj nim wynik res.json(). Opcjonalne bloki, takie jak organic_results?, muszą pozostać opcjonalne, ponieważ strona może ich nie pokazywać.
Jakiej wersji Node.js potrzebuję?
Node.js 18 lub nowszego dla wbudowanego fetch. AbortSignal.timeout jest dostępne od Node.js 17.3, więc 18 obejmuje wszystko z tej strony.