Réponse rapide
Comment appeler une API de recherche web pour agents IA depuis Node.js ?
Utilisez le fetch intégré de Node 18+ pour POST votre requête vers https://llmwebsearch.com/api/v1/search avec une clé Bearer. LLMWebSearch répond avec des résultats classés et les 3 meilleures pages en Markdown, prêtes à être numérotées et transmises à un modèle pour une réponse citée. Une recherche coûte 1 crédit, à partir de 1,20 $US/1K requêtes en octobre 2026. Aucun package npm nécessaire. Sans KYC ; payez en crypto.
Ce qu'il vous faut
| Node.js | 18 ou plus récent, pour le fetch intégré ; enregistrez les scripts en .mjs pour le await de niveau supérieur |
|---|---|
| Packages | Aucun |
| Clé | LLMWEBSEARCH_API_KEY dans votre environnement |
| Pour la boucle de réponse | Un endpoint de chat compatible OpenAI : LLM_BASE_URL, LLM_API_KEY, LLM_MODEL |
| Facturation | Un crédit paie une recherche : jusqu'à 20 résultats classés, avec les pages des 3 premiers lues et renvoyées en Markdown. Lire 4 à 10 pages coûte 2 crédits. Un appel qui se termine sur une erreur ne coûte rien |
Comment écrire une fonction de recherche réutilisable ?
Enveloppez l'appel une fois et réutilisez-le partout. La fonction ci-dessous envoie le corps, lève une erreur lisible à toute défaillance HTTP et renvoie le JSON analysé. Les erreurs portent le code propre à l'API, comme rate_limited ou insufficient_credits, pour que l'appelant décide quoi faire.
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 ?? '');
}
Comment lancer plusieurs requêtes sans atteindre le rate limit ?
Passez-les par un petit pool : un nombre fixe de workers qui prennent chacun la requête suivante dans une liste partagée. Quatre workers est un bon point de départ. Chaque compte a une limite d'appels par minute, affichée dans le dashboard ; demandez au support et elle peut être augmentée.
Le pool ci-dessous ne nécessite aucune bibliothèque. Chaque worker conserve ses propres résultats, et une requête échouée est consignée avec son erreur au lieu d'arrêter l'exécution. C'est important pour les agents de recherche, où une sous-question ratée ne doit pas coûter toute la tâche.
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);
Comment transformer les résultats en réponse avec citations ?
Numérotez les sources, placez l'URL à côté de chacune, rognez-les selon un budget, et demandez au modèle de citer par numéro. L'API renvoie des sources, pas une réponse rédigée : c'est votre propre modèle qui lit les pages, rédige la réponse et cite les URLs. Le script ci-dessous s'adresse à tout endpoint chat/completions compatible OpenAI, choisi avec trois variables d'environnement, donc il fonctionne aussi bien avec des modèles hébergés que locaux.
Le budget d'environ 24 000 caractères, soit à peu près 6 000 tokens, est réparti également entre les sources. Un résultat dont la page n'a pas pu être lue est transmis sous forme de snippet, signalé comme tel.
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?'));
Faut-il utiliser GET ou POST depuis Node ?
POST, avec un body JSON, dans le code applicatif. Les tableaux comme include_domains et les questions longues passent plus proprement dans un body que dans une query string. GET est là pour les tests rapides et pour les outils qui ne peuvent pas envoyer de body ; construisez sa query string avec URLSearchParams et joignez les listes par des virgules.
Les deux formes coûtent pareil et renvoient le même JSON. La référence de recherche liste chaque paramètre pour les deux.
Où doit vivre la clé ?
Dans une variable d'environnement lue à l'exécution, jamais dans le fichier source. Sur un serveur, utilisez le gestionnaire de secrets de votre plateforme. Dans un navigateur, n'appelez pas l'API du tout : la clé serait visible par chaque visiteur. Appelez votre propre backend, qui appelle ensuite LLMWebSearch.
Créez une clé API par agent, application ou coéquipier et révoquez n'importe laquelle depuis le dashboard sans toucher aux autres. Une clé par service rend l'historique du dashboard plus lisible, et une clé compromise facile à révoquer.
Node.js : questions courantes
Existe-t-il un package npm ?
Non. fetch est intégré à Node 18 et versions ultérieures, et l'API est du JSON simple sur HTTPS. Les fonctions de cette page constituent toute l'intégration.
Est-ce que ça fonctionne sur Deno, Bun ou les edge runtimes ?
Le code n'utilise que fetch, JSON et process.env. Les runtimes dotés de fetch fonctionnent après avoir remplacé process.env par leur propre façon de lire les secrets.
Comment réessayer les appels échoués ?
Ne réessayez que 429, 502 et 504, avec une pause qui double à chaque fois. Ne réessayez jamais 400, 401 ni 402. La page des erreurs explique pourquoi et montre un helper.