# API SERP w Node.js: fetch, pula workerów i paginacja

> Wywołaj API SERP SerpCrypto z Node.js 18+ przez wbudowany fetch: typowany klient, pula workerów w granicach limitu współbieżności i pętla paginacji.

- URL: https://serpcrypto.com/pl/docs/node/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: SerpCrypto (https://serpcrypto.com)

Bez zależności: Node.js ma wbudowany `fetch` od wersji 18. Mały klient z typami JSDoc dla edytorów i TypeScript, pula respektująca limit twojego konta i pętla paginacji, która kończy się, gdy wyniki się skończą.

**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

| Atrybut | Wartość |
| --- | --- |
| Ś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](https://serpcrypto.com/pl/docs/errors/)].

*serp-client.mjs*

```javascript
// 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: pula workerów*

```javascript
// 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*

```javascript
// 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.

---

SerpCrypto: SerpCrypto to API SERP dla deweloperów i agentów AI, które zwraca wyniki wyszukiwania Google i Bing w formacie JSON, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty wyszukiwania od 0,30 USD/1K wyszukiwań nigdy nie wygasają. Bez KYC — bez dokumentu, bez selfie, bez dokumentów. Płatność kryptowalutą.
