Automatically rotate between multiple Kimi API keys to handle rate limits and distribute load across accounts.
- Multiple API Key Support — Store and manage unlimited Kimi API keys
- Auto-Rotation — Automatically switch keys when rate limited
- Billing Limit Cooldown — 24-hour automatic cooldown when billing cycle limits are reached
- Health-Based Selection — Smart rotation using health scores and LRU
- Three Rotation Strategies:
round-robin— Cycle through keys sequentiallyhealth-based— Use health scores + freshness (default)sticky— Stay on one key until rate limited
- CLI Management — Easy commands to add, list, and manage keys
- Plugin compatible — Works alongside other OpenCode plugins (oh-my-opencode, antigravity-auth, etc.)
For Humans
Option A: NPM (One-line installation)
npm install -g opencode-kimi-rotatorThat's it! The plugin is automatically built and installed. Now add your API keys:
opencode-kimi add-key sk-kimi-your-key-here "My Account"Option B: Automatic Install Script
curl -fsSL https://raw.githubusercontent.com/deyndev/opencode-kimi-rotator/main/scripts/install.sh | bashOr for Windows (PowerShell):
irm https://raw.githubusercontent.com/deyndev/opencode-kimi-rotator/main/scripts/install.ps1 | iexOption C: Manual setup
-
Clone and build the plugin:
git clone https://github.com/deyndev/opencode-kimi-rotator.git cd opencode-kimi-rotator npm install npm run build npm run install:plugin -
Add the model to your
~/.config/opencode/opencode.json:{ "provider": { "anthropic": { "name": "Anthropic", "models": { "kimi-for-coding": { "name": "Kimi K2.5 (via Kimi API)", "attachment": true, "limit": { "context": 262144, "output": 32768 }, "modalities": { "input": ["text", "image", "video", "pdf"], "output": ["text"] } } } } } } -
Add your API keys:
opencode-kimi add-key sk-kimi-your-key-here "My Account 1" opencode-kimi add-key sk-kimi-another-key "My Account 2"
-
Use it:
opencode run "Hello" --model=anthropic/kimi-for-coding
For LLM Agents
1. Clone and build the plugin:
git clone https://github.com/deyndev/opencode-kimi-rotator.git
cd opencode-kimi-rotator
npm install
npm run install:plugin2. Add the plugin and model to ~/.config/opencode/opencode.json:
Add the plugin to your existing config. The plugin path must use the full absolute path:
{
"plugin": ["file:///Users/YOUR_USERNAME/.config/opencode/plugins/kimi-rotator.js"],
"provider": {
"anthropic": {
"name": "Anthropic",
"models": {
"kimi-for-coding": {
"name": "Kimi K2.5 (via Kimi API)",
"attachment": true,
"limit": {
"context": 262144,
"output": 32768
},
"modalities": {
"input": ["text", "image", "video", "pdf"],
"output": ["text"]
}
}
}
}
}
}Important: Replace
YOUR_USERNAMEwith your actual username. Example paths:
- macOS:
file:///Users/john/.config/opencode/plugins/kimi-rotator.js- Linux:
file:///home/john/.config/opencode/plugins/kimi-rotator.js
3. Configure oh-my-opencode agents in ~/.config/opencode/oh-my-opencode.json:
You must configure ALL agents you want to use with Kimi:
{
"google_auth": false,
"ralph_loop": {
"enabled": true,
"default_max_iterations": 100
},
"agents": {
"sisyphus": { "model": "anthropic/kimi-for-coding" },
"prometheus": { "model": "anthropic/kimi-for-coding" },
"oracle": { "model": "anthropic/kimi-for-coding" },
"librarian": { "model": "anthropic/kimi-for-coding" },
"explore": { "model": "anthropic/kimi-for-coding" },
"frontend-ui-ux-engineer": { "model": "anthropic/kimi-for-coding" },
"document-writer": { "model": "anthropic/kimi-for-coding" },
"multimodal-looker": { "model": "anthropic/kimi-for-coding" },
"atlas": { "model": "anthropic/kimi-for-coding" }
}
}Note: If you get "model is not valid" errors, make sure the agent is configured in oh-my-opencode.json.
4. Add your Kimi API keys:
opencode-kimi add-key sk-kimi-your-key-here "My Account"5. Verify installation:
opencode-kimi list-keys- Sets
ANTHROPIC_BASE_URLto Kimi's API endpoint - Intercepts fetch requests to
api.kimi.com - Rotates API keys based on health scores
- Shows toast notifications for key rotation
The Kimi Coding API provides a single flagship model:
| Model ID | Name | Context | Output |
|---|---|---|---|
kimi-for-coding |
Kimi K2.5 | 262,144 | 32,768 |
Use it as: anthropic/kimi-for-coding
Full configuration (copy-paste ready)
Add this to your ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["file:///Users/YOUR_USERNAME/.config/opencode/plugins/kimi-rotator.js"],
"provider": {
"anthropic": {
"name": "Anthropic",
"models": {
"kimi-for-coding": {
"name": "Kimi K2.5 (via Kimi API)",
"attachment": true,
"limit": {
"context": 262144,
"output": 32768
},
"modalities": {
"input": ["text", "image", "video", "pdf"],
"output": ["text"]
}
}
}
}
}
}# Add a key with optional name
opencode-kimi add-key sk-kimi-your-key-here "My Account 1"
opencode-kimi add-key sk-kimi-another-key "My Account 2"opencode-kimi list-keysOutput:
Kimi API Keys (2 total, strategy: health-based):
● [0] My Account 1
Key: sk-kim...yDigS
Health: ██████████ 100%
Requests: 45/50 successful
Status: ✓ Active
○ [1] My Account 2
Key: sk-kim...xxxxx
Health: ████████░░ 80%
Requests: 20/25 successful
Status: ✓ Active
opencode-kimi remove-key 1opencode-kimi rotate# Round-robin: cycle through keys sequentially
opencode-kimi set-strategy round-robin
# Health-based: smart selection based on health scores (default)
opencode-kimi set-strategy health-based
# Sticky: stay on one key until rate limited
opencode-kimi set-strategy stickyopencode-kimi statsOutput:
📊 Usage Statistics
[0] My Account 1
Total Requests: 1425
Successful: 1227
Failed: 198
Success Rate: 86.1%
Avg Response Time: 245ms
Requests Today: 12
Requests Last 7 Days: 89
[1] My Account 2
Total Requests: 811
Successful: 793
Failed: 18
Success Rate: 97.8%
Avg Response Time: 198ms
Requests Today: 5
Requests Last 7 Days: 42
──────────────────────────────────────────────────
📈 Aggregate Statistics
Total Requests (All Keys): 2236
Total Successful: 2020
Overall Success Rate: 90.3%
Overall Avg Response Time: 221ms
Automatically restore health scores for rate-limited keys after a cooldown period.
# Check current auto-refresh settings
opencode-kimi list-keys
# Enable/disable auto-refresh (default: enabled)
opencode-kimi set-auto-refresh true
opencode-kimi set-auto-refresh false
# Set cooldown period in minutes (default: 30, range: 1-1440)
opencode-kimi set-cooldown 30
opencode-kimi set-cooldown 60 # 1 hour
opencode-kimi set-cooldown 180 # 3 hours
# Manually trigger health refresh
opencode-kimi refresh-healthEach API key has a health score (0-100):
- Initial: 100
- Success: +2 points
- Rate Limited (HTTP 429): -15 points
- Billing Limit Reached: -30 points + 24-hour cooldown
- Failure: -20 points
Keys with health score < 30 are temporarily skipped.
When a key is rate limited (HTTP 429):
- Health score decreases by 15 points
- Key is marked as rate limited with reset time from
retry-afterheader - Next request automatically uses next available key
- Once reset time passes, key becomes available again
When a key hits the billing cycle usage limit ("You've reached your usage limit for this billing cycle"):
- Health score decreases by 30 points
- Key is put on a 24-hour cooldown (until midnight of the next day)
- Next request automatically uses next available key
- A toast notification shows how many hours until the key is available
- The key won't be retried until the cooldown expires
Keys are stored in ~/.config/opencode/kimi-accounts.json:
{
"version": 1,
"accounts": [
{
"key": "sk-kimi-xxx",
"name": "My Account",
"addedAt": 1234567890,
"lastUsed": 1234567890,
"rateLimitResetTime": 0,
"billingLimitResetTime": 0,
"healthScore": 100,
"consecutiveFailures": 0,
"totalRequests": 50,
"successfulRequests": 45,
"responseTimes": [245, 198, 210],
"dailyRequests": {
"2026-01-31": 12,
"2026-02-01": 5
}
}
],
"activeIndex": 0,
"rotationStrategy": "health-based",
"autoRefreshHealth": true,
"healthRefreshCooldownMinutes": 30
}Each account tracks additional metrics for health-based rotation:
- responseTimes — Array of the last 100 response times (in milliseconds)
- dailyRequests — Record of request counts by date (YYYY-MM-DD format)
- autoRefreshHealth — Whether to automatically refresh health scores after cooldown (default:
true) - healthRefreshCooldownMinutes — Minutes to wait before health can be refreshed (default:
30, range: 1-1440)
When enabled, keys that were rate-limited will have their health score gradually restored (+10 points) after the cooldown period passes, making them available for rotation again.
Backup and migrate your API keys between machines using encrypted files.
# Export all keys to an encrypted file
opencode-kimi export-keys
opencode-kimi export-keys my-backup.json.enc
# Import keys from an encrypted file
opencode-kimi import-keys my-backup.json.encFeatures:
- AES-256-GCM encryption with password protection
- Exports all keys, settings, and statistics
- Duplicate keys are skipped during import
- Rotation strategy and auto-refresh settings are preserved
OpenCode uses ~/.config/opencode/ on all platforms including Windows.
| File | Path |
|---|---|
| Main config | ~/.config/opencode/opencode.json |
| Accounts | ~/.config/opencode/kimi-accounts.json |
Windows users:
~resolves to your user home directory (e.g.,C:\Users\YourName). Do NOT use%APPDATA%.
To use Kimi models with Oh My OpenCode agents, update ~/.config/opencode/oh-my-opencode.json:
{
"google_auth": false,
"ralph_loop": {
"enabled": true,
"default_max_iterations": 100
},
"agents": {
"sisyphus": { "model": "anthropic/kimi-for-coding" },
"prometheus": { "model": "anthropic/kimi-for-coding" },
"oracle": { "model": "anthropic/kimi-for-coding" },
"librarian": { "model": "anthropic/kimi-for-coding" },
"explore": { "model": "anthropic/kimi-for-coding" },
"frontend-ui-ux-engineer": { "model": "anthropic/kimi-for-coding" },
"document-writer": { "model": "anthropic/kimi-for-coding" },
"multimodal-looker": { "model": "anthropic/kimi-for-coding" },
"atlas": { "model": "anthropic/kimi-for-coding" }
}
}Run:
opencode-kimi add-key your-api-keyThe plugin will wait for the soonest available key. You can:
- Wait for rate limits to reset
- Add more API keys
- Check status with
opencode-kimi list-keys
- Ensure plugin is installed: Check
~/.config/opencode/opencode.jsonhas"plugin": ["opencode-kimi-rotator@latest"] - Verify the plugin package is installed:
npm list -g opencode-kimi-rotator - Check OpenCode logs for errors
If you need to start fresh:
rm ~/.config/opencode/kimi-accounts.json
opencode-kimi add-key your-new-api-keyBoth plugins can work together. List them in your preferred order:
{
"plugin": ["opencode-kimi-rotator@latest", "opencode-antigravity-auth@latest"]
}Configure agent models in oh-my-opencode.json:
{
"google_auth": false,
"agents": {
"sisyphus": { "model": "anthropic/kimi-for-coding" },
"oracle": { "model": "anthropic/kimi-for-coding" },
"librarian": { "model": "anthropic/kimi-for-coding" },
"atlas": { "model": "anthropic/kimi-for-coding" }
}
}For automated setup, you can use this one-liner:
curl -fsSL https://raw.githubusercontent.com/deyndev/opencode-kimi-rotator/main/scripts/install.sh | bashOr for Windows (PowerShell):
irm https://raw.githubusercontent.com/deyndev/opencode-kimi-rotator/main/scripts/install.ps1 | iex- Clone the repository:
git clone https://github.com/deyndev/opencode-kimi-rotator.git
cd opencode-kimi-rotator- Install dependencies and build:
npm install
npm run build- Link for local testing:
npm link- Add to your OpenCode config:
{
"plugin": ["opencode-kimi-rotator"]
}Main class for managing Kimi API key rotation.
Marks an account as successfully used and updates health metrics.
async markAccountSuccess(
index: number,
responseTime?: number,
date?: string
): Promise<void>- index — Account index in the accounts array
- responseTime — Optional response time in milliseconds (tracked in rolling window of 100)
- date — Optional date string (YYYY-MM-DD) for daily request tracking
Enables or disables automatic health score refresh.
async setAutoRefreshHealth(enabled: boolean): Promise<void>Sets the cooldown period for health refresh.
async setHealthRefreshCooldown(minutes: number): Promise<void>- minutes — Cooldown duration (1-1440, default: 30)
- Throws — Error if minutes is outside valid range
Manually triggers health score refresh for eligible accounts.
async refreshHealthScores(): Promise<{
refreshed: number;
details: string[];
}>Returns the number of accounts refreshed and detailed change log.
Handles persistent storage of account configuration with file locking.
constructor();Creates a new storage instance. The config directory is automatically set to ~/.config/opencode/.
Initializes the storage directory and files.
async init(): Promise<void>Creates the config directory if it doesn't exist and initializes an empty accounts file.
Loads and validates the accounts configuration.
async loadConfig(): Promise<KimiAccountsConfig>- Returns — Parsed and validated configuration
- Throws — Error if file cannot be read or parsed
Saves the configuration to disk with file locking.
async saveConfig(config: KimiAccountsConfig): Promise<void>- config — Configuration object to save
- Throws — Error if validation fails or file cannot be written
Adds a new API key account.
async addAccount(key: string, name?: string): Promise<KimiAccount>- key — The API key to store
- name — Optional display name for the account
- Returns — The created account object
- Throws — Error if the key already exists
Removes an account by index.
async removeAccount(index: number): Promise<void>- index — Account index to remove
- Throws — Error if index is invalid
Returns all stored accounts.
async listAccounts(): Promise<KimiAccount[]>Returns the currently active account.
async getActiveAccount(): Promise<KimiAccount | null>- Returns — The active account or
nullif no accounts exist
Sets the active account index.
async setActiveIndex(index: number): Promise<void>- index — Account index to set as active
- Throws — Error if index is invalid
Returns the current active account index.
async getActiveIndex(): Promise<number>- Returns — The current active index from the configuration
Atomically gets the current active index and sets the next one for round-robin rotation. This ensures proper serialization of rotation operations.
async getAndIncrementActiveIndex(availableIndices: number[]): Promise<number>- availableIndices — Array of available account indices to rotate through
- Returns — The selected next index (guaranteed to be unique per call)
- Throws — Error if no available indices are provided
Atomically sets the active index to a specific value. Use this for sticky and health-based rotation to claim an account.
async atomicSetActiveIndex(preferredIndex: number): Promise<number>- preferredIndex — The desired index to set
- Returns — The actual index that was set (may differ if validation fails)
Updates specific fields of an account.
async updateAccount(index: number, updates: Partial<KimiAccount>): Promise<void>- index — Account index to update
- updates — Partial account object with fields to update
- Throws — Error if index is invalid
Zod schema for validating Kimi account objects:
{
key: string, // API key (required)
name: string, // Optional account name
addedAt: number, // Timestamp when added
lastUsed: number, // Timestamp of last use
rateLimitResetTime: number, // Default: 0
healthScore: number, // 0-100, default: 100
consecutiveFailures: number, // Default: 0
totalRequests: number, // Default: 0
successfulRequests: number, // Default: 0
responseTimes: number[], // Array of response times (ms)
dailyRequests: Record<string, number> // Requests per day
}Zod schema for the accounts configuration:
{
version: number, // Default: 1
accounts: KimiAccount[], // Array of accounts
activeIndex: number, // Default: 0
rotationStrategy: 'round-robin' | 'health-based' | 'sticky',
autoRefreshHealth: boolean, // Default: true
healthRefreshCooldownMinutes: number // Default: 30, range: 1-1440
}KimiAccount— Inferred type fromKimiAccountSchemaKimiAccountsConfig— Inferred type fromKimiAccountsConfigSchema
Default values for new accounts (excludes key, addedAt, lastUsed):
{
rateLimitResetTime: 0,
healthScore: 100,
consecutiveFailures: 0,
totalRequests: 0,
successfulRequests: 0,
responseTimes: [],
dailyRequests: {}
}Default configuration values (excludes accounts):
{
version: 1,
activeIndex: 0,
rotationStrategy: 'health-based',
autoRefreshHealth: true,
healthRefreshCooldownMinutes: 30
}MIT License. See LICENSE for details.
Legal
- Personal / internal development only
- Respect internal quotas and data handling policies
- Not for production services or bypassing intended limits
By using this plugin, you acknowledge:
- Terms of Service risk — This approach may violate ToS of AI model providers
- Account risk — Providers may suspend or ban accounts
- No guarantees — APIs may change without notice
- Assumption of risk — You assume all legal, financial, and technical risks
- Not affiliated with Moonshot AI. This is an independent open-source project.
- "Kimi", "Moonshot", and "Moonshot AI" are trademarks of Moonshot AI.
Thanks to all the people who already contributed!