Back to skills
SkillHub ClubShip Full StackFull StackBackend

api-credits-lite

Display API credit balances for 5 core providers (Anthropic, OpenAI, OpenRouter, Mistral, Groq) with video game style health bars. API auto-checks and manual sync.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,074
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
F35.9

Install command

npx @skill-hub/cli install openclaw-skills-api-credits-lite

Repository

openclaw/skills

Skill path: skills/franciscobuiltdat/api-credits-lite

Display API credit balances for 5 core providers (Anthropic, OpenAI, OpenRouter, Mistral, Groq) with video game style health bars. API auto-checks and manual sync.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Backend.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install api-credits-lite into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding api-credits-lite to shared team environments
  • Use api-credits-lite for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: api-credits-lite
description: Display API credit balances for 5 core providers (Anthropic, OpenAI, OpenRouter, Mistral, Groq) with video game style health bars. API auto-checks and manual sync.
optionalEnv:
  - OPENAI_API_KEY
  - OPENROUTER_API_KEY
  - VERCEL_AI_GATEWAY_KEY
permissions:
  - network: Contact OpenAI, OpenRouter, and Vercel APIs to check balances (optional)
  - filesystem: Read/write config.json and health bar display
---

# API Credits Lite

Use this skill when the user asks about API credits, balances, spending, or wants to update their credit info for Anthropic, OpenAI, OpenRouter, Mistral, or Groq.

## When to Use

โœ… **USE this skill when the user asks:**

- "How much credit do I have left?" / "What's my balance?"
- "Show my API credits" / "Check my credits"
- "Update my [provider] balance to $X"
- "I topped up [provider] by $X"
- "Am I running low on [provider]?"

โŒ **DON'T use when:**
- The user needs 16+ providers, JSONL auto-tracking, cloud SDKs, or heartbeat integration โ†’ use **api-credits-pro**

## How to Use

You run the scripts internally โ€” the user never types `python3`. Respond naturally and present health bar output conversationally.

The skill root is at: `~/.openclaw/workspace/skills/api-credits-lite/`
Run scripts with: `python3 <skill-root>/scripts/<script>.py <args>`

---

## Show Credit Balances

**Triggers:** "show my credits", "how much do I have left", "check my API balance"

```bash
python3 scripts/show_credits.py
```

Displays retro health bars for all configured providers. Colors: ๐ŸŸฉ >75% ยท ๐ŸŸจ 50โ€“75% ยท ๐ŸŸง 25โ€“50% ยท ๐ŸŸฅ <25%

---

## Manual Balance Sync

**Triggers:** "set my Anthropic balance to $X", "update my OpenAI credits to $X", "I have $X left on [provider]"

```bash
python3 scripts/sync_provider.py <provider> <balance>
# With max:  python3 scripts/sync_provider.py <provider> <balance> <max_credits>

# Examples:
python3 scripts/sync_provider.py anthropic 22.97
python3 scripts/sync_provider.py openai 95.00 100.00
```

Supported providers: `anthropic`, `openai`, `openrouter`, `mistral`, `groq`

Where to find balances:
- **Anthropic** โ†’ console.anthropic.com
- **OpenAI** โ†’ platform.openai.com/usage
- **OpenRouter** โ†’ openrouter.ai/activity
- **Mistral** โ†’ console.mistral.ai/billing
- **Groq** โ†’ console.groq.com/settings/billing

---

## Auto-Check Balance via API

**Triggers:** "check my OpenAI balance automatically", "pull my OpenRouter credits"

For providers that expose a balance API (requires the relevant API key set as an env var):

```bash
python3 scripts/check_openai.py       # uses OPENAI_API_KEY
python3 scripts/check_openrouter.py   # uses OPENROUTER_API_KEY
python3 scripts/check_vercel.py       # uses VERCEL_AI_GATEWAY_KEY
```

Anthropic, Mistral, and Groq don't have public balance APIs โ€” use manual sync for those.

---

## Record a Top-Up

**Triggers:** "I added $X to my [provider] account", "I topped up $50 on OpenRouter"

```bash
python3 scripts/topup.py <provider> <amount>
# Example: python3 scripts/topup.py openrouter 20.00
```

---

## Troubleshooting

If the display shows no providers, the config may be empty. Tell the user to run the first manual sync for each provider they use, or copy the example config:

```bash
cp config.example.json config.json
```

---

## Upgrading

Need 16+ providers, auto-tracking from session logs, cloud SDKs (AWS/Azure/GCP), or heartbeat integration? Suggest **api-credits-pro**.


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/show_credits.py

```python
#!/usr/bin/env python3
"""
Simple, fast credit display - just reads config and shows health bars.
No complex processing, designed for quick viewing.
"""

import json
import os
import sys
from datetime import datetime
from render_healthbar import format_credits

# Supported providers in lite version
SUPPORTED_PROVIDERS = ['anthropic', 'openai', 'openrouter', 'mistral', 'groq']

def show_credits():
    """Read config and display all enabled providers."""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'config.json')
    
    if not os.path.exists(config_path):
        print("โš ๏ธ No config.json found. Copy config.example.json and edit it.")
        return 1
    
    with open(config_path, 'r') as f:
        config = json.load(f)
    
    print('๐Ÿ’ฐ API Credit Health')
    print('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”')
    print()
    
    warnings = []
    thresholds = config.get('thresholds', {'warning': 50, 'critical': 25})
    
    providers = config.get('providers', {})
    if not providers:
        print("โš ๏ธ  No providers configured.")
        print("   Copy config.example.json โ†’ config.json and add your providers.")
        return 0

    # Show each enabled provider (lite version: 5 core providers only)
    for provider_name, provider_config in providers.items():
        if provider_name.lower() not in SUPPORTED_PROVIDERS:
            continue
        
        if not provider_config.get('enabled', False):
            continue
        
        provider_display = provider_name.title()
        current = provider_config.get('current_credits', provider_config['max_credits'])
        max_credits = provider_config['max_credits']
        last_sync = provider_config.get('last_sync')
        
        # Format last sync time
        sync_str = None
        if last_sync:
            try:
                sync_time = datetime.fromisoformat(last_sync.replace('Z', '+00:00'))
                now = datetime.now(sync_time.tzinfo)
                diff = now - sync_time
                
                if diff.total_seconds() < 60:
                    sync_str = 'just now'
                elif diff.total_seconds() < 3600:
                    mins = int(diff.total_seconds() / 60)
                    sync_str = f'{mins}m ago'
                elif diff.total_seconds() < 86400:
                    hours = int(diff.total_seconds() / 3600)
                    sync_str = f'{hours}h ago'
                else:
                    days = int(diff.total_seconds() / 86400)
                    sync_str = f'{days}d ago'
            except:
                sync_str = 'recently'
        
        print(format_credits(provider_display, current, max_credits, last_sync=sync_str))
        print()
        
        # Check thresholds
        pct = (current / max_credits * 100) if max_credits > 0 else 0
        if pct <= thresholds['critical']:
            warnings.append(f'๐Ÿšจ {provider_display} is critical!')
        elif pct <= thresholds['warning']:
            warnings.append(f'โš ๏ธ {provider_display} is low')
    
    # Show warnings
    if warnings:
        print('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”')
        for w in warnings:
            print(w)
    
    return 0

if __name__ == "__main__":
    sys.exit(show_credits())

```

### scripts/sync_provider.py

```python
#!/usr/bin/env python3
"""
Manual sync for 5 core providers.
Use to update balance from provider console.

Usage:
    sync_provider.py <provider> <current_balance> [max_credits]

Examples:
    sync_provider.py anthropic 45.00
    sync_provider.py openai 95.00 100.00
"""

import os
import json
import sys
from datetime import datetime

# Supported providers (lite version)
PROVIDER_INFO = {
    'anthropic': {
        'console_url': 'https://console.anthropic.com',
        'notes': 'Check console.anthropic.com โ†’ Settings โ†’ Billing for current balance'
    },
    'openai': {
        'console_url': 'https://platform.openai.com/usage',
        'notes': 'Check platform.openai.com/usage for balance and usage'
    },
    'openrouter': {
        'console_url': 'https://openrouter.ai/activity',
        'notes': 'Check openrouter.ai/activity for current credits'
    },
    'mistral': {
        'console_url': 'https://console.mistral.ai/billing',
        'notes': 'Check console.mistral.ai โ†’ Billing for current balance'
    },
    'groq': {
        'console_url': 'https://console.groq.com/settings/billing',
        'notes': 'Check console.groq.com โ†’ Settings โ†’ Billing for usage'
    }
}

def load_config():
    """Load config from the skill directory"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'config.json')
    
    try:
        with open(config_path, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        return {'providers': {}, 'thresholds': {'warning': 50, 'critical': 25}}

def save_config(config):
    """Save config back to file"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'config.json')
    
    with open(config_path, 'w') as f:
        json.dump(config, f, indent=2)

def sync_balance(provider: str, current_balance: float, max_credits: float = None):
    """
    Sync balance for a provider.
    
    Args:
        provider: Provider name (e.g., 'anthropic', 'openai')
        current_balance: Current balance from console
        max_credits: Optional max credits (creates if not exists)
    """
    config = load_config()
    
    # Normalize provider name
    provider_key = provider.lower().replace('-', '_')
    
    # Check if supported
    if provider_key not in PROVIDER_INFO:
        print(f"โŒ Provider '{provider}' not supported in lite version.")
        print(f"   Supported: {', '.join(PROVIDER_INFO.keys())}")
        print(f"   Upgrade to Pro for 16+ providers.")
        sys.exit(1)
    
    info = PROVIDER_INFO[provider_key]
    
    # Check if provider exists in config
    if provider_key not in config.get('providers', {}):
        config.setdefault('providers', {})
        config['providers'][provider_key] = {
            'enabled': True,
            'max_credits': max_credits or current_balance,
            'current_credits': current_balance,
            'last_sync': datetime.now().astimezone().isoformat(),
            'notes': info.get('notes', f'Manual sync - check {provider} console')
        }
    else:
        config['providers'][provider_key]['current_credits'] = current_balance
        config['providers'][provider_key]['last_sync'] = datetime.now().astimezone().isoformat()
        config['providers'][provider_key]['enabled'] = True
        
        if max_credits:
            config['providers'][provider_key]['max_credits'] = max_credits
    
    save_config(config)
    
    result = {
        'status': 'synced',
        'provider': provider_key,
        'current_credits': current_balance,
        'max_credits': config['providers'][provider_key].get('max_credits'),
        'last_sync': config['providers'][provider_key]['last_sync'],
        'console_url': info['console_url']
    }
    
    return result

def list_providers():
    """List all supported providers with their console URLs."""
    print("Supported providers (Lite version):\n")
    for provider, info in sorted(PROVIDER_INFO.items()):
        print(f"  {provider}")
        print(f"    Console: {info.get('console_url', 'N/A')}")
        print(f"    Notes: {info.get('notes', 'N/A')}")
        print()
    print("\n๐Ÿ’ก Need more providers? Upgrade to api-credits-pro")

if __name__ == "__main__":
    if len(sys.argv) < 2 or sys.argv[1] in ['-h', '--help']:
        print("Usage: sync_provider.py <provider> <current_balance> [max_credits]")
        print("       sync_provider.py --list")
        print("\nSupported providers: anthropic, openai, openrouter, mistral, groq")
        print("\nExamples:")
        print("  sync_provider.py anthropic 45.00")
        print("  sync_provider.py openai 85.00 100.00")
        print("\nUse --list to see all providers and their console URLs.")
        sys.exit(0)
    
    if sys.argv[1] == '--list':
        list_providers()
        sys.exit(0)
    
    if len(sys.argv) < 3:
        print("Error: Both provider and balance are required")
        print("Usage: sync_provider.py <provider> <current_balance> [max_credits]")
        sys.exit(1)
    
    provider = sys.argv[1]
    balance = float(sys.argv[2])
    max_credits = float(sys.argv[3]) if len(sys.argv) > 3 else None
    
    result = sync_balance(provider, balance, max_credits)
    print(json.dumps(result, indent=2))

```

### scripts/check_openai.py

```python
#!/usr/bin/env python3\n\"\"\"\nCheck OpenAI credits balance via API.\n\nRequires: OPENAI_API_KEY environment variable (org admin key, not user key)\n\nUsage:\n    OPENAI_API_KEY=sk-... python3 check_openai.py\n    OPENAI_API_KEY=sk-... python3 check_openai.py --update\n\"\"\"\n\nimport os\nimport json\nimport sys\nfrom datetime import datetime\n\ntry:\n    from openai import OpenAI\nexcept ImportError:\n    print(\"โŒ OpenAI library not installed. Install with: pip install openai\")\n    sys.exit(1)\n\ndef load_config():\n    \"\"\"Load config from the skill directory\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    try:\n        with open(config_path, 'r') as f:\n            return json.load(f)\n    except FileNotFoundError:\n        return {'providers': {}, 'thresholds': {'warning': 50, 'critical': 25}}\n\ndef save_config(config):\n    \"\"\"Save config back to file\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    with open(config_path, 'w') as f:\n        json.dump(config, f, indent=2)\n\ndef check_openai_balance(api_key):\n    \"\"\"\n    Check OpenAI balance using API.\n    \n    Requires: Organization admin API key (not user key)\n    Returns: (total_balance, date)\n    \"\"\"\n    try:\n        client = OpenAI(api_key=api_key, organization=None)\n        \n        # Get billing info\n        billing_info = client.beta.billing.credit_grants.list()\n        \n        total_balance = 0\n        for grant in billing_info.data:\n            if hasattr(grant, 'balance'):\n                total_balance += float(grant.balance)\n        \n        return total_balance, datetime.now().isoformat()\n    \n    except Exception as e:\n        print(f\"โŒ Failed to check OpenAI balance: {str(e)}\")\n        print(\"\\n๐Ÿ’ก Note: This requires an organization admin API key, not a user key.\")\n        print(\"   Get it from: https://platform.openai.com/account/api-keys (select org)\")\n        return None, None\n\ndef check_and_display(api_key, update=False):\n    \"\"\"\n    Check balance and optionally update config.\n    \"\"\"\n    print(\"๐Ÿ” Checking OpenAI balance...\")\n    \n    balance, timestamp = check_openai_balance(api_key)\n    \n    if balance is None:\n        return 1\n    \n    print(f\"โœ… OpenAI balance: ${balance:.2f}\")\n    print(f\"   Checked: {timestamp}\")\n    \n    if update:\n        config = load_config()\n        if 'openai' not in config.get('providers', {}):\n            config.setdefault('providers', {})['openai'] = {\n                'enabled': True,\n                'max_credits': balance,\n                'current_credits': balance,\n                'api_key_env': 'OPENAI_API_KEY',\n                'notes': 'Auto-tracked via OpenAI API'\n            }\n        else:\n            config['providers']['openai']['current_credits'] = balance\n            config['providers']['openai']['last_sync'] = timestamp\n        \n        save_config(config)\n        print(\"   โœ… Config updated\")\n    \n    return 0\n\nif __name__ == \"__main__\":\n    api_key = os.getenv('OPENAI_API_KEY')\n    \n    if not api_key:\n        print(\"โŒ OPENAI_API_KEY environment variable not set\")\n        print(\"\\nUsage:\")\n        print(\"  OPENAI_API_KEY=sk-... python3 check_openai.py\")\n        print(\"  OPENAI_API_KEY=sk-... python3 check_openai.py --update\")\n        sys.exit(1)\n    \n    update = '--update' in sys.argv\n    \n    sys.exit(check_and_display(api_key, update=update))\n"

```

### scripts/check_openrouter.py

```python
#!/usr/bin/env python3\n\"\"\"\nCheck OpenRouter credits balance via API.\n\nRequires: OPENROUTER_API_KEY environment variable\n\nUsage:\n    OPENROUTER_API_KEY=sk-... python3 check_openrouter.py\n    OPENROUTER_API_KEY=sk-... python3 check_openrouter.py --update\n\"\"\"\n\nimport os\nimport json\nimport sys\nfrom datetime import datetime\n\ntry:\n    import requests\nexcept ImportError:\n    print(\"โŒ requests library not installed. Install with: pip install requests\")\n    sys.exit(1)\n\ndef load_config():\n    \"\"\"Load config from the skill directory\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    try:\n        with open(config_path, 'r') as f:\n            return json.load(f)\n    except FileNotFoundError:\n        return {'providers': {}, 'thresholds': {'warning': 50, 'critical': 25}}\n\ndef save_config(config):\n    \"\"\"Save config back to file\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    with open(config_path, 'w') as f:\n        json.dump(config, f, indent=2)\n\ndef check_openrouter_balance(api_key):\n    \"\"\"\n    Check OpenRouter credits using API.\n    \n    Returns: (total_credits, limit, timestamp)\n    \"\"\"\n    try:\n        response = requests.get(\n            'https://openrouter.ai/api/v1/auth/key',\n            headers={\n                'Authorization': f'Bearer {api_key}',\n                'Content-Type': 'application/json'\n            }\n        )\n        \n        if response.status_code != 200:\n            print(f\"โŒ API error: {response.status_code}\")\n            if response.status_code == 401:\n                print(\"   Invalid API key\")\n            return None, None, None\n        \n        data = response.json()\n        \n        # OpenRouter returns credits and limit\n        credits = float(data.get('balance', data.get('credits', 0)))\n        limit = float(data.get('limit', credits * 2))  # Estimate if not provided\n        \n        return credits, limit, datetime.now().isoformat()\n    \n    except Exception as e:\n        print(f\"โŒ Failed to check OpenRouter balance: {str(e)}\")\n        return None, None, None\n\ndef check_and_display(api_key, update=False):\n    \"\"\"\n    Check balance and optionally update config.\n    \"\"\"\n    print(\"๐Ÿ” Checking OpenRouter balance...\")\n    \n    credits, limit, timestamp = check_openrouter_balance(api_key)\n    \n    if credits is None:\n        return 1\n    \n    print(f\"โœ… OpenRouter balance: ${credits:.2f}\")\n    print(f\"   Limit: ${limit:.2f}\")\n    print(f\"   Checked: {timestamp}\")\n    \n    if update:\n        config = load_config()\n        if 'openrouter' not in config.get('providers', {}):\n            config.setdefault('providers', {})['openrouter'] = {\n                'enabled': True,\n                'max_credits': limit,\n                'current_credits': credits,\n                'api_key_env': 'OPENROUTER_API_KEY',\n                'last_sync': timestamp,\n                'notes': 'Auto-tracked via OpenRouter API'\n            }\n        else:\n            config['providers']['openrouter']['current_credits'] = credits\n            config['providers']['openrouter']['max_credits'] = limit\n            config['providers']['openrouter']['last_sync'] = timestamp\n        \n        save_config(config)\n        print(\"   โœ… Config updated\")\n    \n    return 0\n\nif __name__ == \"__main__\":\n    api_key = os.getenv('OPENROUTER_API_KEY')\n    \n    if not api_key:\n        print(\"โŒ OPENROUTER_API_KEY environment variable not set\")\n        print(\"\\nUsage:\")\n        print(\"  OPENROUTER_API_KEY=sk-... python3 check_openrouter.py\")\n        print(\"  OPENROUTER_API_KEY=sk-... python3 check_openrouter.py --update\")\n        sys.exit(1)\n    \n    update = '--update' in sys.argv\n    \n    sys.exit(check_and_display(api_key, update=update))\n"

```

### scripts/check_vercel.py

```python
#!/usr/bin/env python3\n\"\"\"\nCheck Vercel AI Gateway balance via API.\n\nRequires: VERCEL_AI_GATEWAY_KEY environment variable\n\nUsage:\n    VERCEL_AI_GATEWAY_KEY=... python3 check_vercel.py\n    VERCEL_AI_GATEWAY_KEY=... python3 check_vercel.py --update\n\"\"\"\n\nimport os\nimport json\nimport sys\nfrom datetime import datetime\n\ntry:\n    import requests\nexcept ImportError:\n    print(\"โŒ requests library not installed. Install with: pip install requests\")\n    sys.exit(1)\n\ndef load_config():\n    \"\"\"Load config from the skill directory\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    try:\n        with open(config_path, 'r') as f:\n            return json.load(f)\n    except FileNotFoundError:\n        return {'providers': {}, 'thresholds': {'warning': 50, 'critical': 25}}\n\ndef save_config(config):\n    \"\"\"Save config back to file\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    with open(config_path, 'w') as f:\n        json.dump(config, f, indent=2)\n\ndef check_vercel_balance(api_key):\n    \"\"\"\n    Check Vercel AI Gateway balance using API.\n    \n    Returns: (balance, limit, timestamp)\n    \"\"\"\n    try:\n        response = requests.get(\n            'https://api.vercel.com/v1/billing',\n            headers={\n                'Authorization': f'Bearer {api_key}',\n                'Content-Type': 'application/json'\n            }\n        )\n        \n        if response.status_code != 200:\n            print(f\"โŒ API error: {response.status_code}\")\n            if response.status_code == 401:\n                print(\"   Invalid API key\")\n            return None, None, None\n        \n        data = response.json()\n        \n        # Vercel returns balance and usage info\n        balance = float(data.get('balance', 0))\n        limit = float(data.get('limit', balance * 2))\n        \n        return balance, limit, datetime.now().isoformat()\n    \n    except Exception as e:\n        print(f\"โŒ Failed to check Vercel balance: {str(e)}\")\n        return None, None, None\n\ndef check_and_display(api_key, update=False):\n    \"\"\"\n    Check balance and optionally update config.\n    \"\"\"\n    print(\"๐Ÿ” Checking Vercel AI Gateway balance...\")\n    \n    balance, limit, timestamp = check_vercel_balance(api_key)\n    \n    if balance is None:\n        return 1\n    \n    print(f\"โœ… Vercel balance: ${balance:.2f}\")\n    print(f\"   Limit: ${limit:.2f}\")\n    print(f\"   Checked: {timestamp}\")\n    \n    if update:\n        config = load_config()\n        if 'vercel' not in config.get('providers', {}):\n            config.setdefault('providers', {})['vercel'] = {\n                'enabled': True,\n                'max_credits': limit,\n                'current_credits': balance,\n                'api_key_env': 'VERCEL_AI_GATEWAY_KEY',\n                'last_sync': timestamp,\n                'notes': 'Auto-tracked via Vercel API'\n            }\n        else:\n            config['providers']['vercel']['current_credits'] = balance\n            config['providers']['vercel']['max_credits'] = limit\n            config['providers']['vercel']['last_sync'] = timestamp\n        \n        save_config(config)\n        print(\"   โœ… Config updated\")\n    \n    return 0\n\nif __name__ == \"__main__\":\n    api_key = os.getenv('VERCEL_AI_GATEWAY_KEY')\n    \n    if not api_key:\n        print(\"โŒ VERCEL_AI_GATEWAY_KEY environment variable not set\")\n        print(\"\\nUsage:\")\n        print(\"  VERCEL_AI_GATEWAY_KEY=... python3 check_vercel.py\")\n        print(\"  VERCEL_AI_GATEWAY_KEY=... python3 check_vercel.py --update\")\n        sys.exit(1)\n    \n    update = '--update' in sys.argv\n    \n    sys.exit(check_and_display(api_key, update=update))\n"

```

### scripts/topup.py

```python
#!/usr/bin/env python3
"""
Add credits to a provider (record a top-up).

Usage:
    topup.py <provider> <amount>

Example:
    topup.py anthropic 25
"""

import os
import json
import sys
from datetime import datetime

# Supported providers (lite version)
SUPPORTED_PROVIDERS = ['anthropic', 'openai', 'openrouter', 'mistral', 'groq']

def load_config():
    """Load config from the skill directory"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'config.json')
    
    try:
        with open(config_path, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        return {'providers': {}, 'thresholds': {'warning': 50, 'critical': 25}}

def save_config(config):
    """Save config back to file"""
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'config.json')
    
    with open(config_path, 'w') as f:
        json.dump(config, f, indent=2)

def topup(provider: str, amount: float):
    """
    Add credits to a provider.
    
    Args:
        provider: Provider name
        amount: Amount to add
    """
    config = load_config()
    provider_key = provider.lower().replace('-', '_')
    
    # Check if supported
    if provider_key not in SUPPORTED_PROVIDERS:
        print(f"โŒ Provider '{provider}' not supported in lite version.")
        print(f"   Supported: {', '.join(SUPPORTED_PROVIDERS)}")
        sys.exit(1)
    
    if provider_key not in config.get('providers', {}):
        print(f"โŒ Provider '{provider}' not configured. Run sync_provider.py first.")
        sys.exit(1)
    
    old_balance = config['providers'][provider_key].get('current_credits', 0)
    old_max = config['providers'][provider_key].get('max_credits', 0)
    
    new_balance = old_balance + amount
    new_max = old_max + amount  # Also increase max since we added credits
    
    config['providers'][provider_key]['current_credits'] = new_balance
    config['providers'][provider_key]['max_credits'] = new_max
    config['providers'][provider_key]['last_sync'] = datetime.now().astimezone().isoformat()
    
    save_config(config)
    
    print(f"โœ… Added ${amount:.2f} to {provider}")
    print(f"   Old balance: ${old_balance:.2f} / ${old_max:.2f}")
    print(f"   New balance: ${new_balance:.2f} / ${new_max:.2f}")
    
    return {
        'status': 'topped_up',
        'provider': provider_key,
        'amount_added': amount,
        'old_balance': old_balance,
        'new_balance': new_balance,
        'new_max': new_max
    }

if __name__ == "__main__":
    if len(sys.argv) < 3 or sys.argv[1] in ['-h', '--help']:
        print("Usage: topup.py <provider> <amount>")
        print("\nSupported providers: anthropic, openai, openrouter, mistral, groq")
        print("\nExample:")
        print("  topup.py anthropic 25")
        sys.exit(0)
    
    provider = sys.argv[1]
    amount = float(sys.argv[2])
    
    topup(provider, amount)

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### README.md

```markdown
# API Credits Lite

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

An **OpenClaw agent skill** โ€” track and display API credit balances across 5 core providers with retro video game health bars. Just ask your agent to check your credits.

## What It Does

- ๐ŸŽฎ **Visual health bars** โ€” at-a-glance credit status for all your providers
- ๐Ÿ“Š **5 providers** โ€” Anthropic, OpenAI, OpenRouter, Mistral, Groq
- ๐Ÿ”„ **API auto-check** โ€” automated balance pulls for OpenAI, OpenRouter, Vercel
- ๐Ÿ“‹ **Manual sync** โ€” update balances from any provider console
- โš ๏ธ **Low credit alerts** โ€” warning and critical thresholds
- ๐Ÿ’ฐ **Top-up tracking** โ€” record when you add credits

## How to Use

Once installed, just talk to your agent naturally:

> *"How much credit do I have left?"*  
> *"Show my API balances"*  
> *"Update my Anthropic balance to $42.50"*  
> *"I topped up OpenRouter by $20"*  
> *"Am I running low on anything?"*

Your agent handles everything โ€” no commands needed.

## Example Output

```
๐Ÿ’ฐ API Credit Health
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

Anthropic ๐ŸŸง
[โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘] 42% ($22.97/$54.94)
โ†ณ Last sync: 2m ago

OpenAI ๐ŸŸฉ
[โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ] 100% ($100.00/$100.00)

OpenRouter ๐ŸŸจ
[โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘] 60% ($60.00/$100.00)

โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
โš ๏ธ  Anthropic is getting low
```

**Health bar colors:** ๐ŸŸฉ >75% ยท ๐ŸŸจ 50โ€“75% ยท ๐ŸŸง 25โ€“50% ยท ๐ŸŸฅ <25%

## Installation

Install via [ClawHub](https://clawhub.ai) or clone manually:

```bash
git clone https://github.com/FranciscoBuiltDat/openclaw-api-credits-lite.git
```

Optional: install `requests` for API auto-checks:

```bash
pip install requests
```

## Supported Providers

| Provider | Auto-check | Manual sync | Console URL |
|----------|-----------|-------------|-------------|
| Anthropic | โŒ | โœ… | console.anthropic.com |
| OpenAI | โœ… `OPENAI_API_KEY` | โœ… | platform.openai.com/usage |
| OpenRouter | โœ… `OPENROUTER_API_KEY` | โœ… | openrouter.ai/activity |
| Vercel AI | โœ… `VERCEL_AI_GATEWAY_KEY` | โœ… | vercel.com/dashboard |
| Mistral | โŒ | โœ… | console.mistral.ai/billing |
| Groq | โŒ | โœ… | console.groq.com/settings/billing |

## Pro Version

Need 16+ providers, automatic JSONL tracking from session logs, or heartbeat integration? Upgrade to [api-credits-pro](https://github.com/FranciscoBuiltDat/openclaw-api-credits-pro):

- ๐Ÿ”„ Auto-tracking from OpenClaw session logs (includes prompt caching!)
- ๐ŸŒ 16+ providers โ€” AWS Bedrock, Azure, GCP Vertex, xAI, Cerebras, and more
- ๐Ÿ’“ Heartbeat integration for periodic balance checks
- ๐Ÿ“ˆ Daily/weekly spend analytics

## Contributing

Contributions welcome! Please read [SECURITY.md](SECURITY.md) before contributing.

## License

MIT โ€” see [LICENSE](LICENSE)

```

### _meta.json

```json
{
  "owner": "franciscobuiltdat",
  "slug": "api-credits-lite",
  "displayName": "API Credit Health Bar Lite",
  "latest": {
    "version": "1.0.2",
    "publishedAt": 1771874002263,
    "commit": "https://github.com/openclaw/skills/commit/a6baa71bb3d0f68fc22e4cf1c617493eaf74d463"
  },
  "history": []
}

```

### scripts/check_all_apis.py

```python
#!/usr/bin/env python3\n\"\"\"\nCheck all available API providers at once.\n\nRuns check_openai.py, check_openrouter.py, and check_vercel.py\nand displays a summary.\n\nUsage:\n    python3 check_all_apis.py\n    python3 check_all_apis.py --update  (updates config)\n\"\"\"\n\nimport os\nimport subprocess\nimport sys\nimport json\nfrom render_healthbar import render_bar\n\nAPIS = [\n    {\n        'name': 'OpenAI',\n        'script': 'check_openai.py',\n        'env_key': 'OPENAI_API_KEY'\n    },\n    {\n        'name': 'OpenRouter',\n        'script': 'check_openrouter.py',\n        'env_key': 'OPENROUTER_API_KEY'\n    },\n    {\n        'name': 'Vercel',\n        'script': 'check_vercel.py',\n        'env_key': 'VERCEL_AI_GATEWAY_KEY'\n    }\n]\n\ndef load_config():\n    \"\"\"Load current config\"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    config_path = os.path.join(script_dir, '..', 'config.json')\n    \n    try:\n        with open(config_path, 'r') as f:\n            return json.load(f)\n    except FileNotFoundError:\n        return {'providers': {}}\n\ndef check_all_apis(update=False):\n    \"\"\"\n    Run all API checks and display results.\n    \"\"\"\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    \n    print(\"๐Ÿ” Checking all API providers...\\n\")\n    \n    results = []\n    failed = 0\n    \n    for api in APIS:\n        if not os.getenv(api['env_key']):\n            print(f\"โญ๏ธ  {api['name']}: Skipped (${api['env_key']} not set)\")\n            continue\n        \n        cmd = ['python3', os.path.join(script_dir, api['script'])]\n        if update:\n            cmd.append('--update')\n        \n        try:\n            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)\n            \n            if result.returncode == 0:\n                print(f\"โœ… {api['name']}: {result.stdout.strip()}\")\n                results.append(api['name'])\n            else:\n                print(f\"โŒ {api['name']}: Failed\")\n                if result.stderr:\n                    print(f\"   {result.stderr}\")\n                failed += 1\n        \n        except subprocess.TimeoutExpired:\n            print(f\"โฑ๏ธ  {api['name']}: Timeout\")\n            failed += 1\n        except Exception as e:\n            print(f\"โŒ {api['name']}: {str(e)}\")\n            failed += 1\n    \n    print()\n    print(f\"Summary: {len(results)}/{len(APIS)} providers checked successfully\")\n    \n    if update:\n        print(\"โœ… Config updated with latest balances\")\n        print(\"\\nRun: python3 show_credits.py\")\n    \n    return 0 if failed == 0 else 1\n\nif __name__ == \"__main__\":\n    update = '--update' in sys.argv\n    sys.exit(check_all_apis(update=update))\n"

```

### scripts/render_healthbar.py

```python
#!/usr/bin/env python3
"""
Render video-game style health bars for API credits.
"""

def render_bar(current, max_amount, width=10):
    """
    Render a health bar with block characters.
    
    Args:
        current: Current credit amount
        max_amount: Maximum credit amount
        width: Width of bar in characters (default 10)
    
    Returns:
        tuple: (bar_string, emoji, percentage)
    """
    if max_amount <= 0:
        return "โ–‘" * width, "โšช", 0
    
    percentage = (current / max_amount) * 100
    filled = int((current / max_amount) * width)
    empty = width - filled
    
    # Color-coded emoji based on percentage
    if percentage > 75:
        emoji = "๐ŸŸฉ"
    elif percentage > 50:
        emoji = "๐ŸŸจ"
    elif percentage > 25:
        emoji = "๐ŸŸง"
    else:
        emoji = "๐ŸŸฅ"
    
    bar = "โ–ˆ" * filled + "โ–‘" * empty
    
    return bar, emoji, percentage


def format_credits(provider, current, max_amount, daily=None, weekly=None, last_sync=None):
    """
    Format complete credit display for a provider.
    
    Args:
        provider: Provider name (e.g., "OpenAI", "Anthropic")
        current: Current credit balance
        max_amount: Maximum credit amount
        daily: Optional daily spend
        weekly: Optional weekly spend
        last_sync: Optional last sync time string
    
    Returns:
        str: Formatted credit display
    """
    bar, emoji, pct = render_bar(current, max_amount)
    
    lines = []
    lines.append(f"{provider} {emoji}")
    lines.append(f"[{bar}] {pct:.0f}% (${current:.2f}/${max_amount:.2f})")
    
    # Optional stats line
    stats = []
    if daily is not None:
        stats.append(f"${daily:.2f} today")
    if weekly is not None:
        stats.append(f"${weekly:.2f} this week")
    if last_sync:
        stats.append(f"Last sync: {last_sync}")
    
    if stats:
        lines.append("โ†ณ " + " โ€ข ".join(stats))
    
    return "\n".join(lines)


if __name__ == "__main__":
    # Test rendering
    print("Testing health bar rendering:\n")
    
    print(format_credits("OpenAI", 78.50, 100, daily=1.23, weekly=12.45))
    print()
    print(format_credits("Anthropic", 27.50, 50, last_sync="6h ago"))
    print()
    print(format_credits("Groq", 11, 50, daily=2.15))

```