AI活用

OpenRouter APIの使い方|JavaScriptで複数AIモデルとフォールバックを扱う

OpenRouter APIをJavaScriptから使い、複数AIモデルのfallback、provider routing、価格・速度・parameter・ZDR条件、router metadataと費用ログを実装します。

この記事の目次
  1. 結論:候補を絞り、実際の経路を必ず記録する
  2. OpenRouterを使う理由と注意点
  3. provider failoverとmodel fallbackは別
  4. 直接契約との違い
  5. JavaScriptで最小requestを送る
  6. 環境変数を準備する
  7. fetchでChat Completions endpointを呼ぶ
  8. modelとproviderのfallbackを設定する
  9. 必須parameterを無視させない
  10. provider順を固定しすぎない
  11. 価格・速度・ZDRで経路を選ぶ
  12. 機密度が高いrequestへZDR条件を付ける
  13. Models APIで候補を事前確認する
  14. 実際のroutingをログへ残す
  15. router metadataを読む
  16. generation statsで費用・遅延を監査する
  17. errorと再試行を設計する
  18. 評価表をtask別に作る
  19. 本番前チェックリスト
  20. まとめ:routingは可用性だけでなく、監査対象

OpenRouter APIは、共通endpointから複数のAIモデル・providerへrequestを送り、同一モデル内のprovider failoverと、異なるモデルへのfallbackを設定できるサービスです。JavaScriptではOpenAI互換のChat Completions形式を使い、models 配列へ優先順を指定します。

ただし、APIを一つにしただけでは、品質、料金、データ保持、利用地域、parameter対応まで同じにはなりません。requestごとに候補条件を設定し、responseの model とrouter metadataから、実際にどのmodel・providerが使われたかを記録します。

この記事では、Node.jsの最小request、モデル間fallback、provider条件、価格・速度・ZDR(Zero Data Retention:providerがデータを保存しない方針)、error処理、generation statsを実装します。model IDと料金は変化が速いため、2026年7月20日時点の公式仕様を基準にし、固定ランキングは作りません。

情報確認日:2026年7月20日(日本時間)

結論:候補を絞り、実際の経路を必ず記録する

アプリ
  ├─ task要件を決める
  │   ├─ 必須parameter
  │   ├─ 最大価格
  │   ├─ latency / throughput
  │   └─ data collection / ZDR
  ↓
model候補を優先順で指定
  ↓
OpenRouter
  ├─ model Aのprovider 1
  ├─ model Aのprovider 2へfailover
  └─ model Bへfallback
  ↓
response
  ├─ 実使用model
  ├─ usage
  ├─ router metadata
  └─ generation ID
  ↓
品質・費用・遅延・providerをtask単位で記録

本番利用の原則

  • model IDを環境変数・設定DBで管理する
  • fallback候補は同じ入出力契約を満たすmodelに限定する
  • require_parameters で必須parameterの無視を防ぐ
  • 機密度に応じて data_collectionzdr を指定する
  • responseの実使用modelとrouting attemptを保存する
  • 価格・latency・品質を自分の評価データで定期比較する
スポンサーリンク

OpenRouterを使う理由と注意点

provider failoverとmodel fallbackは別

機能 切り替えるもの 設定 品質への影響
Provider failover 同じmodelを提供する接続先 既定で利用。provider条件で制御 同じmodelでも速度・設定差を評価
Model fallback modelそのもの models 配列 出力品質・形式・tokenizationが変わり得る

failoverは障害時に予備経路へ切り替えること、fallbackは第一候補が使えないとき別の候補へ下げることです。OpenRouterでは、同じmodelのprovider切替は既定で行われ、異なるmodelへのfallbackは models 配列で明示します。

直接契約との違い

項目 OpenRouter 各社APIへ直接接続
認証・請求 一つへ集約しやすい providerごとに契約・key管理
モデル切替 共通形式とroutingを使える adapterを自作する
最新固有機能 対応待ち・互換差があり得る 公式APIで先に使える場合がある
データ経路 OpenRouterと選択providerを通る 原則として選択providerへ直接
障害切替 provider・modelのrouting機能 自分で実装・運用

OpenAI互換とは、requestとresponseの形が似ているという意味です。全modelが同じparameter、tool schema、structured output、画像、reasoningを同じ精度で処理する保証ではありません。provider固有機能が重要なら直接APIも比較します。

JavaScriptで最小requestを送る

環境変数を準備する

mkdir openrouter-sample
cd openrouter-sample
npm init -y
npm pkg set type=module
export OPENROUTER_API_KEY="your_api_key_here"
export OPENROUTER_MODELS="provider-a/model-a,provider-b/model-b"

model IDはOpenRouterのModels APIまたはmodel一覧で確認し、organization prefixを含む正確なslugを設定します。記事中の「provider-a/model-a」はplaceholderなので、そのままでは動きません。

fetchでChat Completions endpointを呼ぶ

const apiKey = process.env.OPENROUTER_API_KEY;
const models = (process.env.OPENROUTER_MODELS ?? "")
  .split(",")
  .map((value) => value.trim())
  .filter(Boolean);

if (!apiKey || models.length === 0) {
  throw new Error("OpenRouter configuration is missing");
}

const response = await fetch(
  "https://openrouter.ai/api/v1/chat/completions",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      "X-OpenRouter-Metadata": "enabled",
    },
    body: JSON.stringify({
      models,
      messages: [
        {
          role: "user",
          content:
            "JavaScriptのAbortControllerを" +
            "120文字以内の日本語で説明してください。",
        },
      ],
    }),
  },
);

const data = await response.json();

if (!response.ok) {
  console.error({
    status: response.status,
    code: data.error?.code,
    message: data.error?.message,
    routing: data.openrouter_metadata?.summary,
  });
  process.exitCode = 1;
} else {
  console.log(data.choices[0].message.content);
}

models は優先順のmodel一覧です。第一候補がrate limit、downtime、context length、moderation等のerrorになったとき、次のmodelが試されます。どのerrorでも別modelへ送ってよいかを業務要件で確認してください。

fallbackで安全基準を下げない:moderation refusalもfallbackの契機になり得ます。第一候補が拒否した内容を、安全条件の異なるmodelへ無条件で回し続ける設計は避け、用途ごとの入力検査と共通policyを持ちます。

modelとproviderのfallbackを設定する

必須parameterを無視させない

OpenRouterは、選ばれたmodel・providerが一部parameterへ対応しない場合、既定ではそのparameterを無視することがあります。JSON Schemaやtool callingが業務契約なら require_parameters: true を指定します。

const requestBody = {
  models,
  messages: [
    {
      role: "user",
      content: "次の問い合わせをcategory別に分類してください。",
    },
  ],
  response_format: { type: "json_object" },
  provider: {
    allow_fallbacks: true,
    require_parameters: true,
  },
};

require_parameters により候補providerが減り、利用可能な接続先がなくなる場合があります。「JSON形式が無視されたまま成功する」より「503として失敗し、手動経路へ戻す」方が安全な業務もあります。

provider順を固定しすぎない

provider.order でprovider slugを優先順に指定でき、only で許可、ignore で除外できます。しかし、1社だけへ絞るとOpenRouterの冗長化が失われます。契約・地域・データ方針で必要な場合だけ制限し、利用可能性低下をテストします。

const fixedProviderRouting = {
  provider: {
    order: [
      "approved-provider-a",
      "approved-provider-b",
    ],
    allow_fallbacks: false,
  },
};

exact provider slugはmodelページから取得します。表示名を推測で小文字化して作らないでください。allow_fallbacks: false はorder外へ回したくない場合に使いますが、両providerが利用できないとrequestは失敗します。

価格・速度・ZDRで経路を選ぶ

要件 routing候補 注意点
費用優先 price順、max price、安価model候補 品質と再試行増加を同時に測る
TTFT優先 latency指標を優先 地域・時間帯で変動する
生成速度優先 throughput指標を優先 最初の文字の速さとは別
parameter必須 require_parameters: true 候補0件のfailure pathを用意
学習利用を避ける data_collection: "deny" endpointごとの最新policyを確認
保存を避ける zdr: true OpenRouterとprovider双方の条件を確認

latency(レイテンシ)はrequestから応答開始までの遅れ、throughput(スループット)は1秒あたりに生成できるtoken数です。ZDRはproviderがprompt・responseを一定時間保存しない方針で、「第三者を一切経由しない」「すべての法的要件を満たす」という意味ではありません。

機密度が高いrequestへZDR条件を付ける

const privateProviderRouting = {
  provider: {
    data_collection: "deny",
    zdr: true,
    require_parameters: true,
  },
};

条件を厳しくすると、modelによっては利用可能なendpointがなくなります。本番では「条件を緩めて自動送信」せず、入力を送らない、承認済みの直接APIへ切り替える、利用者へ再試行を案内するなど明示的なfallbackを選びます。

個人情報を扱う判断は生成AIで個人情報を扱うときの注意点も参照してください。routing parameterは組織の法務・セキュリティ確認を代替しません。

Models APIで候補を事前確認する

model ID、context length、料金、対応parameterは更新されます。起動時または管理用jobでModels APIを取得し、設定したslugが存在し、必須parameterへ対応するかを検証します。

const modelsResponse = await fetch(
  "https://openrouter.ai/api/v1/models",
);
const catalog = await modelsResponse.json();
const modelById = new Map(
  catalog.data.map((item) => [item.id, item]),
);

for (const modelId of models) {
  const item = modelById.get(modelId);
  if (!item) {
    throw new Error(`Unknown OpenRouter model: ${modelId}`);
  }

  console.log({
    id: item.id,
    contextLength: item.context_length,
    promptPrice: item.pricing?.prompt,
    completionPrice: item.pricing?.completion,
    supportedParameters: item.supported_parameters,
  });
}

pricingの値はtokenあたりの文字列として返るため、浮動小数点の丸め誤差を避ける料金計算が必要です。記事へ金額をコピーして固定せず、取得日時とcatalog responseの必要項目をsnapshotとして保存します。

実際のroutingをログへ残す

router metadataを読む

request headerへ X-OpenRouter-Metadata: enabled を付けると、成功responseへ openrouter_metadata が追加されます。新しいoptional fieldが増える可能性があるため、未知fieldをerrorにせず無視します。

const metadata = data.openrouter_metadata;

console.log({
  generationId: data.id,
  servedModel: data.model,
  finishReason: data.choices[0].finish_reason,
  promptTokens: data.usage?.prompt_tokens,
  completionTokens: data.usage?.completion_tokens,
  routingStrategy: metadata?.strategy,
  attempt: metadata?.attempt,
  summary: metadata?.summary,
  selectedEndpoints:
    metadata?.endpoints?.available
      ?.filter((endpoint) => endpoint.selected)
      .map((endpoint) => ({
        provider: endpoint.provider,
        model: endpoint.model,
      })) ?? [],
});

attempt が2以上なら、前の候補で失敗しfallbackしたことが分かります。metadataがないこと自体を障害にしません。cache replay等ではmetadataが付かない場合があると公式ガイドに明記されています。

generation statsで費用・遅延を監査する

async function getGenerationStats(generationId) {
  const response = await fetch(
    "https://openrouter.ai/api/v1/generation?" +
      new URLSearchParams({ id: generationId }),
    {
      headers: {
        Authorization: `Bearer ${apiKey}`,
      },
    },
  );

  if (!response.ok) {
    throw new Error(`Stats failed: ${response.status}`);
  }

  return response.json();
}

const stats = await getGenerationStats(data.id);
console.log({
  model: stats.data.model,
  provider: stats.data.provider_name,
  latency: stats.data.latency,
  generationTime: stats.data.generation_time,
  totalCost: stats.data.total_cost,
});

generation statsにはprovider、latency、token、cost等が含まれます。金額・性能ログへprompt本文を一緒に保存する必要はありません。task種別、評価dataset version、model、provider、routing条件、response IDを紐付けます。

errorと再試行を設計する

HTTP 意味の例 対応
400 parameter・request形式が不正 再送せず実装を修正
401 API keyが無効 key・権限を確認
402 credit不足 予算・残高・上限を確認
429 rate limit Retry-After を尊重
502 選択model/providerのdownstream error routing attempt確認、上限付き再試行
503 条件を満たすproviderなし policyを自動緩和せず明示的に処理

429や503で Retry-After headerがある場合は、その秒数を待ちます。fetchを使うなら自分で処理し、最大回数、総待機時間、jitter(同時再試行をずらす乱数)を設けます。model fallbackが既に複数回試した後、アプリ側でも無制限に再送しないようにします。

評価表をtask別に作る

記録項目 目的
task・dataset version 同じ問題で比較する
requested models 候補と優先順を再現する
served model・provider 実際の経路を特定する
routing conditions ZDR・parameter・価格条件を記録
品質score 正確性・形式・安全性を測る
TTFT・総時間 待ち時間と生成速度を分ける
token・total cost 1件あたりの実費を測る
attempt・error fallback率と失敗原因を測る

「最も高性能なmodel」を一つ決めるのではなく、coding、分類、要約、長文、tool callingなどtaskごとに評価します。費用管理の設計はAI APIのコストを管理する方法を参照してください。

本番前チェックリスト

  1. model slugをModels APIで検証し、取得日を記録している
  2. fallback候補は同じ入出力契約と安全基準を満たす
  3. 必須parameterへ require_parameters を使っている
  4. データ分類に応じてcollection・ZDR・providerを制限している
  5. 条件を満たすproviderが0件のfailure pathがある
  6. responseのserved model、provider、attemptを保存している
  7. generation statsでtoken・cost・latencyを照合している
  8. 429/503のRetry-Afterと再試行上限がある
  9. modelごとの出力差を評価datasetで回帰テストしている
  10. 直接APIへ接続すべき固有機能・契約要件を確認した

まとめ:routingは可用性だけでなく、監査対象

OpenRouter APIでは、models 配列で異なるmodelのfallbackを設定し、provider でparameter対応、data collection、ZDR、provider順などを制御できます。同じmodel内のprovider failoverとmodel fallbackを分けて理解することが重要です。

requestが成功したら、回答だけでなくserved model、provider、attempt、usage、cost、latencyを記録してください。価格・速度・ZDRの条件を厳しくしたときに候補がなくなる動作もテストし、条件を黙って緩めない設計にすると、複数modelを安全に運用できます。

スポンサーリンク