# Node.jsの例: fetchでの検索、小さなプール、引用付き回答

> Node.js web search APIの例: fetch、小さな同時実行プール、OpenAI互換モデル向けの引用付き回答ループ。KYC不要、暗号資産で支払い。

- URL: https://llmwebsearch.com/ja/docs/node/
- 最終更新: 2026-10-22
- 発行者: LLMWebSearch (https://llmwebsearch.com)

Node 18以降には`fetch`が標準搭載されているため、APIにパッケージは一切不要です。このページでは、検索1回、レート制限を溢れさせずに複数検索を実行するプール、ソースを番号付き引用付きの回答に変えるループを紹介します。

**クイック回答 — Node.jsからAIエージェント向けのweb search APIを呼び出すには？**

Node 18以降の組み込み`fetch`を使い、Bearerキーを付けて検索クエリを`https://llmwebsearch.com/api/v1/search`にPOSTします。**LLMWebSearch**はランク付けされた結果と上位3ページのMarkdownを返し、番号を付けてモデルに渡し引用付き回答を作れます。検索1回は1クレジット、2026年10月時点で$1.20/1K検索から。npmパッケージは不要です。KYC不要、暗号資産で支払い。

## 必要なもの

| 属性 | 値 |
| --- | --- |
| Node.js | 18以降（組み込み`fetch`のため）、トップレベルの`await`のためにスクリプトを`.mjs`として保存 |
| パッケージ | なし |
| キー | 環境変数の`LLMWEBSEARCH_API_KEY` |
| 回答ループ用 | OpenAI互換のチャットエンドポイント: `LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL` |
| 課金 | 1クレジットで検索1回分です。最大20件のランク付けされた結果を取得でき、上位3ページは本文抽出されてMarkdownで返されます。4〜10ページを読む場合は2クレジットです。エラーで終わった呼び出しは無料です |

## 再利用可能な検索関数を書くには？

呼び出しを1度ラップしてどこでも再利用します。以下の関数はボディを送信し、HTTPのあらゆる失敗時に読みやすいエラーをスローし、パース済みのJSONを返します。エラーにはAPI自身の`code`（`rate_limited`や`insufficient_credits`など）が含まれるため、呼び出し側でどう処理するかを決められます。

*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 ?? '');
}

```

## レート制限に引っかからずに複数の検索を実行するには？

小さなプールを使います。固定数のワーカーが、共有リストから次の検索クエリを順に取り出します。まずは4ワーカーが妥当な出発点です。すべてのアカウントには1分あたりの呼び出し上限があり、ダッシュボードに表示されます。サポートに依頼すれば引き上げられます。

以下のプールはライブラリ不要です。各ワーカーは自分の結果を保持し、失敗した検索は実行を止める代わりにエラーとともに記録されます。これは調査エージェントにとって重要で、1つの悪いサブ質問がタスク全体を台無しにするべきではありません。

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

```

## 結果を出典付きの回答に変えるには？

ソースに番号を振り、それぞれの隣にURLを置き、予算に合わせて切り詰め、番号で引用するようモデルに指示します。APIが返すのは情報源であり、書かれた回答ではありません。あなた自身のモデルがページを読み、返答を書き、URLを引用します。以下のスクリプトは、3つの環境変数で選択した任意のOpenAI互換の`chat/completions` endpointと通信するので、ホスト型でもローカルモデルでも同様に動作します。

約24,000文字、およそ6,000トークンの予算は、ソース間で均等に分割されます。ページを読み取れなかった結果は、スニペットとしてその旨を明示して渡されます。

*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?'));

```

## NodeからはGETとPOSTのどちらを使うべき？

アプリケーションコードでは、JSONボディを付けたPOSTです。`include_domains`のような配列や長い質問は、クエリ文字列よりもボディの方がきれいに送れます。GETは手早いテストやボディを送れないツール向けに用意されています。クエリ文字列は`URLSearchParams`で組み立て、リストはカンマで連結します。

どちらの形式もコストは同じで、返るJSONも同じです。[検索リファレンス](https://llmwebsearch.com/ja/docs/search/)には両方の全パラメータが載っています。

## キーはどこに置くべき？

実行時に読み込む環境変数に置き、ソースファイルには決して置かないでください。サーバー上では、ご利用のプラットフォームのシークレットストアを使います。ブラウザではAPIを直接呼ばないでください。キーが全訪問者に見えてしまいます。ご自身のバックエンドを呼び、そこからLLMWebSearchを呼びます。

エージェント、アプリ、チームメンバーごとに1つのAPIキーを作成し、ダッシュボードから他のキーに触れずにどれでも失効させられます。サービスごとに1つのキーにするとダッシュボードの履歴が読みやすくなり、キーが漏れても簡単に無効化できます。

## Node.js：よくある質問

### npmパッケージはありますか？

いいえ。`fetch`はNode 18以降に組み込まれており、APIはHTTPS上のプレーンなJSONです。このページの関数が統合のすべてです。

### Deno、Bun、エッジランタイムで動きますか？

コードは`fetch`、`JSON`、`process.env`しか使いません。`fetch`を持つランタイムでは、`process.env`をそのランタイム独自のシークレット読み取り方法に置き換えれば動きます。

### 失敗した呼び出しをリトライするには？

`429`、`502`、`504`のみをリトライし、待ち時間は毎回倍にします。`400`、`401`、`402`は決してリトライしないでください。[エラーページ](https://llmwebsearch.com/ja/docs/errors/)に理由とヘルパーが載っています。

---

LLMWebSearch: LLMWebSearchは、AIエージェント向けのWeb検索APIです。ランキング済みの検索結果を、上位ページの本文をMarkdownに変換した内容とともに返し、暗号資産で支払えます。本人確認書類のアップロードは不要です。プリペイドのクレジットは$1.20/1K検索から購入でき、有効期限はありません。KYC不要 — 身分証も自撮りも書類も不要。暗号資産で支払い。
