# Exemples Node.js : recherche avec fetch, un petit pool et des réponses citées

> Exemples d'API de recherche web en Node.js : fetch, pool de concurrence et boucle de réponse citée pour tout modèle compatible OpenAI. Sans KYC, crypto.

- URL: https://llmwebsearch.com/fr/docs/node/
- Dernière mise à jour: 2026-10-22
- Éditeur: LLMWebSearch (https://llmwebsearch.com)

Node 18 et plus récents embarquent `fetch`, donc l'API ne nécessite aucun package. Cette page montre une recherche, un pool qui exécute plusieurs recherches sans saturer votre rate limit, et une boucle qui transforme des sources en une réponse avec des citations numérotées.

**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

| Attribut | Valeur |
| --- | --- |
| 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.

*search.mjs*

```javascript
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.

*pool.mjs*

```javascript
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.

*answer.mjs*

```javascript
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](https://llmwebsearch.com/fr/docs/search/) 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](https://llmwebsearch.com/fr/docs/errors/) explique pourquoi et montre un helper.

---

LLMWebSearch: LLMWebSearch est une API de recherche web pour agents IA qui renvoie des résultats classés avec le contenu des meilleures pages en Markdown, payée en crypto sans envoi de pièce d'identité. Les crédits prépayés à partir de 1,20 $US/1K requêtes n'expirent jamais. Sans KYC — pas de pièce d'identité, pas de selfie, pas de documents. Payez en crypto.
