Snel antwoord
Hoe gebruik ik een SERP-API in Node.js?
Roep fetch("https://serpcrypto.com/api/v1/search?" + new URLSearchParams({ q })) aan met de header Authorization: Bearer <key> en lees await res.json(). Node.js 18+ heeft geen package nodig. SerpCrypto geeft Google- of Bing-zoekresultaten als JSON voor 1 credit per geslaagde zoekopdracht, vanaf US$ 0,30/1K zoekopdrachten, en brengt errors nooit in rekening. Betalen met crypto, zonder KYC.
Node.js-setup in één oogopslag
| Runtime | Node.js 18 of later (ingebouwde fetch, AbortSignal.timeout) |
|---|---|
| Modules | ES modules: .mjs-bestanden of "type": "module" in package.json |
| Sleutel | process.env.SERPCRYPTO_API_KEY |
| Timeout | 90 seconden met AbortSignal.timeout(90_000) |
| Parallelle zoekopdrachten | Een pool afgestemd op concurrency_limit |
| Types | JSDoc-typedefs, gelezen door VS Code en tsc --checkJs |
Hoe schrijf ik een kleine getypeerde client?
Zet het endpoint, de header en de foutafhandeling in één module. De JSDoc-regels @typedef beschrijven het antwoord, zodat editors organic_results automatisch aanvullen en je code type-checken zonder build-stap. TypeScript-gebruikers kunnen dezelfde shapes in een interface kopiëren.
De functie search gooit een Error met de HTTP-status en de fout-code van de API in het bericht. Je code kan dan beslissen om 429, 502 en 504 opnieuw te proberen en bij de andere te stoppen, zoals de foutenpagina uitlegt.
// 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();
}Hoe voer ik zoekopdrachten parallel uit zonder een 429 te krijgen?
Start zoveel workers als je account toestaat en laat elke worker het volgende keyword uit een gedeelde lijst pakken. De pool leest concurrency_limit uit het endpoint voor gratis credits, zodat hij nooit meer zoekopdrachten tegelijk verstuurt dan het account accepteert.
Een mislukt keyword wordt vastgelegd en de worker gaat verder. Draai de mislukte opdrachten daarna opnieuw: fouten kosten niets, dus een tweede ronde is gratis tot het lukt.
// 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));Hoe haal ik meerdere pagina's met resultaten op?
Loop over page en stop zodra pagination.next_page ontbreekt. Ongeveer 10 organische resultaten per pagina, zoals Google levert sinds het in september 2025 stopte met num=100; elke extra pagina kost nog een credit. Een controle van drie pagina's voor één keyword kost drie credits; een keyword met maar één pagina aan resultaten kost één.
// 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" }));Kan ik de API aanroepen vanuit een browser of een edge function?
Vanuit een server of edge function wel: Cloudflare Workers, Vercel en Deno bieden allemaal dezelfde fetch. Vanuit een browserpagina niet: de API-sleutel zou zichtbaar zijn voor elke bezoeker. Zet ertussen een kleine server-route die de sleutel bevat en alleen de parameters doorstuurt die je toestaat.
Dezelfde regel geldt voor mobiele apps. Alles wat naar het apparaat van een gebruiker gaat, kan worden gelezen, dus sleutels blijven op je servers.
Hoe houd ik de kosten voorspelbaar in Node.js?
Tel de calls die je doet: één geslaagde call is één credit, tegen US$ 0,56/1K zoekopdrachten op het 100.000 zoekopdrachten-pack. Controleer credits_remaining vóór een grote batch en stop de pool wanneer die bijna op nul staat, in plaats van een reeks 402-antwoorden te verzamelen. Prepaid zoekcredits verlopen nooit: ongebruikte zoekopdrachten blijven op je account staan tot je ze gebruikt, dus een ongebruikt restant blijft bewaard voor de volgende klus.
Node.js-vragen
Is er een npm-package voor SerpCrypto?
Nee. De client hierboven is ongeveer 40 regels standaard JavaScript met fetch, dus er is niets van ons te installeren. Kopieer hem naar je project en pas hem aan.
Werkt dit met CommonJS (require)?
De voorbeelden gebruiken ES modules voor top-level await. Wikkel in een CommonJS-bestand de calls in een functie async en gebruik fetch op dezelfde manier; die is globaal in Node.js 18+.
Hoe gebruik ik het met TypeScript?
Kopieer de JSDoc-shapes naar een interface SearchResponse en typeer het resultaat van res.json() ermee. Optionele blokken zoals organic_results? moeten optioneel blijven, omdat een pagina ze niet altijd toont.
Welke Node.js-versie heb ik nodig?
Node.js 18 of later voor de ingebouwde fetch. AbortSignal.timeout is beschikbaar vanaf Node.js 17.3, dus 18 dekt alles op deze pagina.