Claude Cursor Skill

api-quota-monitor

查询监控 API 供应商/服务的额度、余额、消耗情况。支持官方供应商(Gemini/xAI/ZAI/Minimax/OpenRouter等)、中转站(AIXN/Provider-A等)、订阅服务(Brave/Tavily/Serper)。触发词:查额度、查余额、API用量、供应商额度、quota、balance、usage、billing。

LLM Mart · 0 points · 1 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download aaaaqwq-agi-super-team-skills_api-quota-monitor-cdb04e8.zip · 8 KB
Part of aaaaqwq/agi-super-team — 46 skills

Install

skills CLI npx skills add https://github.com/aAAaqwq/AGI-Super-Team/tree/main/skills/api-quota-monitor
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install aaaaqwq-agi-super-team@llmmart
Git git clone https://github.com/aAAaqwq/AGI-Super-Team.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole aaaaqwq/agi-super-team collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

API Quota Monitor

查询监控各类 API 供应商和订阅服务的额度/余额/消耗。

快速使用

# 查询所有已配置的供应商
python3 scripts/query_quota.py

# 查询特定供应商
python3 scripts/query_quota.py xai
python3 scripts/query_quota.py openrouter
python3 scripts/query_quota.py brave

# 只查官方供应商
python3 scripts/query_quota.py --type official

# 只查订阅服务
python3 scripts/query_quota.py --type service

# JSON 输出
python3 scripts/query_quota.py --format json

# 发现已配置的供应商
python3 scripts/query_quota.py --discover

工作流

1. 发现本地已配置的 API/供应商(从环境变量、auth-session-state.json)
   ↓
2. 按类型查询:API 直查 > 浏览器抓取
   ↓
3. 生成报告:供应商 | 消耗 | 总额度 | 状态

供应商分类

官方供应商 (Official)

供应商 查询方式 环境变量 状态
ZAI (智谱) API 直查 ZAI_API_KEY ✅ 支持
Minimax API 直查 MINIMAX_API_KEY + MINIMAX_GROUP_ID ✅ 支持
OpenRouter API 直查 OPENROUTER_API_KEY ✅ 支持
xAI (Grok) 浏览器 XAI_API_KEY 🔍 需浏览器
Gemini 浏览器 GOOGLE_API_KEY 🔍 需浏览器
Anthropic 浏览器 ANTHROPIC_API_KEY 🔍 需浏览器

中转站 (Reseller)

平台 查询方式 状态来源
AIXN 浏览器抓 ai.9w7.cn auth-session-state.json
Provider-A 浏览器抓 your-provider.example.com auth-session-state.json

中转站查询流程:

  1. 检查 ~/.openclaw/auth-session-state.json 获取登录状态
  2. 使用 browser(action='navigate', profile='openclaw') 访问控制台
  3. 从 snapshot 提取余额/消耗信息

订阅服务 (Services)

服务 查询方式 环境变量
Brave Search API API 直查 BRAVE_API_KEY
Tavily API API 直查 TAVILY_API_KEY
Serper API API 直查 SERPER_API_KEY

浏览器查询 (需浏览器抓取的供应商)

当供应商没有公开的 usage API 时,使用 browser 工具:

# 1. 导航到控制台页面
browser(action='navigate', profile='openclaw', targetUrl='<console_url>')

# 2. 等待加载后获取快照
browser(action='snapshot', profile='openclaw', compact=true, maxChars=2000)

# 3. 从快照中提取余额/消耗信息
# 查找关键词:余额、balance、usage、消耗、tokens、额度

各供应商控制台 URL:

报告格式

📊 API Quota Report
⏰ 2026-03-15 20:30

━━ 官方供应商 ━━
✅ ZAI (智谱): ¥50.00
✅ Minimax: ¥10.00 CNY
✅ OpenRouter: $45.00 remaining
🔍 xAI (Grok): 需浏览器查询

━━ 订阅服务 ━━
✅ Brave Search: 1500/2000 requests
✅ Tavily: 800 remaining

━━ 中转站 ━━
✅ AIXN: ¥238.81
✅ Provider-A: ¥9.35

扩展新供应商

在 scripts/query_quota.py 中添加新供应商配置:

OFFICIAL_PROVIDERS = {
    "new_provider": {
        "name": "New Provider",
        "env_key": "NEW_PROVIDER_API_KEY",
        "api_url": "https://api.newprovider.com/v1/usage",
        "method": "api",  # 或 "browser"
        "extract": lambda data: {
            "balance": data.get("balance", 0),
        }
    },
}

缓存策略

  • API 查询结果可缓存 1-6 小时(避免频繁请求)
  • 缓存文件: ~/.openclaw/quota-cache.json
  • 格式:
{
  "openrouter": {
    "queried_at": "2026-03-15T20:00:00",
    "balance": 45.0,
    "cached": true
  }
}

定时监控

可配置 cron 定期检查并推送报告:

# 在 OpenClaw cron 中添加
# 每 6 小时检查一次 API 供应商额度
0 */6 * * * python3 ~/clawd/skills/api-quota-monitor/scripts/query_quota.py --report | newsbot_send

参考文档

详细的 API 端点和响应格式见 references/providers.md

Files (agi-super-team)
  • references
    • providers.md 4.2 KB
      # Provider Configurations
      
      API供应商/服务的查询配置。按类型分组。
      
      ## 官方供应商 (API 直查)
      
      ### xAI (Grok)
      
      - **API Base**: `https://api.x.ai/v1` (Inference) / `https://management-api.x.ai` (Management)
      - **Auth**:
        - Inference: `XAI_API_KEY`
        - Management: `XAI_MGMT_KEY` (from console.x.ai → Settings → Management Keys)
        - Team ID: `XAI_TEAM_ID`
      - **Billing API**: ✅ 有 Management API
        ```bash
        # 预付费余额
        curl -s "https://management-api.x.ai/v1/billing/teams/$XAI_TEAM_ID/prepaid/balance" \
          -H "Authorization: Bearer $XAI_MGMT_KEY"
      
        # 后付费账单预览
        curl -s "https://management-api.x.ai/v1/billing/teams/$XAI_TEAM_ID/postpaid/invoice/preview" \
          -H "Authorization: Bearer $XAI_MGMT_KEY"
      
        # 用量历史 (POST)
        curl -s -X POST "https://management-api.x.ai/v1/billing/teams/$XAI_TEAM_ID/usage" \
          -H "Authorization: Bearer $XAI_MGMT_KEY" \
          -H "Content-Type: application/json" \
          -d '{"analyticsRequest":{"timeRange":{"startTime":"2026-03-01 00:00:00","endTime":"2026-03-15 23:59:59"},"values":[{"name":"usd","aggregation":"AGGREGATION_SUM"}],"groupBy":["description"]}}'
        ```
      - **返回**:
        - `total.val`: 预付费余额(负数 cents,如 -500 = $5.00)
        - `changes[]`: 充值记录
      - **Docs**: https://docs.x.ai/developers/rest-api-reference/management/billing
      
      ### Google AI (Gemini)
      
      - **API Base**: `https://generativelanguage.googleapis.com/v1`
      - **Auth**: `GOOGLE_API_KEY` 或 `GEMINI_API_KEY`
      - **Usage API**: ❌ 需要浏览器抓 Google AI Studio
      - **Browser**: https://aistudio.google.com/apikey
      
      ### ZAI (智谱 GLM)
      
      - **API Base**: `https://open.bigmodel.cn/api/paas/v4`
      - **Auth**: `ZAI_API_KEY` 或 `ZHIPU_API_KEY`
      - **Usage API**: ❌ 无公开余额API
      - **Browser**: https://open.bigmodel.cn/oa/userCenter
      
      ### Minimax
      
      - **API Base**: `https://api.minimax.chat/v1`
      - **Auth**: `MINIMAX_API_KEY` + `MINIMAX_GROUP_ID`
      - **Usage API**: 
        ```bash
        curl -s "https://api.minimax.chat/v1/user/balance" \
          -H "Authorization: Bearer $MINIMAX_API_KEY" \
          -H "GroupId: $MINIMAX_GROUP_ID"
        ```
      - **返回**: `{"data": {"balance": 10.0, "unit": "CNY"}}`
      
      ### OpenRouter
      
      - **API Base**: `https://openrouter.ai/api/v1`
      - **Auth**: `OPENROUTER_API_KEY`
      - **Usage API**: 
        ```bash
        curl -s "https://openrouter.ai/api/v1/auth/key" \
          -H "Authorization: Bearer $OPENROUTER_API_KEY"
        ```
      - **返回**: `{"data": {"limit_remaining": 50.0, "usage": 10.0}}`
      
      ### Anthropic (Claude)
      
      - **API Base**: `https://api.anthropic.com/v1`
      - **Auth**: `ANTHROPIC_API_KEY`
      - **Usage API**: ❌ 无公开API,需浏览器抓 console.anthropic.com
      
      ---
      
      ## 中转站 (浏览器抓取)
      
      通用模板,需用户提供:
      - 登录URL
      - 余额页面URL
      - 账号/密码(首次登录)
      - 或使用现有 browser profile (openclaw)
      
      ### AIXN (XAPI)
      
      - **Console**: `https://ai.9w7.cn/console`
      - **检查方式**: Browser + snapshot
      - **提取**: `Portfolio`, `余额`, `消耗`
      
      ### Provider-A
      
      - **Console**: `https://your-provider.example.com/dashboard`
      - **检查方式**: Browser + snapshot
      - **提取**: 余额、令牌使用量
      
      ---
      
      ## 订阅服务 (API 直查)
      
      ### Brave Search API
      
      - **API Base**: `https://api.search.brave.com/res/v1`
      - **Auth**: `BRAVE_API_KEY`
      - **Usage API**: ❌ 无公开API,API调用会301重定向到dashboard
      - **Browser**: https://api-dashboard.search.brave.com
      
      ### Tavily API
      
      - **API Base**: `https://api.tavily.com`
      - **Auth**: `TAVILY_API_KEY`
      - **Usage API**:
        ```bash
        curl -s "https://api.tavily.com/balance" \
          -H "Authorization: Bearer $TAVILY_API_KEY"
        ```
      - **返回**: `{"balance": 1000, "used": 500}`
      
      ### Serper API
      
      - **API Base**: `https://serper.dev/api`
      - **Auth**: `SERPER_API_KEY`
      - **Usage API**:
        ```bash
        curl -s "https://serper.dev/api/account" \
          -H "X-API-KEY: $SERPER_API_KEY"
        ```
      
      ### Jina AI Reader
      
      - **API Base**: `https://r.jina.ai`
      - **Auth**: `JINA_API_KEY` (optional for free tier)
      - **Usage API**: 检查响应头 `X-RateLimit-Remaining`
      
      ---
      
      ## 本地缓存的供应商
      
      从 OpenClaw 配置和 auth 状态读取已配置的供应商:
      
      ```bash
      # 读取 OpenClaw config
      cat ~/.openclaw/openclaw.json | jq '.models'
      
      # 读取环境变量
      env | grep -iE "_API_KEY|_TOKEN"
      
      # 读取 auth 状态
      cat ~/.openclaw/auth-session-state.json
      ```
      
  • scripts
    • query_quota.py 16.7 KB
      #!/usr/bin/env python3
      """
      API Quota Monitor - Query provider balances/usage.
      
      Usage:
          python3 query_quota.py                    # Query all configured providers
          python3 query_quota.py xai                # Query specific provider
          python3 query_quota.py --type official    # Query only official providers
          python3 query_quota.py --type reseller    # Query only reseller platforms
          python3 query_quota.py --format json      # JSON output
          python3 query_quota.py --report           # Generate human-readable report
      """
      
      import os
      import sys
      import json
      import argparse
      import subprocess
      from datetime import datetime
      from typing import Optional, Dict, Any, List
      from urllib.request import Request, urlopen
      from urllib.error import URLError, HTTPError
      
      # Provider configurations
      OFFICIAL_PROVIDERS = {
          "zai": {
              "name": "ZAI (智谱)",
              "env_key": "ZAI_API_KEY",
              "alt_env_keys": ["ZHIPU_API_KEY"],
              "method": "browser",
              "browser_url": "https://open.bigmodel.cn/oa/userCenter",
              "note": "No public balance API, use console"
          },
          "minimax": {
              "name": "Minimax",
              "env_key": "MINIMAX_API_KEY",
              "extra_env": ["MINIMAX_GROUP_ID"],
              "api_url": "https://api.minimax.chat/v1/user/balance",
              "method": "api",
              "headers": lambda: {
                  "Authorization": f"Bearer {os.environ.get('MINIMAX_API_KEY', '')}",
                  "GroupId": os.environ.get("MINIMAX_GROUP_ID", ""),
              },
              "extract": lambda data: {
                  "balance": data.get("data", {}).get("balance", 0),
                  "unit": data.get("data", {}).get("unit", "CNY"),
              }
          },
          "openrouter": {
              "name": "OpenRouter",
              "env_key": "OPENROUTER_API_KEY",
              "api_url": "https://openrouter.ai/api/v1/auth/key",
              "method": "api",
              "extract": lambda data: {
                  "limit_remaining": data.get("data", {}).get("limit_remaining", 0),
                  "usage": data.get("data", {}).get("usage", 0),
              }
          },
          "xai": {
              "name": "xAI (Grok)",
              "env_key": "XAI_MGMT_KEY",  # Management API key (separate from inference key)
              "alt_env_keys": ["XAI_MANAGEMENT_KEY"],
              "extra_env": ["XAI_TEAM_ID"],
              "method": "api",
              "api_url": "https://management-api.x.ai/v1/billing/teams/{team_id}/prepaid/balance",
              "team_id_env": "XAI_TEAM_ID",
              "headers": lambda: {
                  "Authorization": f"Bearer {os.environ.get('XAI_MGMT_KEY', '')}",
              },
              "extract": lambda data: {
                  "prepaid_balance": float(data.get("total", {}).get("val", "0")) / -100,  # Negative cents to positive dollars
                  "currency": "USD",
              },
              "note": "Requires Management API key from console.x.ai -> Settings -> Management Keys"
          },
          "gemini": {
              "name": "Google AI (Gemini)",
              "env_key": "GOOGLE_API_KEY",
              "alt_env_keys": ["GEMINI_API_KEY"],
              "method": "browser",
              "browser_url": "https://aistudio.google.com/apikey",
              "note": "No public usage API"
          },
          "anthropic": {
              "name": "Anthropic (Claude)",
              "env_key": "ANTHROPIC_API_KEY",
              "method": "browser",
              "browser_url": "https://console.anthropic.com/settings/billing",
              "note": "No public usage API"
          },
      }
      
      SERVICE_PROVIDERS = {
          "brave": {
              "name": "Brave Search API",
              "env_key": "BRAVE_API_KEY",
              "method": "api_headers",
              "api_url": "https://api.search.brave.com/res/v1/web/search?q=test&count=1",
              "headers": lambda: {
                  "X-Subscription-Token": os.environ.get("BRAVE_API_KEY", ""),
                  "Accept": "application/json",
              },
              "extract_headers": lambda headers: {
                  "rate_limit": headers.get("x-ratelimit-limit", ""),
                  "rate_remaining": headers.get("x-ratelimit-remaining", ""),
                  "rate_reset_seconds": headers.get("x-ratelimit-reset", ""),
                  "rate_policy": headers.get("x-ratelimit-policy", ""),
              },
              "note": "Usage info from response headers (x-ratelimit-*)"
          },
          "tavily": {
              "name": "Tavily API",
              "env_key": "TAVILY_API_KEY",
              "api_url": "https://api.tavily.com/balance",
              "method": "api",
          },
          "serper": {
              "name": "Serper API",
              "env_key": "SERPER_API_KEY",
              "api_url": "https://serper.dev/api/account",
              "method": "api",
              "headers": lambda: {
                  "X-API-KEY": os.environ.get("SERPER_API_KEY", ""),
              },
          },
      }
      
      RESELLER_PROVIDERS = {
          "provider-b": {
              "name": "AIXN (XAPI)",
              "method": "browser",
              "browser_url": "https://ai.9w7.cn/console",
              "indicators": ["余额", "消耗", "tokens"],
          },
          "your-provider": {
              "name": "性价比API",
              "method": "browser",
              "browser_url": "https://your-provider.example.com/home",
              "indicators": ["余额", "令牌"],
          },
      }
      
      
      def get_api_key(provider: Dict) -> Optional[str]:
          """Get API key from environment."""
          key = os.environ.get(provider.get("env_key", ""))
          if not key and "alt_env_keys" in provider:
              for alt_key in provider["alt_env_keys"]:
                  key = os.environ.get(alt_key, "")
                  if key:
                      break
          return key
      
      
      def query_api_headers(provider: Dict, key: str) -> Dict[str, Any]:
          """Query provider via API and extract info from response headers."""
          url = provider["api_url"]
          
          # Build headers
          headers = {"User-Agent": "OpenClaw-API-Quota-Monitor/1.0"}
          if "headers" in provider:
              provider_headers = provider["headers"]()
              headers.update(provider_headers)
          if "Authorization" not in headers and key:
              headers["Authorization"] = f"Bearer {key}"
          
          try:
              req = Request(url, headers=headers)
              with urlopen(req, timeout=10) as response:
                  resp_headers = {k.lower(): v for k, v in response.getheaders()}
                  
                  result = {
                      "status": "ok",
                      "raw_headers": {k: v for k, v in resp_headers.items() if "ratelimit" in k or "quota" in k},
                  }
                  
                  if "extract_headers" in provider:
                      result.update(provider["extract_headers"](resp_headers))
                  
                  return result
          except HTTPError as e:
              # Even on error, check headers
              if hasattr(e, 'headers'):
                  resp_headers = {k.lower(): v for k, v in dict(e.headers).items()}
                  rate_headers = {k: v for k, v in resp_headers.items() if "ratelimit" in k}
                  if rate_headers:
                      result = {
                          "status": "ok",
                          "note": f"HTTP {e.code} but headers available",
                          "raw_headers": rate_headers,
                      }
                      if "extract_headers" in provider:
                          result.update(provider["extract_headers"](resp_headers))
                      return result
              return {"status": "error", "error": f"HTTP {e.code}: {e.reason}"}
          except Exception as e:
              return {"status": "error", "error": str(e)}
      
      
      def query_api(provider: Dict, key: str) -> Dict[str, Any]:
          """Query provider via API."""
          url = provider["api_url"]
          
          # Handle team_id substitution in URL
          if "{team_id}" in url:
              team_id_env = provider.get("team_id_env", "XAI_TEAM_ID")
              team_id = os.environ.get(team_id_env, "")
              if not team_id:
                  return {
                      "status": "error",
                      "error": f"Missing {team_id_env} environment variable",
                  }
              url = url.replace("{team_id}", team_id)
          
          # Build headers - start with default User-Agent (some APIs require it)
          headers = {
              "User-Agent": "OpenClaw-API-Quota-Monitor/1.0"
          }
          # Add provider-specific headers
          if "headers" in provider:
              provider_headers = provider["headers"]()
              headers.update(provider_headers)
          # Add default Authorization if not already set
          if "Authorization" not in headers and key:
              headers["Authorization"] = f"Bearer {key}"
          
          try:
              req = Request(url, headers=headers)
              with urlopen(req, timeout=10) as response:
                  data = json.loads(response.read().decode())
                  
                  result = {
                      "status": "ok",
                      "raw": data,
                  }
                  
                  if "extract" in provider:
                      result.update(provider["extract"](data))
                  
                  return result
          except HTTPError as e:
              return {
                  "status": "error",
                  "error": f"HTTP {e.code}: {e.reason}",
              }
          except URLError as e:
              return {
                  "status": "error",
                  "error": str(e.reason),
              }
          except Exception as e:
              return {
                  "status": "error",
                  "error": str(e),
              }
      
      
      def query_browser(provider: Dict) -> Dict[str, Any]:
          """Query provider via browser (placeholder - actual implementation uses browser tool)."""
          return {
              "status": "browser_required",
              "url": provider.get("browser_url", ""),
              "indicators": provider.get("indicators", []),
              "note": provider.get("note", "Use browser tool to check"),
          }
      
      
      def query_provider(provider_id: str, provider: Dict) -> Dict[str, Any]:
          """Query a single provider."""
          result = {
              "id": provider_id,
              "name": provider.get("name", provider_id),
              "queried_at": datetime.now().isoformat(),
          }
          
          # Check if API key exists
          key = get_api_key(provider)
          method = provider.get("method", "api")
          
          if method == "api":
              if not key:
                  result["status"] = "no_key"
                  result["note"] = f"Set {provider.get('env_key', 'API_KEY')} environment variable"
              else:
                  api_result = query_api(provider, key)
                  result.update(api_result)
          elif method == "api_headers":
              if not key:
                  result["status"] = "no_key"
                  result["note"] = f"Set {provider.get('env_key', 'API_KEY')} environment variable"
              else:
                  api_result = query_api_headers(provider, key)
                  result.update(api_result)
          elif method == "browser":
              if not key:
                  result["status"] = "no_key"
                  result["note"] = f"Set {provider.get('env_key', 'API_KEY')} or use browser automation"
              else:
                  # Has key but no usage API - need browser
                  browser_result = query_browser(provider)
                  result.update(browser_result)
          else:
              result["status"] = "unknown_method"
          
          return result
      
      
      def discover_configured_providers() -> Dict[str, List[str]]:
          """Discover which providers are configured based on environment variables."""
          configured = {"official": [], "service": [], "reseller": []}
          
          for pid, p in OFFICIAL_PROVIDERS.items():
              if get_api_key(p):
                  configured["official"].append(pid)
          
          for pid, p in SERVICE_PROVIDERS.items():
              if get_api_key(p):
                  configured["service"].append(pid)
          
          # Resellers are checked via auth-session-state.json
          auth_state = os.path.expanduser("~/.openclaw/auth-session-state.json")
          if os.path.exists(auth_state):
              try:
                  with open(auth_state) as f:
                      state = json.load(f)
                  for pid in RESELLER_PROVIDERS:
                      if pid in state and state[pid].get("status") == "ok":
                          configured["reseller"].append(pid)
              except:
                  pass
          
          return configured
      
      
      def generate_report(results: List[Dict], format: str = "text") -> str:
          """Generate a report from query results."""
          if format == "json":
              return json.dumps(results, indent=2, ensure_ascii=False)
          
          lines = ["📊 API Quota Report", f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M')}", ""]
          
          # Group by type
          official = [r for r in results if r["id"] in OFFICIAL_PROVIDERS]
          services = [r for r in results if r["id"] in SERVICE_PROVIDERS]
          resellers = [r for r in results if r["id"] in RESELLER_PROVIDERS]
          
          if official:
              lines.append("━━ 官方供应商 ━━")
              for r in official:
                  status_icon = "✅" if r.get("status") == "ok" else "❌" if r.get("status") == "error" else "⚠️"
                  # Check various balance field names
                  balance = r.get("balance", r.get("prepaid_balance", r.get("limit_remaining", None)))
                  unit = r.get("unit", r.get("currency", ""))
                  if balance is not None and isinstance(balance, (int, float)):
                      if unit:
                          lines.append(f"{status_icon} {r['name']}: ${balance:.2f} {unit}")
                      else:
                          lines.append(f"{status_icon} {r['name']}: ${balance:.2f}")
                  else:
                      lines.append(f"{status_icon} {r['name']}: {r.get('status', '-')}")
              lines.append("")
          
          if services:
              lines.append("━━ 订阅服务 ━━")
              for r in services:
                  status_icon = "✅" if r.get("status") == "ok" else "❌" if r.get("status") == "error" else "⚠️"
                  # Handle rate limit info from headers (e.g., Brave)
                  rate_remaining = r.get("rate_remaining", "")
                  rate_limit = r.get("rate_limit", "")
                  if rate_remaining and rate_limit:
                      # Parse multi-value rate limits: "49, 0" → burst, monthly
                      parts_remaining = [p.strip() for p in str(rate_remaining).split(",")]
                      parts_limit = [p.strip() for p in str(rate_limit).split(",")]
                      if len(parts_remaining) >= 2 and len(parts_limit) >= 2:
                          monthly_remaining = parts_remaining[1]
                          monthly_limit = parts_limit[1]
                          # 0/0 means unlimited or not tracked
                          if monthly_limit == "0" and monthly_remaining == "0":
                              burst_remaining = parts_remaining[0]
                              burst_limit = parts_limit[0]
                              lines.append(f"{status_icon} {r['name']}: ✓ active (burst: {burst_remaining}/{burst_limit} req/s)")
                          else:
                              lines.append(f"{status_icon} {r['name']}: 月剩余 {monthly_remaining}/{monthly_limit}")
                      else:
                          lines.append(f"{status_icon} {r['name']}: remaining {rate_remaining}")
                  else:
                      balance = r.get("balance", r.get("limit_remaining", "-"))
                      lines.append(f"{status_icon} {r['name']}: {balance}")
              lines.append("")
          
          if resellers:
              lines.append("━━ 中转站 (浏览器) ━━")
              for r in resellers:
                  status_icon = "✅" if r.get("status") == "ok" else "🔍"
                  lines.append(f"{status_icon} {r['name']}: {r.get('status', '-')}")
              lines.append("")
          
          return "\n".join(lines)
      
      
      def main():
          parser = argparse.ArgumentParser(description="Query API provider quotas")
          parser.add_argument("provider", nargs="?", help="Specific provider to query")
          parser.add_argument("--type", choices=["official", "service", "reseller", "all"], 
                             default="all", help="Provider type to query")
          parser.add_argument("--format", choices=["text", "json"], default="text",
                             help="Output format")
          parser.add_argument("--report", action="store_true", help="Generate report")
          parser.add_argument("--discover", action="store_true", help="Only discover configured providers")
          
          args = parser.parse_args()
          
          if args.discover:
              configured = discover_configured_providers()
              print(json.dumps(configured, indent=2))
              return
          
          results = []
          
          if args.provider:
              # Query specific provider
              all_providers = {**OFFICIAL_PROVIDERS, **SERVICE_PROVIDERS, **RESELLER_PROVIDERS}
              if args.provider in all_providers:
                  results.append(query_provider(args.provider, all_providers[args.provider]))
              else:
                  print(f"Unknown provider: {args.provider}")
                  print(f"Available: {', '.join(all_providers.keys())}")
                  sys.exit(1)
          else:
              # Query all configured providers
              if args.type in ["official", "all"]:
                  for pid, p in OFFICIAL_PROVIDERS.items():
                      if get_api_key(p):
                          results.append(query_provider(pid, p))
              
              if args.type in ["service", "all"]:
                  for pid, p in SERVICE_PROVIDERS.items():
                      if get_api_key(p):
                          results.append(query_provider(pid, p))
              
              if args.type in ["reseller", "all"]:
                  for pid, p in RESELLER_PROVIDERS.items():
                      results.append(query_provider(pid, p))
          
          if args.format == "json":
              print(json.dumps(results, indent=2, ensure_ascii=False))
          else:
              print(generate_report(results, "text"))
      
      
      if __name__ == "__main__":
          main()
      
  • SKILL.md 4.5 KB
    ---
    name: api-quota-monitor
    description: "查询监控 API 供应商/服务的额度、余额、消耗情况。支持官方供应商(Gemini/xAI/ZAI/Minimax/OpenRouter等)、中转站(AIXN/Provider-A等)、订阅服务(Brave/Tavily/Serper)。触发词:查额度、查余额、API用量、供应商额度、quota、balance、usage、billing。"
    author: Daniel Li
    ---
    
    # API Quota Monitor
    
    查询监控各类 API 供应商和订阅服务的额度/余额/消耗。
    
    ## 快速使用
    
    ```bash
    # 查询所有已配置的供应商
    python3 scripts/query_quota.py
    
    # 查询特定供应商
    python3 scripts/query_quota.py xai
    python3 scripts/query_quota.py openrouter
    python3 scripts/query_quota.py brave
    
    # 只查官方供应商
    python3 scripts/query_quota.py --type official
    
    # 只查订阅服务
    python3 scripts/query_quota.py --type service
    
    # JSON 输出
    python3 scripts/query_quota.py --format json
    
    # 发现已配置的供应商
    python3 scripts/query_quota.py --discover
    ```
    
    ## 工作流
    
    ```
    1. 发现本地已配置的 API/供应商(从环境变量、auth-session-state.json)
       ↓
    2. 按类型查询:API 直查 > 浏览器抓取
       ↓
    3. 生成报告:供应商 | 消耗 | 总额度 | 状态
    ```
    
    ## 供应商分类
    
    ### 官方供应商 (Official)
    
    | 供应商 | 查询方式 | 环境变量 | 状态 |
    |--------|----------|----------|------|
    | ZAI (智谱) | API 直查 | `ZAI_API_KEY` | ✅ 支持 |
    | Minimax | API 直查 | `MINIMAX_API_KEY` + `MINIMAX_GROUP_ID` | ✅ 支持 |
    | OpenRouter | API 直查 | `OPENROUTER_API_KEY` | ✅ 支持 |
    | xAI (Grok) | 浏览器 | `XAI_API_KEY` | 🔍 需浏览器 |
    | Gemini | 浏览器 | `GOOGLE_API_KEY` | 🔍 需浏览器 |
    | Anthropic | 浏览器 | `ANTHROPIC_API_KEY` | 🔍 需浏览器 |
    
    ### 中转站 (Reseller)
    
    | 平台 | 查询方式 | 状态来源 |
    |------|----------|----------|
    | AIXN | 浏览器抓 ai.9w7.cn | auth-session-state.json |
    | Provider-A | 浏览器抓 your-provider.example.com | auth-session-state.json |
    
    中转站查询流程:
    1. 检查 `~/.openclaw/auth-session-state.json` 获取登录状态
    2. 使用 `browser(action='navigate', profile='openclaw')` 访问控制台
    3. 从 snapshot 提取余额/消耗信息
    
    ### 订阅服务 (Services)
    
    | 服务 | 查询方式 | 环境变量 |
    |------|----------|----------|
    | Brave Search API | API 直查 | `BRAVE_API_KEY` |
    | Tavily API | API 直查 | `TAVILY_API_KEY` |
    | Serper API | API 直查 | `SERPER_API_KEY` |
    
    ## 浏览器查询 (需浏览器抓取的供应商)
    
    当供应商没有公开的 usage API 时,使用 browser 工具:
    
    ```
    # 1. 导航到控制台页面
    browser(action='navigate', profile='openclaw', targetUrl='<console_url>')
    
    # 2. 等待加载后获取快照
    browser(action='snapshot', profile='openclaw', compact=true, maxChars=2000)
    
    # 3. 从快照中提取余额/消耗信息
    # 查找关键词:余额、balance、usage、消耗、tokens、额度
    ```
    
    **各供应商控制台 URL:**
    - xAI: https://console.x.ai/usage
    - Gemini: https://aistudio.google.com/apikey
    - Anthropic: https://console.anthropic.com/settings/billing
    - AIXN: https://ai.9w7.cn/console
    - Provider-A: https://your-provider.example.com/dashboard
    
    ## 报告格式
    
    ```
    📊 API Quota Report
    ⏰ 2026-03-15 20:30
    
    ━━ 官方供应商 ━━
    ✅ ZAI (智谱): ¥50.00
    ✅ Minimax: ¥10.00 CNY
    ✅ OpenRouter: $45.00 remaining
    🔍 xAI (Grok): 需浏览器查询
    
    ━━ 订阅服务 ━━
    ✅ Brave Search: 1500/2000 requests
    ✅ Tavily: 800 remaining
    
    ━━ 中转站 ━━
    ✅ AIXN: ¥238.81
    ✅ Provider-A: ¥9.35
    ```
    
    ## 扩展新供应商
    
    在 `scripts/query_quota.py` 中添加新供应商配置:
    
    ```python
    OFFICIAL_PROVIDERS = {
        "new_provider": {
            "name": "New Provider",
            "env_key": "NEW_PROVIDER_API_KEY",
            "api_url": "https://api.newprovider.com/v1/usage",
            "method": "api",  # 或 "browser"
            "extract": lambda data: {
                "balance": data.get("balance", 0),
            }
        },
    }
    ```
    
    ## 缓存策略
    
    - API 查询结果可缓存 1-6 小时(避免频繁请求)
    - 缓存文件: `~/.openclaw/quota-cache.json`
    - 格式:
    ```json
    {
      "openrouter": {
        "queried_at": "2026-03-15T20:00:00",
        "balance": 45.0,
        "cached": true
      }
    }
    ```
    
    ## 定时监控
    
    可配置 cron 定期检查并推送报告:
    
    ```bash
    # 在 OpenClaw cron 中添加
    # 每 6 小时检查一次 API 供应商额度
    0 */6 * * * python3 ~/clawd/skills/api-quota-monitor/scripts/query_quota.py --report | newsbot_send
    ```
    
    ## 参考文档
    
    详细的 API 端点和响应格式见 [references/providers.md](references/providers.md)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related