Resposta rápida
Como chamo uma API de pesquisa na web para agentes de IA a partir do Node.js?
Use o fetch nativo do Node 18+ para enviar sua consulta por POST para https://llmwebsearch.com/api/v1/search com uma chave Bearer. LLMWebSearch responde com resultados classificados e as 3 principais páginas em Markdown, prontas para numerar e passar a um modelo para uma resposta com citações. Uma pesquisa custa 1 crédito, a partir de US$ 1,20/1K pesquisas em outubro de 2026. Nenhum pacote npm é necessário. Sem KYC; pague com cripto.
O que você precisa
| Node.js | 18 ou mais recente, para o fetch nativo; salve os scripts como .mjs para await de nível superior |
|---|---|
| Pacotes | Nenhum |
| Chave | LLMWEBSEARCH_API_KEY no seu ambiente |
| Para o loop de resposta | Um endpoint de chat compatível com OpenAI: LLM_BASE_URL, LLM_API_KEY, LLM_MODEL |
| Cobrança | Um crédito paga uma pesquisa: até 20 resultados classificados, com as páginas dos 3 primeiros lidas e devolvidas em Markdown. Ler de 4 a 10 páginas custa 2 créditos. Uma chamada que termina em erro não custa nada |
Como escrevo uma função de pesquisa reutilizável?
Envolva a chamada uma vez e reutilize em todo lugar. A função abaixo envia o body, lança um erro legível em qualquer falha HTTP e retorna o JSON analisado. Os erros carregam o próprio code da API, como rate_limited ou insufficient_credits, para que quem chama decida o que fazer.
const API = 'https://llmwebsearch.com/api/v1/search';
export async function search(body) {
const res = await fetch(API, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await res.json();
if (!res.ok) {
const err = new Error(`${res.status} ${data.error?.code}: ${data.error?.message}`);
err.status = res.status;
throw err;
}
return data;
}
const data = await search({ q: 'how to size a postgres connection pool', read: 3 });
for (const r of data.results) {
console.log(r.position, r.content_status, r.url, r.word_count ?? '');
}
Como faço várias pesquisas sem atingir o rate limit?
Execute-as através de um pequeno pool: um número fixo de workers, cada um pega a próxima consulta de uma lista compartilhada. Quatro workers é um bom começo. Toda conta tem um limite de chamadas por minuto, exibido no dashboard; peça ao suporte e ele pode ser aumentado.
O pool abaixo não precisa de biblioteca. Cada worker mantém seus próprios resultados, e uma pesquisa com falha é registrada com seu erro em vez de interromper a execução. Isso importa para agentes de pesquisa, onde uma subpergunta ruim não deve custar a tarefa inteira.
const API = 'https://llmwebsearch.com/api/v1/search';
const questions = [
'what is connection pooling in postgres',
'recommended pool size per cpu core',
'pgbouncer transaction mode limitations',
'connection pool exhaustion symptoms',
'pool size for serverless functions',
];
async function search(q) {
const res = await fetch(API, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ q, read: 2, max_chars: 4000 }),
});
const data = await res.json();
if (!res.ok) return { q, error: data.error?.code ?? String(res.status) };
return { q, urls: data.results.filter((r) => r.content_status === 'ok').map((r) => r.url) };
}
async function runPool(items, size, task) {
const out = [];
let next = 0;
const worker = async () => {
while (next < items.length) {
const i = next++;
out[i] = await task(items[i]);
}
};
await Promise.all(Array.from({ length: size }, worker));
return out;
}
for (const row of await runPool(questions, 4, search)) console.log(row);
Como transformo resultados em uma resposta com citações?
Numere as fontes, coloque a URL ao lado de cada uma, corte-as conforme um limite e peça ao modelo que cite por número. A API retorna fontes, não uma resposta escrita: o seu próprio modelo lê as páginas, escreve a resposta e cita as URLs. O script abaixo fala com qualquer endpoint chat/completions compatível com OpenAI, escolhido com três variáveis de ambiente, então funciona tanto com modelos hospedados quanto locais.
O limite de cerca de 24.000 caracteres, aproximadamente 6.000 tokens, é dividido igualmente entre as fontes. Um resultado cuja página não pôde ser lida é passado como seu snippet, marcado como tal.
const SEARCH = 'https://llmwebsearch.com/api/v1/search';
const BUDGET = 24000; // characters, about 6,000 tokens
async function sources(question) {
const res = await fetch(SEARCH, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLMWEBSEARCH_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ q: question, max_results: 5, read: 3 }),
});
const { results } = await res.json();
const share = Math.floor(BUDGET / Math.max(results.length, 1));
return results
.map((r, i) => {
const body = r.content_status === 'ok' ? r.content.slice(0, share) : `(snippet only) ${r.snippet}`;
return `[${i + 1}] ${r.title} - ${r.url}\n${body}`;
})
.join('\n\n');
}
async function answer(question) {
const prompt = 'Answer using only the sources below. Cite them as [1], [2] and so on. '
+ 'Say so if they do not answer the question.\n\n' + (await sources(question)) + '\n\nQuestion: ' + question;
const res = await fetch(`${process.env.LLM_BASE_URL.replace(/\/$/, '')}/chat/completions`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LLM_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model: process.env.LLM_MODEL, messages: [{ role: 'user', content: prompt }] }),
});
const data = await res.json();
return data.choices[0].message.content;
}
console.log(await answer('How should I size a Postgres connection pool?'));
Devo usar GET ou POST no Node?
POST, com um corpo JSON, no código da aplicação. Arrays como include_domains e perguntas longas viajam mais limpos em um corpo do que em uma query string. O GET está lá para testes rápidos e para ferramentas que não conseguem enviar um corpo; monte a query string com URLSearchParams e junte listas com vírgulas.
Ambos os formatos custam o mesmo e retornam o mesmo JSON. A referência de pesquisa lista todos os parâmetros para ambos.
Onde a chave deve ficar?
Em uma variável de ambiente lida em tempo de execução, nunca no arquivo de código. Em um servidor, use o cofre de segredos da sua plataforma. No navegador, não chame a API de jeito nenhum: a chave ficaria visível para todos os visitantes. Chame seu próprio backend, que por sua vez chama LLMWebSearch.
Crie uma chave de API por agente, app ou colega de equipe e revogue qualquer uma delas no dashboard sem afetar as outras. Uma chave por serviço facilita a leitura do histórico do painel e torna uma chave vazada fácil de revogar.
Node.js: perguntas frequentes
Existe um pacote npm?
Não. fetch é nativo do Node 18 e superiores, e a API é JSON puro sobre HTTPS. As funções desta página são a integração inteira.
Funciona em Deno, Bun ou runtimes de edge?
O código usa apenas fetch, JSON e process.env. Runtimes com fetch funcionam depois de substituir process.env pela própria forma de ler segredos deles.
Como faço para tentar novamente chamadas com falha?
Tente novamente apenas 429, 502 e 504, com uma pausa que dobra a cada tentativa. Nunca tente novamente 400, 401 ou 402. A página de erros explica o motivo e mostra um helper.