Resposta rápida
Como uso uma API de SERP em Node.js?
Chame fetch("https://serpcrypto.com/api/v1/search?" + new URLSearchParams({ q })) com o header Authorization: Bearer <key> e leia await res.json(). O Node.js 18+ não precisa de pacote. SerpCrypto retorna resultados do Google ou do Bing em JSON por 1 crédito por pesquisa bem-sucedida, a partir de US$ 0,30/1K pesquisas, e nunca cobra erros. Pagamento em cripto, sem KYC.
Configuração do Node.js em resumo
| Runtime | Node.js 18 ou posterior (fetch, AbortSignal.timeout embutidos) |
|---|---|
| Módulos | Módulos ES: arquivos .mjs ou "type": "module" no package.json |
| Chave | process.env.SERPCRYPTO_API_KEY |
| Tempo limite | 90 segundos com AbortSignal.timeout(90_000) |
| Pesquisas em paralelo | Um pool dimensionado para concurrency_limit |
| Tipos | typedefs JSDoc, lidos pelo VS Code e pelo tsc --checkJs |
Como escrevo um pequeno cliente tipado?
Coloque o endpoint, o header e o tratamento de erros em um único módulo. As linhas @typedef do JSDoc descrevem a resposta, então os editores autocompletam organic_results e verificam os tipos do seu código sem etapa de build. Quem usa TypeScript pode copiar as mesmas formas para uma interface.
A função search lança um Error com o status HTTP e o code de erro da API na mensagem. Seu código pode então decidir repetir em 429, 502 e 504 e parar nos demais, como explica a página de erros].
// 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();
}Como executo pesquisas em paralelo sem tomar 429?
Inicie tantos workers quanto sua conta permitir e deixe cada um pegar a próxima palavra-chave de uma lista compartilhada. O pool lê concurrency_limit do endpoint de créditos gratuitos, então nunca envia mais pesquisas ao mesmo tempo do que a conta aceita.
Uma palavra-chave que falha é registrada e o worker segue em frente. Reprocesse as falhas depois: erros não custam nada, então uma segunda passada é gratuita até dar certo.
// 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));Como busco várias páginas de resultados?
Percorra page e pare assim que pagination.next_page estiver ausente. Cerca de 10 resultados orgânicos por página, como o Google vem entregando desde que deixou de respeitar num=100 em setembro de 2025; cada página extra é mais um crédito. Uma verificação de três páginas de uma palavra-chave custa três créditos; uma palavra-chave com apenas uma página de resultados custa um.
// 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 chamar a API a partir de um navegador ou de uma edge function?
De um servidor ou de uma edge function, sim: Cloudflare Workers, Vercel e Deno oferecem o mesmo fetch. De uma página no navegador, não: a chave ficaria visível para todos os visitantes. Coloque no meio uma pequena rota de servidor que guarda a chave e encaminha apenas os parâmetros que você permitir.
A mesma regra vale para apps mobile. Tudo o que vai para o dispositivo do usuário pode ser lido, então as chaves ficam nos seus servidores.
Como mantenho os custos previsíveis em Node.js?
Conte as chamadas que faz: uma chamada bem-sucedida é um crédito, a US$ 0,56/1K pesquisas no pack 100.000 pesquisas. Verifique credits_remaining antes de um lote grande e pare o pool quando estiver perto de zero, em vez de acumular uma série de respostas 402. Os créditos de pesquisa pré-pagos nunca expiram: as pesquisas não utilizadas ficam na sua conta até você usá-las, então o restante não usado fica para o próximo job.
Perguntas sobre Node.js
Existe um pacote npm do SerpCrypto?
Não. O cliente acima tem cerca de 40 linhas de JavaScript padrão usando fetch, então não há nada nosso para instalar. Copie para o seu projeto e adapte.
Isso funciona com CommonJS (require)?
Os exemplos usam módulos ES para await no topo. Em um arquivo CommonJS, envolva as chamadas em uma função async e use fetch da mesma forma; ele é global no Node.js 18+.
Como uso com TypeScript?
Copie as formas do JSDoc para uma interface SearchResponse e tipar o resultado de res.json() com ela. Blocos opcionais como organic_results? devem continuar opcionais, porque uma página pode não exibi-los.
Qual versão do Node.js eu preciso?
Node.js 18 ou superior para o fetch nativo. AbortSignal.timeout está disponível a partir do Node.js 17.3, então o 18 cobre tudo nesta página.