swarmkey
v1.0.3
Published
API Key Swarm Manager - Automatically rotate multiple API keys to handle rate limits
Maintainers
Readme
🔑 SwarmKey
Simple API Key Manager - Automatically rotate through multiple API keys with cooldown management.
🎯 What is SwarmKey?
SwarmKey is a lightweight, framework-agnostic API key manager that helps you:
- ✅ Manage multiple API keys in a single pool
- ✅ Automatic cooldown when keys hit rate limits (429 errors)
- ✅ Simple rotation to the next available key
- ✅ Zero dependencies on specific AI providers
- ✅ Works with any API (Gemini, OpenAI, Claude, Anthropic, etc.)
Perfect for: Applications that need to distribute API usage across multiple keys with automatic failover.
🚀 Quick Start
Installation
npm install swarmkeyBasic Usage
import SwarmKey from 'swarmkey'
// Initialize with your API keys
const sk = SwarmKey.init({
keys: ['key1', 'key2', 'key3']
})
// Get an available key
const key = sk.getAvailableKey()
if (key) {
try {
// Use the key with YOUR API client
const response = await yourApiClient.call(key, 'Your prompt here')
} catch (error) {
// If you get 429 rate limit error, mark the key as failed
if (error.status === 429) {
sk.markKeyFailed(key) // Key will be in cooldown for 50 minutes
}
}
} else {
console.log('No keys available right now')
}📖 Usage Examples
With Environment Variables
# .env
SWARM_KEYS="key1,key2,key3,key4,key5"import SwarmKey from 'swarmkey'
import 'dotenv/config'
// Initialize from environment variables
const sk = SwarmKey.init()
const key = sk.getAvailableKey()
// Use the key...Error Handling with Automatic Cooldown
import SwarmKey from 'swarmkey'
const sk = SwarmKey.init({
keys: ['key1', 'key2', 'key3']
})
async function makeApiCall(prompt: string) {
const key = sk.getAvailableKey()
if (!key) {
throw new Error('No keys available')
}
try {
// Make your API call
const response = await yourApiClient.call(key, prompt)
return response
} catch (error) {
// Handle rate limit error
if (error.status === 429) {
// Mark key as failed - it will be unavailable for 50 minutes
sk.markKeyFailed(key)
// Try with next available key
return makeApiCall(prompt)
}
throw error
}
}Get All Available Keys
const sk = SwarmKey.init({
keys: ['key1', 'key2', 'key3']
})
// Get all keys that are currently available (not in cooldown)
const availableKeys = sk.getAvailableKeys()
console.log(`${availableKeys.length} keys available`)
// Use them for parallel requests
const promises = availableKeys.map(async (key) => {
try {
return await yourApiClient.call(key)
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
}
throw error
}
})
const results = await Promise.allSettled(promises)Monitor Keys Status
const sk = SwarmKey.init({
keys: ['key1', 'key2', 'key3']
})
// Get detailed status for all keys
const status = sk.getKeysStatus()
status.forEach(keyInfo => {
console.log(`Key: ${keyInfo.keyPreview}`)
console.log(` Available: ${keyInfo.available}`)
console.log(` In cooldown: ${keyInfo.inCooldown}`)
if (keyInfo.cooldownRemainingMs) {
const minutes = Math.ceil(keyInfo.cooldownRemainingMs / 60000)
console.log(` Cooldown remaining: ${minutes} minutes`)
}
})
// Get counts
console.log(`Total keys: ${sk.getTotalKeys()}`)
console.log(`Available: ${sk.getAvailableKeysCount()}`)⚙️ Configuration
Environment Variables
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| SWARM_KEYS | Comma-separated API keys | Yes* | - |
*Can be provided via config object instead
Initialization Options
interface SwarmKeyConfig {
keys?: string[] // API keys to manage
}📊 How It Works
Key Selection Logic
Request for key
│
▼
┌─────────────────┐
│ Get all keys │
└────────┬────────┘
│
▼
┌─────────────────────────┐
│ For each key: │
│ 1. Check if in cooldown │
│ 2. Clear expired │
│ cooldowns │
└────────┬────────────────┘
│
▼
┌─────────────────┐ ┌──────────────┐
│ Key available? │────▶│ Return key │
└─────────┬───────┘ └──────────────┘
│
▼ No
┌─────────────────┐
│ Try next key │
└─────────────────┘Cooldown Management
- Simple State: Each key is either available or in cooldown
- Automatic Recovery: Keys automatically become available after 50 minutes
- 429 Handling: When a key hits rate limit (429), mark it as failed with
markKeyFailed(key) - No Tracking: No request counters or rate limit tracking needed
🧮 API Reference
SwarmKey.init(config?)
Initialize a new SwarmKey instance.
static init(config?: SwarmKeyConfig): SwarmKeyParameters:
config.keys?: string[]- Array of API keys (or useSWARM_KEYSenv var)
Returns: SwarmKey instance
Throws: Error if no keys provided
getAvailableKey()
Get an available API key (not in cooldown).
getAvailableKey(): string | nullReturns: An available key, or null if all keys are in cooldown
getAvailableKeys()
Get all available API keys (not in cooldown).
getAvailableKeys(): string[]Returns: Array of available keys (may be empty)
markKeyFailed(key)
Mark a key as failed, putting it in cooldown for 50 minutes.
markKeyFailed(key: string): voidParameters:
key- The API key that failed (received 429 error)
Use this when a key returns a 429 rate limit error. The key will be unavailable for 50 minutes.
getKeysStatus()
Get detailed status for all keys.
getKeysStatus(): Array<{
key: string
keyPreview: string
available: boolean
inCooldown: boolean
cooldownUntil: number | null
cooldownRemainingMs: number | null
}>getTotalKeys()
Get total number of keys being managed.
getTotalKeys(): numbergetAvailableKeysCount()
Get count of keys currently available (not in cooldown).
getAvailableKeysCount(): number🛠️ Integration Examples
With Google Gemini API
import { GoogleGenerativeAI } from '@google/generative-ai'
import SwarmKey from 'swarmkey'
const sk = SwarmKey.init({
keys: ['gemini-key-1', 'gemini-key-2', 'gemini-key-3']
})
async function generate(prompt: string) {
const key = sk.getAvailableKey()
if (!key) {
throw new Error('No keys available')
}
try {
const genAI = new GoogleGenerativeAI(key)
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' })
const result = await model.generateContent(prompt)
return result.response.text()
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
// Retry with next key
return generate(prompt)
}
throw error
}
}With OpenAI API
import OpenAI from 'openai'
import SwarmKey from 'swarmkey'
const sk = SwarmKey.init({
keys: ['openai-key-1', 'openai-key-2']
})
async function chat(message: string) {
const key = sk.getAvailableKey()
if (!key) {
throw new Error('No keys available')
}
try {
const openai = new OpenAI({ apiKey: key })
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: message }]
})
return completion.choices[0].message.content
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
// Retry with next key
return chat(message)
}
throw error
}
}With Anthropic Claude API
import Anthropic from '@anthropic-ai/sdk'
import SwarmKey from 'swarmkey'
const sk = SwarmKey.init({
keys: ['anthropic-key-1', 'anthropic-key-2']
})
async function askClaude(prompt: string) {
const key = sk.getAvailableKey()
if (!key) {
throw new Error('No keys available')
}
try {
const anthropic = new Anthropic({ apiKey: key })
const message = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }]
})
return message.content[0].text
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
// Retry with next key
return askClaude(prompt)
}
throw error
}
}🧪 Testing
Run Tests
npm testBuild
npm run build🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
# Clone repository
git clone https://github.com/yourusername/swarmkey.git
cd swarmkey
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test📄 License
MIT License - see LICENSE file for details.
💡 Tips & Best Practices
1. Retry Logic with Exponential Backoff
async function callWithRetry(prompt: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const key = sk.getAvailableKey()
if (!key) {
await sleep(5000) // Wait 5 seconds if no keys available
continue
}
try {
const result = await apiCall(key, prompt)
return result
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
continue
}
throw error
}
}
throw new Error('All retries exhausted')
}2. Monitor Key Health
setInterval(() => {
const total = sk.getTotalKeys()
const available = sk.getAvailableKeysCount()
console.log(`Keys: ${available}/${total} available`)
if (available < total * 0.3) {
console.warn('WARNING: Less than 30% keys available!')
}
}, 60000) // Check every minute3. Graceful Degradation
async function makeRequest(prompt: string) {
const key = sk.getAvailableKey()
if (!key) {
// Fallback strategy
console.warn('No keys available, using fallback')
return await fallbackStrategy(prompt)
}
try {
return await apiCall(key, prompt)
} catch (error) {
if (error.status === 429) {
sk.markKeyFailed(key)
return makeRequest(prompt) // Retry
}
throw error
}
}❓ FAQ
Q: Does this work with any API provider?
A: Yes! SwarmKey is completely framework-agnostic. It just manages keys and cooldowns.
Q: Can I use this in browser/frontend?
A: Not recommended. API keys should be kept server-side for security.
Q: What happens when all keys are in cooldown?
A: getAvailableKey() returns null. Implement retry logic or wait for cooldowns to expire.
Q: Can I customize the cooldown duration?
A: Currently fixed at 50 minutes. This matches typical API provider rate limit windows.
Q: Does it track request counts or rate limits?
A: No. SwarmKey only tracks failed/available state. This keeps it simple and framework-agnostic.
Made with ❤️ for developers dealing with API rate limits
