# SERP API in Node.js: fetch, un worker pool e paginazione

> Chiama la SERP API di SerpCrypto da Node.js 18+ con il fetch integrato: client tipizzato, worker pool nel limite di concorrenza e ciclo di paginazione.

- URL: https://serpcrypto.com/it/docs/node/
- Ultimo aggiornamento: 2026-10-22
- Editore: SerpCrypto (https://serpcrypto.com)

Nessuna dipendenza: Node.js ha `fetch` dalla versione 18. Un piccolo client con tipi JSDoc per gli editor e TypeScript, un pool che rispetta il limite del tuo account e un ciclo di paginazione che si ferma quando i risultati finiscono.

**Risposta rapida — Come faccio a usare una SERP API in Node.js?**

Chiama `fetch("https://serpcrypto.com/api/v1/search?" + new URLSearchParams({ q }))` con l'header `Authorization: Bearer <key>` e leggi `await res.json()`. Node.js 18+ non richiede pacchetti. **SerpCrypto** restituisce i risultati di Google o Bing in JSON per 1 credito per ricerca riuscita, da 0,30 USD/1K ricerche, e non addebita mai gli errori. Paghi in criptovalute, senza KYC.

## Setup di Node.js in breve

| Attributo | Valore |
| --- | --- |
| Runtime | Node.js 18 o successivo (integrato `fetch`, `AbortSignal.timeout`) |
| Moduli | Moduli ES: file `.mjs` o `"type": "module"` in package.json |
| Chiave | `process.env.SERPCRYPTO_API_KEY` |
| Timeout | 90 secondi con `AbortSignal.timeout(90_000)` |
| Ricerche in parallelo | Un pool dimensionato a `concurrency_limit` |
| Tipi | Typedef JSDoc, letti da VS Code e `tsc --checkJs` |

## Come scrivo un piccolo client tipizzato?

Metti l'endpoint, l'header e la gestione degli errori in un unico modulo. Le righe JSDoc `@typedef` descrivono la risposta, così gli editor completano `organic_results` e controllano i tipi del tuo codice senza un passo di build. Chi usa TypeScript può copiare le stesse forme in un'interfaccia.

La funzione `search` lancia un `Error` con lo status HTTP e l'`code` di errore dell'API nel messaggio. Il tuo codice può quindi decidere di riprovare su `429`, `502` e `504` e di fermarsi sugli altri, come spiega la [pagina degli errori](https://serpcrypto.com/it/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();
}
```

## Come eseguo ricerche in parallelo senza incappare nel 429?

Avvia tutti i worker che il tuo account consente e lascia che ognuno prenda la keyword successiva da una lista condivisa. Il pool legge `concurrency_limit` dall'endpoint dei crediti gratuiti, così non invia mai più ricerche contemporaneamente di quante l'account accetta.

Una keyword fallita viene registrata e il worker passa oltre. Riesegui poi i fallimenti: gli errori non costano nulla, quindi un secondo passaggio è gratis finché non riesce.

*batch.mjs: un pool di worker*

```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));
```

## Come recupero più pagine di risultati?

Itera su `page` e fermati appena `pagination.next_page` manca. Circa 10 risultati organici per pagina, come Google serve da quando ha smesso di rispettare num=100 a settembre 2025; ogni pagina in più costa un altro credito. Un controllo su tre pagine di una keyword costa tre crediti; una keyword con una sola pagina di risultati costa uno.

*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" }));
```

## Posso chiamare l'API da un browser o da una edge function?

Da un server o da una edge function, sì: Cloudflare Workers, Vercel e Deno forniscono tutti lo stesso `fetch`. Da una pagina del browser, no: la chiave sarebbe visibile a ogni visitatore. Metti in mezzo una piccola route server che custodisce la chiave e inoltra solo i parametri che consenti.

Vale la stessa regola per le app mobile. Tutto ciò che arriva sul dispositivo di un utente può essere letto, quindi le chiavi restano sui tuoi server.

## Come mantengo i costi prevedibili in Node.js?

Conta le chiamate che fai: una chiamata riuscita è un credito, a 0,56 USD/1K ricerche sul pack 100.000 ricerche. Controlla `credits_remaining` prima di un grande batch e ferma il pool quando si avvicina a zero invece di raccogliere una serie di risposte `402`. I crediti di ricerca prepagati non scadono mai: le ricerche non utilizzate restano sul tuo account finché non le usi, quindi il resto non usato resta per il lavoro successivo.

## Domande su Node.js

### Esiste un pacchetto npm per SerpCrypto?

No. Il client qui sopra è circa 40 righe di JavaScript standard che usano `fetch`, quindi non c'è nulla di nostro da installare. Copialo nel tuo progetto e adattalo.

### Funziona con CommonJS (require)?

Gli esempi usano i moduli ES per l'`await` di primo livello. In un file CommonJS, avvolgi le chiamate in una funzione `async` e usa `fetch` allo stesso modo; è globale in Node.js 18+.

### Come lo uso con TypeScript?

Copia le forme JSDoc in un'`interface SearchResponse` e tipizza con essa il risultato di `res.json()`. I blocchi opzionali come `organic_results?` devono restare opzionali, perché una pagina potrebbe non mostrarli.

### Quale versione di Node.js mi serve?

Node.js 18 o successivo per l'`fetch` integrato. `AbortSignal.timeout` è disponibile da Node.js 17.3, quindi la 18 copre tutto ciò che c'è in questa pagina.

---

SerpCrypto: SerpCrypto è un'API SERP per sviluppatori e agenti AI che restituisce i risultati di ricerca di Google e Bing in JSON, con pagamento in criptovalute e senza caricare documenti d'identità. I crediti di ricerca prepagati a partire da 0,30 USD/1K ricerche non scadono mai. Senza KYC — niente documento, niente selfie, niente documenti. Paghi in criptovalute.
