Skip to main content

open-keypool

Minimal Python library for pooling and rotating API keys to avoid HTTP 429 rate-limit errors. Provide a list of keys (or pull them from Doppler), choose a rotation strategy (round-robin or least-recently-used), and the pool handles cooldown on rate-limit responses and permanent disablement on invalid keys — all thread-safe.

Install

# From TestPyPI (until published on PyPI):
pip install --index-url https://test.pypi.org/simple/ open-keypool

Quickstart

Local keys array

from open_keypool import KeyPool, AllKeysExhaustedError, KeyState

pool = KeyPool(keys=["sk-key1", "sk-key2", "sk-key3"], strategy="round_robin")

for attempt in range(pool.max_retries):
    key = pool.get_key()
    response = call_your_api(key)

    # Feed the response — the pool decides success / cooldown / disable
    new_state = pool.handle_response(
        key, response.status_code,
        headers=dict(response.headers),
        body=response.json(),
    )

    if new_state == KeyState.ACTIVE:
        break  # success
    elif new_state == KeyState.COOLDOWN:
        continue  # key is rate-limited, rotate to next
    elif new_state == KeyState.DISABLED:
        continue  # key is invalid, rotate to next

Handle response auto-dispatching

pool.handle_response(key, status_code, headers, body) introspects the HTTP response and automatically:

Status Action
2xx Marks success — clears errors, resets failure count
429, 413, or "rate_limit_exceeded" in body Marks cooldown, reads Retry-After header
401, 403 Permanently disables the key
5xx Places on cooldown (transient)

Returns KeyState so you can branch on the result.

Multi-key Doppler pool with status tracking

import os
from open_keypool import KeyPool

DOPPLER_TOKEN = os.getenv("DOPPLER_TOKEN", "dp.st.YOUR_SERVICE_TOKEN")

pool = KeyPool.from_doppler(
    token=DOPPLER_TOKEN,
    project="refactor-ai",
    config="dev",
    key_prefix="GROQ_",
    strategy="round_robin",
)

# Every key's state, error history, and cooldown — safely masked
for masked_key, info in pool.status().items():
    print(f"{masked_key}  state={info['state']}  "
          f"http={info.get('last_status_code')}  "
          f"err=[{info.get('last_error_code')}]  "
          f"failures={info['failure_count']}")

Load keys from .env file

from open_keypool import KeyPool

# .env contains:
#   TSN_GROQ_KEY=sk-aaa
#   BACKUP_GROQ_KEY=sk-bbb
#   OTHER_SECRET=sk-ccc

pool = KeyPool.from_env(suffix="GROQ_KEY")
# Picks TSN_GROQ_KEY and BACKUP_GROQ_KEY (ends with "GROQ_KEY")

Load keys from JSON file

{
    "TSN_GROQ_KEY": "sk-aaa",
    "BACKUP_GROQ_KEY": "sk-bbb",
    "OTHER_SECRET": "sk-ccc"
}
from open_keypool import KeyPool

pool = KeyPool.from_json("keys.json", suffix="GROQ_KEY")
# Picks TSN_GROQ_KEY and BACKUP_GROQ_KEY (ends with "GROQ_KEY")

Constructor parameters

Parameter Type Default Description
keys list[str] required Initial API key strings (non-empty).
max_retries int 3 Max retry count reference for the caller's loop.
cooldown_seconds int 60 How long a rate-limited key stays in cooldown.
strategy str "round_robin" Rotation strategy: "round_robin" or "lru".

Doppler caching

KeyPool.from_doppler() uses an in-memory TTL cache with a 1-hour expiration. On the first call within a process, keys are fetched from Doppler and cached. Subsequent calls within the same hour serve keys from memory without touching the network. After one hour (if the process is still running), the cache entry expires and the next call fetches fresh keys automatically. The cache is never persisted across process restarts — every fresh process starts with an empty cache.

Pass force_refresh=True to bypass the cache and re-fetch immediately (useful after rotating keys in Doppler when you don't want to wait out the TTL).

Full API reference

docs/index.html — self-contained HTML page with quickstart + class/method documentation generated from docstrings.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

open_keypool-0.2.0.tar.gz (62.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

open_keypool-0.2.0-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

File details

Details for the file open_keypool-0.2.0.tar.gz.

File metadata

  • Download URL: open_keypool-0.2.0.tar.gz
  • Upload date:
  • Size: 62.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.13

File hashes

Hashes for open_keypool-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a555edc7c18fea2ad84542462dcad724326be0341d58405f37417603e1f91610
MD5 be9cd25ec9556fd2032a78a522ff5ce8
BLAKE2b-256 a630ced517f4935c260dbb6e78fb13b10d4a77cd9aa70ec1162a8118551c9378

See more details on using hashes here.

File details

Details for the file open_keypool-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: open_keypool-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 13.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.13

File hashes

Hashes for open_keypool-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 326cf72b4f2638df24b1ddd4ce89aba7f60f8993779d5b143228490e1f290571
MD5 ae97763f05471e975c62760b7ef10ceb
BLAKE2b-256 c5f9d70f4374bd70338315eed6ec7386a773917de8c848cc5fb2a69314c17c4a

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

This release

0.2.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page