API Key Pools
API key pooling lets you configure multiple API keys for the same provider. Requests are distributed using weighted round-robin, and the system automatically switches to another available key when rate limits or failures are encountered.
When to Use
- High-concurrency scenarios: A single key's rate limit is insufficient; multiple keys share the traffic load
- Team sharing: Each team member uses their own key quota to split costs
- Stacking free tiers: Keys from multiple free accounts are used in rotation
- High availability: When one key fails, the system automatically switches to another key to prevent interruptions
- Test/production separation: Use different weights to control the proportion of requests going to production keys vs. test keys
How-To Guide
Adding an API Key
- Open Settings → Provider Management
- Select the target provider (e.g. OpenAI)
- Locate the API Key Pool section
- Click the Add Key button
- Fill in the information:
| Field | Required | Description |
|---|---|---|
| API Key | Yes | Key value or environment variable reference (prefixed with $) |
| Label | No | A name for easy identification, e.g. "Production Key #1" or "Test Key" |
| Weight | No | 1–100, defaults to 1; higher values increase the probability of selection |
- Click Confirm to add
Setting the Weight
- Find the target key in the API key list
- Modify the Weight value (1–100)
- The weight determines the relative probability of that key being selected
Enabling / Disabling an Individual Key
- Find the target key in the API key list
- Toggle the Enabled switch
- Disabled keys are excluded from the rotation but their configuration is preserved
Deleting a Key
- Find the target key in the API key list
- Click the Delete button
- Confirm the deletion (this action cannot be undone)
Configuration Reference
| Setting | Type | Default | Range | Description |
|---|---|---|---|---|
| API Key | String | (empty) | -- | Key value or $ENV_VAR reference |
| Label | String | (empty) | -- | Note name for distinguishing keys |
| Weight | Integer | 1 | 1–100 | Weight for weighted round-robin |
| Enabled | Boolean | true | -- | Whether this key participates in rotation |
| Order | Integer | 0 | -- | Display order in the list |
Behavior Notes
Weighted Round-Robin
Each key in the pool occupies a number of "slots" proportional to its weight. Elftia cycles through these slots in a fixed sequence, distributing requests according to the weight ratio.
Weight distribution example:
Assume 3 keys with weights 3, 2, and 1 (total weight = 6):
| Key | Weight | Share | Selections per 6 requests |
|---|---|---|---|
| Key A | 3 | 50% | 3 |
| Key B | 2 | 33% | 2 |
| Key C | 1 | 17% | 1 |
Rotation order: A → A → A → B → B → C → A → A → A → B → ...
Equal-weight example:
3 keys each with weight 1 (total weight = 3), so they rotate strictly: A → B → C → A → B → C → ...
Session Affinity
All requests within the same chat session are bound to the same API key. This design has two important reasons:
- Prompt caching: Some providers (e.g. Anthropic) support prompt caching — consecutive requests using the same key can hit the cache, significantly reducing latency and cost
- Consistency: Prevents rate-limit counter resets caused by frequent key switches within the same conversation
Session binding is reassigned in the following situations:
- The currently bound key is disabled or deleted
- The currently bound key enters a cooldown period due to errors
- The session ends (closed or deleted)
Automatic Failover
When a request encounters an error, the key pool applies different strategies based on the error type:
Rate Limiting (429/529)
When an HTTP 429 (rate limit) or 529 (service overloaded) response is received:
- The current key enters a cooldown period
- The system automatically switches to the next available key in the pool
- The request is retried with the new key
Cooldown mechanism:
| Consecutive failures | Cooldown duration | Formula |
|---|---|---|
| 1st | 60 seconds | 60s × 2^0 |
| 2nd | 120 seconds | 60s × 2^1 |
| 3rd | 240 seconds | 60s × 2^2 |
| 4th | 480 seconds | 60s × 2^3 |
| 5th and beyond | 900 seconds (cap) | min(60s × 2^(n-1), 900s) |
The key is excluded from rotation during the cooldown period and automatically becomes available again once it expires. A successful request resets the consecutive-failure counter for that key.
Authentication Failure (401/403)
When an HTTP 401 (Unauthorized) or 403 (Forbidden) response is received:
- The key is permanently disabled (marked as disabled in the database)
- The system automatically switches to the next available key
- In the settings UI, the key is shown as disabled
This is because authentication errors usually indicate the key itself has become invalid (expired, revoked, or quota exhausted) and is unlikely to recover by waiting.
Failover Flow Chart
Request sent
|
v
Use session-bound key
|
+---> Success ---> Reset cooldown counter ---> Return response
|
+---> 429/529 (rate limited)
| |
| v
| Current key enters cooldown
| (starts at 60s, exponential backoff, cap at 15 minutes)
| |
| v
| Any other available keys in the pool?
| | |
| Yes No
| | |
| v v
| Switch to new key Request fails, return error
| Rebind session (all keys unavailable)
| |
| v
| Retry with new key
|
+---> 401/403 (authentication failure)
|
v
Permanently disable this key
|
v
Any other available keys in the pool?
| |
Yes No
| |
v v
Switch to new key Request fails, return error
Rebind session
Relationship Between Key Pool and Provider-Level API Key
- When a provider has both a provider-level API key (the
api_keyfield in the Provider configuration) and a key pool configured, the key pool takes priority - If the key pool is empty (no entries), the provider-level API key is used
- Using the key pool instead of a provider-level API key is recommended for load balancing and failover capabilities
Troubleshooting
| Issue | Possible cause | Solution |
|---|---|---|
| All keys are in cooldown; requests fail | All keys hit rate limits simultaneously | Wait for the cooldown to expire (up to 15 minutes), or add more keys to expand pool capacity |
| A key is never selected | Weight is 0, or other keys have much higher weights | Check weight settings; ensure the weight is at least 1 |
| A key is automatically disabled | A 401/403 authentication error was received | Check whether the key is expired or revoked; verify the key's status on the provider's website, then manually re-enable it after fixing the issue |
| Pool is configured correctly but requests still use the old key | Session affinity is bound to the old key | Start a new chat session, or wait for the old key to enter a cooldown period so the system automatically rebinds |
| Added key not taking effect immediately | Key cache not refreshed | Save the configuration and retry; the system refreshes the cache automatically |
| Environment variable key cannot be resolved | Variable name is wrong or not set | Confirm the variable name after $ exactly matches the one set in the system, including case |
| Cooldown is too long | Exponential backoff from multiple consecutive rate-limit hits | Reduce request frequency, or add more keys to spread the load. The cooldown cap is 15 minutes |
| Key switched within the same session | Original bound key entered cooldown or was disabled | This is expected behavior; the system automatically selects the best available key |
Related Pages
- LLM Providers Overview - Understand the overall architecture of the provider system
- Adding a Provider - Configure a provider's basic information and API key
- Custom Endpoints - Local deployment services typically do not require a key pool
- Model Parameters - Configure model generation parameters