
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つの防御策(コストガードレール、冪等性、リスクメタデータ)を解説します。
2026年2月6日、X.comは新しいPay-Per-Use課金モデル
を発表しました。主な変更点は以下の通りです:
- クレジット制: 事前にクレジットを購入し、API呼び出しごとに消費
- エンドポイント別単価: 取得系・投稿系で単価が異なる
- 重複取得の軽減策: 同日中の同一データ取得は原則無課金(ただし例外あり)
- 予算制限: 自動購入設定と支出上限設定が可能
この変更により、開発者は「APIを何回叩いたか」「いくら使ったか」を常に意識する必要が出てきました。特に、エージェントにCLIを任せる場合、以下のリスクが顕在化します:
- 無限ループで課金: エージェントがエラーをリトライし続けて課金
- 重複投稿で課金: 同じツイートを複数回投稿して無駄な課金
- 予算超過: 気づかないうちに予算上限を突破
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.codeがBUDGET_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.dedupeStatusがreplayedになっており、エージェントは「キャッシュから返された」と判断できます。
パラメータ変更の検出
同じ--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(復元不可) |
エージェント側の判断ロジック
エージェントは、riskとhasCostを組み合わせて判断できます。
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 Schemahelp <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つの防御策は以下の通りです:
- billing モジュール: コスト見積もり・予算ガードレール・使用履歴レポート
- idempotency ledger: 冪等キーによる重複投稿防止
- 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
を参照してください。
参考リンク