Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Feb 16, 2026 - 日記

X.com API従量課金時代のCLI設計: xcom-rsが実装したコストガードレールと冪等性

X.com API従量課金時代のCLI設計: xcom-rsが実装したコストガードレールと冪等性 cover image

2026年2月6日、X.com(旧Twitter)がX API の新しい従量課金モデル(Pay-Per-Use)を発表しました。これまでの月額200ドル・5,000ドルの固定プランから、API呼び出しごとに課金されるクレジット制に移行します。

この変更により、AIエージェント にX.com APIを操作させる際の設計要件が大きく変わりました。「何度実行しても安全」だった無料APIと違い、「1回の誤実行が課金につながる」有料APIでは、CLIツールに新しい防御機構が必要です。

本記事では、私が開発したxcom-rs というRust製X.com API CLIツールを題材に、従量課金時代のCLI設計で実装すべき3つの防御策(コストガードレール、冪等性、リスクメタデータ)を解説します。

X.com API従量課金の背景

2026年2月6日、X.comは新しいPay-Per-Use課金モデル を発表しました。主な変更点は以下の通りです:

  • クレジット制: 事前にクレジットを購入し、API呼び出しごとに消費
  • エンドポイント別単価: 取得系・投稿系で単価が異なる
  • 重複取得の軽減策: 同日中の同一データ取得は原則無課金(ただし例外あり)
  • 予算制限: 自動購入設定と支出上限設定が可能

この変更により、開発者は「APIを何回叩いたか」「いくら使ったか」を常に意識する必要が出てきました。特に、エージェントにCLIを任せる場合、以下のリスクが顕在化します:

  1. 無限ループで課金: エージェントがエラーをリトライし続けて課金
  2. 重複投稿で課金: 同じツイートを複数回投稿して無駄な課金
  3. 予算超過: 気づかないうちに予算上限を突破

xcom-rsは、これらのリスクに対応するため、3つの防御策を実装しています。

防御策1: billing モジュール - コスト見積もりとガードレール

xcom-rsのbillingモジュールは、API呼び出し前にコストを見積もり、予算超過を防ぐ機能を提供します。

コスト見積もり

billing estimateコマンドで、実行前にコストを確認できます。

1
2
# tweets create のコスト見積もり
xcom-rs billing estimate --command "tweets create" --output json

出力例(JSON):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "ok": true,
  "type": "billing.estimate",
  "schemaVersion": 1,
  "data": {
    "command": "tweets create",
    "estimatedCost": {
      "credits": 1.0,
      "usd": 0.01
    },
    "budgetRemaining": {
      "credits": 99.0,
      "usd": 0.99
    }
  }
}

この出力から、エージェントは「実行すると1クレジット消費」「残り予算は99クレジット」を判断できます。

予算ガードレール

--budget-limitオプションで、コマンド実行前に予算チェックを強制できます。

1
2
# 予算上限を10ドルに設定
xcom-rs tweets create "Hello World" --budget-limit 10.00 --output json

予算超過時の出力例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "ok": false,
  "type": "error",
  "schemaVersion": 1,
  "error": {
    "code": "BUDGET_EXCEEDED",
    "message": "Budget limit exceeded: $10.00 remaining, $15.00 required",
    "isRetryable": false
  }
}

エージェントはerror.codeBUDGET_EXCEEDEDであることを確認し、リトライせずに停止できます。

コストレポート

billing reportコマンドで、過去の使用履歴を確認できます。

1
2
# 過去7日間のコストレポート
xcom-rs billing report --days 7 --output json

出力例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "ok": true,
  "type": "billing.report",
  "schemaVersion": 1,
  "data": {
    "period": {
      "start": "2026-02-09T00:00:00Z",
      "end": "2026-02-16T00:00:00Z"
    },
    "totalCost": {
      "credits": 45.0,
      "usd": 0.45
    },
    "breakdown": [
      {
        "command": "tweets create",
        "count": 30,
        "cost": { "credits": 30.0, "usd": 0.30 }
      },
      {
        "command": "tweets list",
        "count": 15,
        "cost": { "credits": 15.0, "usd": 0.15 }
      }
    ]
  }
}

この情報をもとに、エージェントは「どのコマンドが高コストか」を判断し、最適化できます。

防御策2: idempotency ledger - ツイート重複防止

従量課金APIでは、「同じツイートを2回投稿する」ことが直接的な課金の無駄につながります。xcom-rsはidempotency ledger(冪等性台帳)で、重複投稿を防ぎます。

冪等キーの仕組み

tweets createコマンドに--dedupe-keyオプションを付けると、同じキーでの再実行時にAPI呼び出しをスキップします。

1
2
# 初回実行(API呼び出しあり)
xcom-rs tweets create "Deploy completed" --dedupe-key "deploy-20260216-001" --output json

初回実行時の出力:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "ok": true,
  "type": "tweets.create",
  "schemaVersion": 1,
  "response": {
    "data": {
      "id": "1234567890",
      "text": "Deploy completed"
    }
  },
  "meta": {
    "dedupeKey": "deploy-20260216-001",
    "dedupeStatus": "executed"
  }
}

同じキーで再実行した場合:

1
2
# 再実行(キャッシュから返却、API呼び出しなし)
xcom-rs tweets create "Deploy completed" --dedupe-key "deploy-20260216-001" --output json

再実行時の出力:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "ok": true,
  "type": "tweets.create",
  "schemaVersion": 1,
  "response": {
    "data": {
      "id": "1234567890",
      "text": "Deploy completed"
    }
  },
  "meta": {
    "dedupeKey": "deploy-20260216-001",
    "dedupeStatus": "replayed"
  }
}

meta.dedupeStatusreplayedになっており、エージェントは「キャッシュから返された」と判断できます。

パラメータ変更の検出

同じ--dedupe-keyで異なる内容を投稿しようとすると、エラーになります(安全側に倒す)。

1
2
# 同じキーで異なる内容を投稿
xcom-rs tweets create "Different message" --dedupe-key "deploy-20260216-001" --output json

エラー出力:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "ok": false,
  "type": "error",
  "schemaVersion": 1,
  "error": {
    "code": "DEDUPE_KEY_CONFLICT",
    "message": "Dedupe key 'deploy-20260216-001' already used with different parameters",
    "isRetryable": false
  }
}

これにより、「キーの使い回しミス」を検出できます。

実装の詳細

冪等性台帳は、ローカルストレージ(~/.xcom-rs/idempotency.db)に以下の情報を保存します:

  • team_id/user_id/method/dedupe_key: 一意キー
  • request_hash: リクエストパラメータのハッシュ
  • response: API応答のキャッシュ
  • created_at: 初回実行時刻

古いエントリは自動的にクリーンアップされます(デフォルト: 30日)。

防御策3: risk メタデータ - コマンドの危険度表示

xcom-rsは、すべてのコマンドにrisk(危険度)とhasCost(課金有無)のメタデータを付与しています。

commands エンドポイント

commandsコマンドで、利用可能なコマンド一覧とメタデータを取得できます。

1
xcom-rs commands --output json

出力例(抜粋):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "ok": true,
  "type": "commands",
  "schemaVersion": 1,
  "data": {
    "commands": [
      {
        "name": "commands",
        "description": "List all available commands with metadata",
        "arguments": [],
        "risk": "safe",
        "hasCost": false
      },
      {
        "name": "tweets create",
        "description": "Create a new tweet",
        "arguments": [
          {
            "name": "text",
            "description": "Tweet text",
            "required": true,
            "type": "string"
          }
        ],
        "risk": "medium",
        "hasCost": true
      },
      {
        "name": "tweets list",
        "description": "List tweets",
        "arguments": [],
        "risk": "low",
        "hasCost": true
      }
    ]
  }
}

risk レベルの定義

risk意味
safe読み取り専用、課金なしcommands, schema, help
low読み取り系、課金ありtweets list, users info
medium書き込み系、取り消し可能tweets create(削除可能)
high書き込み系、取り消し不可tweets delete(復元不可)

エージェント側の判断ロジック

エージェントは、riskhasCostを組み合わせて判断できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import subprocess
import json

def run_xcom(args):
    """xcom-rs を実行し、JSON応答を返す"""
    cmd = ["xcom-rs"] + args + ["--output", "json"]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return json.loads(result.stdout)

# コマンド一覧を取得
commands = run_xcom(["commands"])

# 課金ありのコマンドをフィルタ
costly_commands = [
    c for c in commands["data"]["commands"]
    if c["hasCost"]
]

# high risk かつ課金ありのコマンドは確認を求める
for cmd in costly_commands:
    if cmd["risk"] == "high":
        print(f"Warning: {cmd['name']} is high risk and has cost")
        # ユーザー確認を求める処理

このように、エージェントは「実行前に危険度を判断」できます。

slack-rs との比較: 無料API vs 有料API

私は以前、slack-rs というSlack API CLIツールも開発しました(詳細は「Agentic CLI Designを実装する: slack-rsで学ぶ7原則の具体化 」参照)。

slack-rsとxcom-rsは、どちらも「Agentic CLI Design: CLIをAIエージェント向けプロトコルとして設計する7つの原則 」に基づいて設計されていますが、API特性により以下の違いがあります:

項目slack-rs(無料API)xcom-rs(有料API)
課金なし従量課金
billing モジュールなしあり(コスト見積もり・予算制限)
冪等性--idempotency-key--dedupe-key + ledger
risk メタデータなしあり(safe/low/medium/high)
hasCost フラグなしあり
主な懸念重複投稿の防止課金の無駄・予算超過

slack-rsでは「重複投稿を避ける」ことが主目的でしたが、xcom-rsでは「課金の無駄を避ける」ことが最優先です。このため、billingモジュールとriskメタデータが追加されています。

実装のポイント

xcom-rsの実装で重視したポイントを3つ紹介します。

1. 統一エンベロープ

すべてのコマンドが、以下の統一エンベロープ(共通ラッパー)でJSON出力を返します。

1
2
3
4
5
6
7
{
  "ok": true,
  "type": "コマンド名",
  "schemaVersion": 1,
  "data": { ... },
  "meta": { ... }
}

エラー時:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "ok": false,
  "type": "error",
  "schemaVersion": 1,
  "error": {
    "code": "エラーコード",
    "message": "エラーメッセージ",
    "isRetryable": true/false
  }
}

この構造により、エージェントは「成功/失敗」「リトライ可否」を確実に判断できます。

2. 自己記述的なAPI

xcom-rsは、以下の3つのイントロスペクションコマンドを提供します:

  • commands: 利用可能なコマンド一覧(risk/hasCost付き)
  • schema --command <name>: 入出力のJSON Schema
  • help <command>: 終了コード・エラー語彙の詳細

これにより、エージェントは「ドキュメントを読まずにCLIを理解」できます。

3. 非対話モード

--non-interactiveフラグで、確認プロンプトを完全に無効化できます。

1
xcom-rs tweets create "Hello" --non-interactive --yes --output json

CI/CD環境やエージェント実行時に、対話プロンプトで詰まることがありません。

まとめ

X.com APIの従量課金化により、CLIツールに求められる要件が変わりました。xcom-rsが実装した3つの防御策は以下の通りです:

  1. billing モジュール: コスト見積もり・予算ガードレール・使用履歴レポート
  2. idempotency ledger: 冪等キーによる重複投稿防止
  3. risk メタデータ: コマンドごとの危険度・課金有無の明示

これらの機構により、エージェントは「課金の無駄を避けながら、安全にX.com APIを操作」できます。

今後、他の有料API(Google Maps API、OpenAI APIなど)向けのCLIツールでも、同様の設計パターンが有効です。特に、billing estimate--budget-limitの組み合わせは、予算超過を防ぐ強力な手段になります。

xcom-rsはGitHub で公開しています。Rust 1.70以上があれば、以下のコマンドでビルドできます:

1
2
3
git clone https://github.com/tumf/xcom-rs
cd xcom-rs
make build

詳細な使用例は、リポジトリのEXAMPLES.md を参照してください。

参考リンク