# Node.jsでのSERP API: fetch、ワーカープール、ページネーション

> Node.js 18+の組み込みfetchでSerpCrypto SERP APIを呼び出します。型付きクライアント、同時実行数の上限内で動くワーカープール、ページネーションループです。

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

依存関係なし: Node.jsにはバージョン18以降`fetch`が組み込まれています。エディタやTypeScript向けのJSDoc型付きの小さなクライアント、アカウントの上限を守るプール、結果が尽きたら停止するページネーションループです。

**簡潔な回答 — Node.jsでSERP APIを使うにはどうすればよいですか？**

ヘッダー`Authorization: Bearer <key>`を付けて`fetch("https://serpcrypto.com/api/v1/search?" + new URLSearchParams({ q }))`を呼び出し、`await res.json()`を読み取ります。Node.js 18+ではパッケージは不要です。**SerpCrypto**はGoogleまたはBingの検索結果をJSONで返し、成功した検索1回につき1クレジット、$0.30/1K検索から利用でき、エラーは課金されません。暗号資産で支払い、KYC不要。

## Node.jsのセットアップ概要

| 項目 | 値 |
| --- | --- |
| ランタイム | Node.js 18以降（組み込みの`fetch`、`AbortSignal.timeout`） |
| モジュール | ES modules: `.mjs`ファイル、またはpackage.jsonで`"type": "module"` |
| キー | `process.env.SERPCRYPTO_API_KEY` |
| タイムアウト | `AbortSignal.timeout(90_000)`で90秒 |
| 並列検索 | `concurrency_limit`に合わせたプール |
| 型 | JSDoc typedef。VS Codeと`tsc --checkJs`が読み取ります |

## 型付きの小さなクライアントはどう書けばいいですか？

endpoint、header、エラー処理を1つのモジュールにまとめます。JSDocの`@typedef`行が応答を記述しているので、エディタはビルドステップなしで`organic_results`を自動補完し、コードを型チェックできます。TypeScriptユーザーは同じ形をinterfaceにコピーできます。

`search`関数は、HTTPステータスとAPIのエラーメッセージ`code`を含む`Error`をスローします。そうすればコード側で、`429`、`502`、`504`ではリトライし、それ以外では停止するかを判断できます。詳しくは[エラーページ](https://serpcrypto.com/ja/docs/errors/)]で説明しています。

*serp-client.mjs*

```javascript
// 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();
}
```

## 429に当たらずに検索を並列で実行するには？

アカウントで許可されている数だけワーカーを起動し、それぞれが共有リストから次のキーワードを取得します。プールは無料クレジットのendpointから`concurrency_limit`を読み取るので、アカウントが受け付ける数を超えて一度に検索を送ることはありません。

失敗したキーワードは記録され、ワーカーは次へ進みます。あとで失敗分を再実行してください。エラーにはクレジットがかからないので、成功するまで2回目は無料です。

*batch.mjs: ワーカーのプール*

```javascript
// 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));
```

## 複数ページの結果を取得するには？

`page`をループし、`pagination.next_page`がなくなった時点で停止します。1ページあたり約10件のオーガニック結果です。Googleは2025年9月にnum=100を尊重しなくなって以降、この件数を返しています。追加の各ページにはさらに1クレジットが必要です。1つのキーワードで3ページ分を確認すると3クレジットかかります。結果が1ページしかないキーワードなら1クレジットです。

*pages.mjs*

```javascript
// 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" }));
```

## ブラウザやエッジ関数からAPIを呼べますか？

サーバーやエッジ関数からなら可能です。Cloudflare Workers、Vercel、Denoはいずれも同じ`fetch`を提供しています。ブラウザページからは不可です。キーがすべての訪問者に見えてしまいます。間に小さなサーバールートを置き、キーを保持して、許可したパラメータだけを転送してください。

同じルールがモバイルアプリにも当てはまります。ユーザーの端末に配布されたものは何でも読み取れるため、キーはサーバー上に置きます。

## Node.jsでコストを予測可能にするには？

呼び出し回数を数えます。成功した呼び出し1回が1クレジットで、100,000検索パックでは$0.56/1K検索です。大量のバッチの前に`credits_remaining`を確認し、0に近づいたら`402`の応答を重ねて集めるのではなくプールを停止します。プリペイドの検索クレジットに有効期限はありません。未使用の検索は、使用するまでアカウントに残りますので、使い切らなかった残りは次のジョブに残ります。

## Node.jsに関する質問

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

いいえ。上記のクライアントは`fetch`を使った標準JavaScriptで約40行なので、インストールするものはありません。プロジェクトにコピーして調整してください。

### CommonJS（require）で動作しますか？

例ではトップレベルの`await`のためにES modulesを使っています。CommonJSファイルでは、呼び出しを`async`関数で包み、`fetch`を同じように使ってください。Node.js 18以降ではグローバルです。

### TypeScriptで使うには？

JSDocの形を`interface SearchResponse`にコピーし、`res.json()`の結果にその型を付けます。`organic_results?`のような任意のブロックは任意のままにしてください。ページに表示されないことがあるためです。

### 必要なNode.jsバージョンは？

組み込みの`fetch`にはNode.js 18以降が必要です。`AbortSignal.timeout`はNode.js 17.3から利用できるので、18ならこのページのすべてをカバーできます。

---

SerpCrypto: SerpCryptoは、開発者とAIエージェント向けのSERP APIで、GoogleとBingの検索結果をJSONで返し、身分証明書のアップロードなしで暗号資産で支払えます。前払いの検索クレジットは$0.30/1K検索から購入でき、有効期限はありません。KYC不要 — 身分証明書もセルフィーも書類も不要。暗号資産で支払い。
