api-service-handler 0.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. api_service_handler-0.1.6/.coveragerc +4 -0
  2. api_service_handler-0.1.6/.gitignore +15 -0
  3. api_service_handler-0.1.6/.python-version +1 -0
  4. api_service_handler-0.1.6/PKG-INFO +282 -0
  5. api_service_handler-0.1.6/README.md +248 -0
  6. api_service_handler-0.1.6/pyproject.toml +71 -0
  7. api_service_handler-0.1.6/src/api_service_handler/__init__.py +77 -0
  8. api_service_handler-0.1.6/src/api_service_handler/cli.py +341 -0
  9. api_service_handler-0.1.6/src/api_service_handler/client.py +868 -0
  10. api_service_handler-0.1.6/src/api_service_handler/config.py +177 -0
  11. api_service_handler-0.1.6/src/api_service_handler/encryption.py +238 -0
  12. api_service_handler-0.1.6/src/api_service_handler/enums.py +217 -0
  13. api_service_handler-0.1.6/src/api_service_handler/exceptions.py +184 -0
  14. api_service_handler-0.1.6/src/api_service_handler/models.py +301 -0
  15. api_service_handler-0.1.6/src/api_service_handler/py.typed +0 -0
  16. api_service_handler-0.1.6/src/api_service_handler/rate_limiter.py +187 -0
  17. api_service_handler-0.1.6/src/api_service_handler/rotation.py +163 -0
  18. api_service_handler-0.1.6/src/api_service_handler/storage/__init__.py +7 -0
  19. api_service_handler-0.1.6/src/api_service_handler/storage/base.py +243 -0
  20. api_service_handler-0.1.6/src/api_service_handler/storage/memory.py +229 -0
  21. api_service_handler-0.1.6/src/api_service_handler/storage/mongodb.py +432 -0
  22. api_service_handler-0.1.6/src/api_service_handler/storage/postgresql.py +429 -0
  23. api_service_handler-0.1.6/src/api_service_handler/storage/sqlite.py +511 -0
  24. api_service_handler-0.1.6/src/api_service_handler/usage_tracker.py +219 -0
  25. api_service_handler-0.1.6/src/api_service_handler/utils.py +322 -0
  26. api_service_handler-0.1.6/tests/conftest.py +56 -0
  27. api_service_handler-0.1.6/tests/test_cli.py +87 -0
  28. api_service_handler-0.1.6/tests/test_client.py +105 -0
  29. api_service_handler-0.1.6/tests/test_client_extra.py +92 -0
  30. api_service_handler-0.1.6/tests/test_config.py +41 -0
  31. api_service_handler-0.1.6/tests/test_encryption.py +40 -0
  32. api_service_handler-0.1.6/tests/test_logic.py +80 -0
  33. api_service_handler-0.1.6/tests/test_models.py +71 -0
  34. api_service_handler-0.1.6/tests/test_rate_limiter.py +69 -0
  35. api_service_handler-0.1.6/tests/test_storage_memory.py +82 -0
  36. api_service_handler-0.1.6/tests/test_storage_mongodb.py +52 -0
  37. api_service_handler-0.1.6/tests/test_storage_postgresql.py +47 -0
  38. api_service_handler-0.1.6/tests/test_storage_sqlite.py +107 -0
  39. api_service_handler-0.1.6/tests/test_usage_tracker.py +70 -0
  40. api_service_handler-0.1.6/tests/test_utils.py +77 -0
  41. api_service_handler-0.1.6/uv.lock +1118 -0
@@ -0,0 +1,4 @@
1
+ [run]
2
+ omit =
3
+ src/api_service_handler/storage/mongodb.py
4
+ src/api_service_handler/storage/postgresql.py
@@ -0,0 +1,15 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Test and coverage artifacts
13
+ .coverage
14
+ .pytest_cache/
15
+ htmlcov/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: api-service-handler
3
+ Version: 0.1.6
4
+ Summary: Enterprise API key management: rotation, rate-limiting, usage tracking, multi-backend storage
5
+ Author-email: Parkky <parth.kale.dev@gmail.com>
6
+ License-Expression: MIT
7
+ Keywords: api,key-management,rate-limiting,rotation,service-handler
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: click>=8.0
19
+ Requires-Dist: cryptography>=41.0
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: rich>=13.0
22
+ Requires-Dist: tabulate>=0.9
23
+ Provides-Extra: all
24
+ Requires-Dist: aiosqlite<1.0,>=0.19; extra == 'all'
25
+ Requires-Dist: asyncpg<1.0,>=0.27; extra == 'all'
26
+ Requires-Dist: motor<4.0,>=3.0; extra == 'all'
27
+ Provides-Extra: mongodb
28
+ Requires-Dist: motor<4.0,>=3.0; extra == 'mongodb'
29
+ Provides-Extra: postgresql
30
+ Requires-Dist: asyncpg<1.0,>=0.27; extra == 'postgresql'
31
+ Provides-Extra: sqlite
32
+ Requires-Dist: aiosqlite<1.0,>=0.19; extra == 'sqlite'
33
+ Description-Content-Type: text/markdown
34
+
35
+ <div align="center">
36
+ <h1>🚀 API Service Handler</h1>
37
+ <p><b>Enterprise-grade, async-first API key management for modern AI applications.</b></p>
38
+
39
+ <p>
40
+ <img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python Version" />
41
+ <img src="https://img.shields.io/badge/coverage-90%25-brightgreen.svg" alt="Test Coverage" />
42
+ <img src="https://img.shields.io/badge/asyncio-native-blueviolet.svg" alt="Asyncio" />
43
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
44
+ </p>
45
+ </div>
46
+
47
+ <br/>
48
+
49
+ A robust, async-first Python package for managing, rate-limiting, rotating, securely storing, and tracking usage of API keys across multiple providers (OpenAI, Anthropic, Google, and many more). Designed for LLM agent servers that need reliable, high-volume API key management.
50
+
51
+ ---
52
+
53
+ ## ✨ Core Features
54
+
55
+ | Feature | Description |
56
+ | --- | --- |
57
+ | ⚡ **Async Native** | Built entirely with `asyncio` for high-throughput, non-blocking applications. |
58
+ | 🔄 **Smart Rotation** | Automatically distributes API calls using Round Robin, Weighted, Random, or Least Used strategies. |
59
+ | 🛡️ **Rate Limiting** | Enforces daily, monthly, and concurrent usage limits to prevent throttling and unexpected billing overages. |
60
+ | 🔐 **Bank-Grade Encryption** | Protects your API keys at rest using AES-256-GCM encryption. |
61
+ | 🗄️ **Flexible Storage** | Choose between Memory, SQLite, MongoDB, or PostgreSQL backends out of the box. |
62
+ | 🏷️ **Extensible Metadata** | Tag keys and attach JSON metadata to easily query subsets of keys (e.g., `environment="prod"`). |
63
+
64
+ ---
65
+
66
+ ## 📦 Installation
67
+
68
+ Since the package is currently hosted on GitHub, you can install it using `uv` or `pip` by pointing directly to the repository and specifying a version tag.
69
+
70
+ > [!TIP]
71
+ > **Recommended:** Install using `uv` for lightning-fast dependency resolution.
72
+
73
+ ```bash
74
+ uv add git+https://github.com/parkky21/apikeys-manager.git@v0.1.6
75
+ ```
76
+
77
+ *Or using pip:*
78
+ ```bash
79
+ pip install git+https://github.com/parkky21/apikeys-manager.git@v0.1.6
80
+ ```
81
+
82
+ <details>
83
+ <summary><b>🗄️ Database Specific Extras (Click to expand)</b></summary>
84
+
85
+ If you plan to use **MongoDB** or **PostgreSQL** as your storage backend, install the relevant extras:
86
+
87
+ ```bash
88
+ # For MongoDB support
89
+ uv add "git+https://github.com/parkky21/apikeys-manager.git@v0.1.6#egg=api-service-handler[mongodb]"
90
+
91
+ # For PostgreSQL support
92
+ uv add "git+https://github.com/parkky21/apikeys-manager.git@v0.1.6#egg=api-service-handler[postgresql]"
93
+ ```
94
+
95
+ </details>
96
+
97
+ ---
98
+
99
+ ## ⚙️ Configuration & Initialization
100
+
101
+ The main entry point to the library is the `APIServiceHandler` class. It manages the connection to your underlying database, loads the encryption settings, and serves as the facade for all operations.
102
+
103
+ ```python
104
+ import asyncio
105
+ from api_service_handler.client import APIServiceHandler
106
+
107
+ async def main():
108
+ handler = APIServiceHandler(
109
+ storage_backend="sqlite", # memory, sqlite, mongodb, postgresql
110
+ connection_string="sqlite:///api_keys.db", # Path or URI to database
111
+ encrypt_keys=True, # Strongly recommended
112
+ shared_secret="your-super-secret-32-byte-key-here!!!", # Used for AES-GCM
113
+ rotation_strategy="round_robin", # round_robin, least_used, random, weighted
114
+ auto_reset_counters=True, # Auto-refresh quotas on day/month rollover
115
+ soft_delete=True # Keep deleted keys as "revoked" for auditing
116
+ )
117
+
118
+ # ⚠️ You MUST initialize the handler to establish db connections
119
+ await handler.initialize()
120
+
121
+ print("🚀 API Service Handler is ready!")
122
+
123
+ # Safely close connections before your app shuts down
124
+ await handler.close()
125
+
126
+ if __name__ == "__main__":
127
+ asyncio.run(main())
128
+ ```
129
+
130
+ > [!NOTE]
131
+ > **Environment Variables:** You can configure the client purely using environment variables (`ASH_STORAGE_BACKEND`, `ASH_CONNECTION_STRING`, `ASH_SHARED_SECRET`, `ASH_ROTATION_STRATEGY`).
132
+
133
+ ---
134
+
135
+ ## 🔑 Managing API Keys
136
+
137
+ ### Adding a Key
138
+ Add keys dynamically with precise controls over their environments and usage thresholds.
139
+
140
+ ```python
141
+ from api_service_handler.models import Environment
142
+
143
+ key = await handler.add_key(
144
+ provider="openai", # Supported provider enum string
145
+ key_value="sk-proj-1234567890", # The raw API key
146
+ alias="prod-gpt4-key", # Friendly name
147
+ daily_limit=1000, # Max 1000 uses per day
148
+ monthly_limit=25000, # Max 25000 uses per month
149
+ max_concurrent=5, # Max 5 active parallel requests
150
+ environment=Environment.PRODUCTION,
151
+ tags=["premium", "gpt-4"],
152
+ metadata={"billing_account": "acct_123"},
153
+ weight=10 # Useful if using "weighted" rotation
154
+ )
155
+ ```
156
+
157
+ ### Retrieving & Filtering
158
+ Keys are retrieved as `APIKey` Pydantic models. **By default, `key_value` is encrypted at rest and will remain masked unless explicitly requested.**
159
+
160
+ ```python
161
+ # Get a specific key (Decrypted)
162
+ key = await handler.get_key(key_id="uuid-string", decrypt=True)
163
+
164
+ # Complex Filtering
165
+ filtered_keys = await handler.get_all_keys(
166
+ provider="google_gemini",
167
+ tags=["premium"],
168
+ environment="production",
169
+ has_capacity=True # ✨ Magic! Only returns keys that haven't hit rate limits
170
+ )
171
+ ```
172
+
173
+ ### 🎯 Metadata Filtering (Deep Search)
174
+ You can attach arbitrary JSON metadata to any key and query against it. All storage backends (including Memory & SQLite) natively support filtering by nested metadata using dot-notation!
175
+
176
+ ```python
177
+ # Returns only keys assigned to the engineering team
178
+ engineering_keys = await handler.get_all_keys(
179
+ metadata_filter={"team": "engineering"}
180
+ )
181
+
182
+ # Nested JSON matching using dot-notation!
183
+ enterprise_keys = await handler.get_all_keys(
184
+ metadata_filter={"billing.tier": "enterprise"}
185
+ )
186
+
187
+ # You can even route requests strictly based on metadata!
188
+ async with handler.use_key(provider="openai", metadata_filter={"project_id": "proj-789"}) as p_key:
189
+ pass
190
+ ```
191
+
192
+ ---
193
+
194
+ ## ♻️ Using Keys (Rotation & Rate Limiting)
195
+
196
+ The core purpose of this library is to safely dispense API keys while preventing you from hitting provider rate limits.
197
+
198
+ The **safest and easiest** way to retrieve a key is using the `use_key` context manager. It automatically:
199
+ 1. Filters out keys that have hit their Daily/Monthly limits.
200
+ 2. Filters out keys that have hit their Max Concurrent limit.
201
+ 3. Rotates between valid keys based on your strategy.
202
+ 4. Safely tracks parallel execution blocks and historical usage.
203
+
204
+ ```python
205
+ from api_service_handler.exceptions import RateLimitExceededError, NoAvailableKeyError, MaxConcurrentExceededError
206
+
207
+ async def generate_text(prompt: str):
208
+ try:
209
+ # Request a key for Anthropic that is designated for production
210
+ async with handler.use_key(provider="anthropic", environment="production") as api_key:
211
+
212
+ # The key is automatically decrypted and ready to use
213
+ raw_key = api_key.key_value
214
+ print(f"Executing request with {api_key.alias}")
215
+
216
+ # response = await my_llm_client.chat(api_key=raw_key, messages=prompt)
217
+
218
+ except NoAvailableKeyError:
219
+ print("❌ No active Anthropic keys found!")
220
+ except RateLimitExceededError as e:
221
+ print(f"🛑 All keys are rate limited! {e}")
222
+ except MaxConcurrentExceededError as e:
223
+ print(f"⚠️ Too many parallel requests active right now! {e}")
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 📊 Usage Tracking & Statistics
229
+
230
+ Track exactly how much your APIs are being utilized. Counters auto-reset at the start of a new calendar day/month based on UTC time.
231
+
232
+ ```python
233
+ # Get usage for a specific key
234
+ stats = await handler.get_usage_stats(key.id)
235
+ print(f"Daily remaining: {stats.daily_remaining}")
236
+
237
+ # Get aggregated usage across all keys for a provider
238
+ provider_stats = await handler.get_provider_stats("openai")
239
+ for stat in provider_stats:
240
+ print(f"{stat.alias}: {stat.daily_usage_count} uses today")
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 💻 CLI Tool (`ash`)
246
+
247
+ A full-featured command-line utility is bundled with the package for administrative tasks.
248
+
249
+ > [!IMPORTANT]
250
+ > Ensure you have your environment variables exported so the CLI connects to your production database!
251
+ > `export ASH_CONNECTION_STRING="postgresql://user:pass@localhost/db"`
252
+
253
+ | Command | Description | Example |
254
+ | :--- | :--- | :--- |
255
+ | `ash keys add` | Add a new key | `ash keys add --provider openai --key "sk-xyz" --alias "prod"` |
256
+ | `ash keys list` | List all keys visually | `ash keys list --provider anthropic --show-keys` |
257
+ | `ash keys get` | View detailed key info | `ash keys get <key_id>` |
258
+ | `ash keys update`| Update aliases or limits | `ash keys update <key_id> --alias "new-name"` |
259
+ | `ash keys delete`| Soft/Hard delete a key | `ash keys delete <key_id> --hard` |
260
+ | `ash usage stats`| View limit vs usage data | `ash usage stats <key_id>` |
261
+ | `ash health` | Test DB connection | `ash health` |
262
+
263
+ ---
264
+
265
+ ## 🌐 Supported Providers
266
+
267
+ The library enforces strict string enums for API providers. You can use any of the following (case-insensitive):
268
+
269
+ <details open>
270
+ <summary><b>Click to see full list</b></summary>
271
+
272
+ - **🤖 AI / LLM:** `openai`, `anthropic`, `google_gemini`, `google_vertex`, `mistral`, `cohere`, `huggingface`, `replicate`, `together_ai`, `groq`, `fireworks`, `deepseek`, `xai`, `perplexity`, `openrouter`, `lemofox`
273
+ - **🎙️ Speech / Audio:** `deepgram`, `eleven_labs`, `assembly_ai`, `whisper`
274
+ - **☁️ Cloud & Auth:** `aws`, `azure`, `gcp`, `cloudflare`, `vercel`, `auth0`, `clerk`, `supabase_auth`
275
+ - **💬 Communication:** `twilio`, `sendgrid`, `mailgun`, `resend`, `postmark`
276
+ - **💳 Payments:** `stripe`, `razorpay`, `paypal`, `lemonsqueezy`
277
+ - **🔍 Search & Vector:** `serp_api`, `bing_search`, `algolia`, `pinecone`, `weaviate`
278
+ - **🔧 Misc:** `github`, `slack`, `discord`, `custom`
279
+
280
+ *(If a provider isn't strictly typed, it will safely fallback to `"custom"`, though you can also just pass `"custom"`).*
281
+
282
+ </details>
@@ -0,0 +1,248 @@
1
+ <div align="center">
2
+ <h1>🚀 API Service Handler</h1>
3
+ <p><b>Enterprise-grade, async-first API key management for modern AI applications.</b></p>
4
+
5
+ <p>
6
+ <img src="https://img.shields.io/badge/python-3.10%2B-blue.svg" alt="Python Version" />
7
+ <img src="https://img.shields.io/badge/coverage-90%25-brightgreen.svg" alt="Test Coverage" />
8
+ <img src="https://img.shields.io/badge/asyncio-native-blueviolet.svg" alt="Asyncio" />
9
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
10
+ </p>
11
+ </div>
12
+
13
+ <br/>
14
+
15
+ A robust, async-first Python package for managing, rate-limiting, rotating, securely storing, and tracking usage of API keys across multiple providers (OpenAI, Anthropic, Google, and many more). Designed for LLM agent servers that need reliable, high-volume API key management.
16
+
17
+ ---
18
+
19
+ ## ✨ Core Features
20
+
21
+ | Feature | Description |
22
+ | --- | --- |
23
+ | ⚡ **Async Native** | Built entirely with `asyncio` for high-throughput, non-blocking applications. |
24
+ | 🔄 **Smart Rotation** | Automatically distributes API calls using Round Robin, Weighted, Random, or Least Used strategies. |
25
+ | 🛡️ **Rate Limiting** | Enforces daily, monthly, and concurrent usage limits to prevent throttling and unexpected billing overages. |
26
+ | 🔐 **Bank-Grade Encryption** | Protects your API keys at rest using AES-256-GCM encryption. |
27
+ | 🗄️ **Flexible Storage** | Choose between Memory, SQLite, MongoDB, or PostgreSQL backends out of the box. |
28
+ | 🏷️ **Extensible Metadata** | Tag keys and attach JSON metadata to easily query subsets of keys (e.g., `environment="prod"`). |
29
+
30
+ ---
31
+
32
+ ## 📦 Installation
33
+
34
+ Since the package is currently hosted on GitHub, you can install it using `uv` or `pip` by pointing directly to the repository and specifying a version tag.
35
+
36
+ > [!TIP]
37
+ > **Recommended:** Install using `uv` for lightning-fast dependency resolution.
38
+
39
+ ```bash
40
+ uv add git+https://github.com/parkky21/apikeys-manager.git@v0.1.6
41
+ ```
42
+
43
+ *Or using pip:*
44
+ ```bash
45
+ pip install git+https://github.com/parkky21/apikeys-manager.git@v0.1.6
46
+ ```
47
+
48
+ <details>
49
+ <summary><b>🗄️ Database Specific Extras (Click to expand)</b></summary>
50
+
51
+ If you plan to use **MongoDB** or **PostgreSQL** as your storage backend, install the relevant extras:
52
+
53
+ ```bash
54
+ # For MongoDB support
55
+ uv add "git+https://github.com/parkky21/apikeys-manager.git@v0.1.6#egg=api-service-handler[mongodb]"
56
+
57
+ # For PostgreSQL support
58
+ uv add "git+https://github.com/parkky21/apikeys-manager.git@v0.1.6#egg=api-service-handler[postgresql]"
59
+ ```
60
+
61
+ </details>
62
+
63
+ ---
64
+
65
+ ## ⚙️ Configuration & Initialization
66
+
67
+ The main entry point to the library is the `APIServiceHandler` class. It manages the connection to your underlying database, loads the encryption settings, and serves as the facade for all operations.
68
+
69
+ ```python
70
+ import asyncio
71
+ from api_service_handler.client import APIServiceHandler
72
+
73
+ async def main():
74
+ handler = APIServiceHandler(
75
+ storage_backend="sqlite", # memory, sqlite, mongodb, postgresql
76
+ connection_string="sqlite:///api_keys.db", # Path or URI to database
77
+ encrypt_keys=True, # Strongly recommended
78
+ shared_secret="your-super-secret-32-byte-key-here!!!", # Used for AES-GCM
79
+ rotation_strategy="round_robin", # round_robin, least_used, random, weighted
80
+ auto_reset_counters=True, # Auto-refresh quotas on day/month rollover
81
+ soft_delete=True # Keep deleted keys as "revoked" for auditing
82
+ )
83
+
84
+ # ⚠️ You MUST initialize the handler to establish db connections
85
+ await handler.initialize()
86
+
87
+ print("🚀 API Service Handler is ready!")
88
+
89
+ # Safely close connections before your app shuts down
90
+ await handler.close()
91
+
92
+ if __name__ == "__main__":
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ > [!NOTE]
97
+ > **Environment Variables:** You can configure the client purely using environment variables (`ASH_STORAGE_BACKEND`, `ASH_CONNECTION_STRING`, `ASH_SHARED_SECRET`, `ASH_ROTATION_STRATEGY`).
98
+
99
+ ---
100
+
101
+ ## 🔑 Managing API Keys
102
+
103
+ ### Adding a Key
104
+ Add keys dynamically with precise controls over their environments and usage thresholds.
105
+
106
+ ```python
107
+ from api_service_handler.models import Environment
108
+
109
+ key = await handler.add_key(
110
+ provider="openai", # Supported provider enum string
111
+ key_value="sk-proj-1234567890", # The raw API key
112
+ alias="prod-gpt4-key", # Friendly name
113
+ daily_limit=1000, # Max 1000 uses per day
114
+ monthly_limit=25000, # Max 25000 uses per month
115
+ max_concurrent=5, # Max 5 active parallel requests
116
+ environment=Environment.PRODUCTION,
117
+ tags=["premium", "gpt-4"],
118
+ metadata={"billing_account": "acct_123"},
119
+ weight=10 # Useful if using "weighted" rotation
120
+ )
121
+ ```
122
+
123
+ ### Retrieving & Filtering
124
+ Keys are retrieved as `APIKey` Pydantic models. **By default, `key_value` is encrypted at rest and will remain masked unless explicitly requested.**
125
+
126
+ ```python
127
+ # Get a specific key (Decrypted)
128
+ key = await handler.get_key(key_id="uuid-string", decrypt=True)
129
+
130
+ # Complex Filtering
131
+ filtered_keys = await handler.get_all_keys(
132
+ provider="google_gemini",
133
+ tags=["premium"],
134
+ environment="production",
135
+ has_capacity=True # ✨ Magic! Only returns keys that haven't hit rate limits
136
+ )
137
+ ```
138
+
139
+ ### 🎯 Metadata Filtering (Deep Search)
140
+ You can attach arbitrary JSON metadata to any key and query against it. All storage backends (including Memory & SQLite) natively support filtering by nested metadata using dot-notation!
141
+
142
+ ```python
143
+ # Returns only keys assigned to the engineering team
144
+ engineering_keys = await handler.get_all_keys(
145
+ metadata_filter={"team": "engineering"}
146
+ )
147
+
148
+ # Nested JSON matching using dot-notation!
149
+ enterprise_keys = await handler.get_all_keys(
150
+ metadata_filter={"billing.tier": "enterprise"}
151
+ )
152
+
153
+ # You can even route requests strictly based on metadata!
154
+ async with handler.use_key(provider="openai", metadata_filter={"project_id": "proj-789"}) as p_key:
155
+ pass
156
+ ```
157
+
158
+ ---
159
+
160
+ ## ♻️ Using Keys (Rotation & Rate Limiting)
161
+
162
+ The core purpose of this library is to safely dispense API keys while preventing you from hitting provider rate limits.
163
+
164
+ The **safest and easiest** way to retrieve a key is using the `use_key` context manager. It automatically:
165
+ 1. Filters out keys that have hit their Daily/Monthly limits.
166
+ 2. Filters out keys that have hit their Max Concurrent limit.
167
+ 3. Rotates between valid keys based on your strategy.
168
+ 4. Safely tracks parallel execution blocks and historical usage.
169
+
170
+ ```python
171
+ from api_service_handler.exceptions import RateLimitExceededError, NoAvailableKeyError, MaxConcurrentExceededError
172
+
173
+ async def generate_text(prompt: str):
174
+ try:
175
+ # Request a key for Anthropic that is designated for production
176
+ async with handler.use_key(provider="anthropic", environment="production") as api_key:
177
+
178
+ # The key is automatically decrypted and ready to use
179
+ raw_key = api_key.key_value
180
+ print(f"Executing request with {api_key.alias}")
181
+
182
+ # response = await my_llm_client.chat(api_key=raw_key, messages=prompt)
183
+
184
+ except NoAvailableKeyError:
185
+ print("❌ No active Anthropic keys found!")
186
+ except RateLimitExceededError as e:
187
+ print(f"🛑 All keys are rate limited! {e}")
188
+ except MaxConcurrentExceededError as e:
189
+ print(f"⚠️ Too many parallel requests active right now! {e}")
190
+ ```
191
+
192
+ ---
193
+
194
+ ## 📊 Usage Tracking & Statistics
195
+
196
+ Track exactly how much your APIs are being utilized. Counters auto-reset at the start of a new calendar day/month based on UTC time.
197
+
198
+ ```python
199
+ # Get usage for a specific key
200
+ stats = await handler.get_usage_stats(key.id)
201
+ print(f"Daily remaining: {stats.daily_remaining}")
202
+
203
+ # Get aggregated usage across all keys for a provider
204
+ provider_stats = await handler.get_provider_stats("openai")
205
+ for stat in provider_stats:
206
+ print(f"{stat.alias}: {stat.daily_usage_count} uses today")
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 💻 CLI Tool (`ash`)
212
+
213
+ A full-featured command-line utility is bundled with the package for administrative tasks.
214
+
215
+ > [!IMPORTANT]
216
+ > Ensure you have your environment variables exported so the CLI connects to your production database!
217
+ > `export ASH_CONNECTION_STRING="postgresql://user:pass@localhost/db"`
218
+
219
+ | Command | Description | Example |
220
+ | :--- | :--- | :--- |
221
+ | `ash keys add` | Add a new key | `ash keys add --provider openai --key "sk-xyz" --alias "prod"` |
222
+ | `ash keys list` | List all keys visually | `ash keys list --provider anthropic --show-keys` |
223
+ | `ash keys get` | View detailed key info | `ash keys get <key_id>` |
224
+ | `ash keys update`| Update aliases or limits | `ash keys update <key_id> --alias "new-name"` |
225
+ | `ash keys delete`| Soft/Hard delete a key | `ash keys delete <key_id> --hard` |
226
+ | `ash usage stats`| View limit vs usage data | `ash usage stats <key_id>` |
227
+ | `ash health` | Test DB connection | `ash health` |
228
+
229
+ ---
230
+
231
+ ## 🌐 Supported Providers
232
+
233
+ The library enforces strict string enums for API providers. You can use any of the following (case-insensitive):
234
+
235
+ <details open>
236
+ <summary><b>Click to see full list</b></summary>
237
+
238
+ - **🤖 AI / LLM:** `openai`, `anthropic`, `google_gemini`, `google_vertex`, `mistral`, `cohere`, `huggingface`, `replicate`, `together_ai`, `groq`, `fireworks`, `deepseek`, `xai`, `perplexity`, `openrouter`, `lemofox`
239
+ - **🎙️ Speech / Audio:** `deepgram`, `eleven_labs`, `assembly_ai`, `whisper`
240
+ - **☁️ Cloud & Auth:** `aws`, `azure`, `gcp`, `cloudflare`, `vercel`, `auth0`, `clerk`, `supabase_auth`
241
+ - **💬 Communication:** `twilio`, `sendgrid`, `mailgun`, `resend`, `postmark`
242
+ - **💳 Payments:** `stripe`, `razorpay`, `paypal`, `lemonsqueezy`
243
+ - **🔍 Search & Vector:** `serp_api`, `bing_search`, `algolia`, `pinecone`, `weaviate`
244
+ - **🔧 Misc:** `github`, `slack`, `discord`, `custom`
245
+
246
+ *(If a provider isn't strictly typed, it will safely fallback to `"custom"`, though you can also just pass `"custom"`).*
247
+
248
+ </details>
@@ -0,0 +1,71 @@
1
+ [project]
2
+ name = "api-service-handler"
3
+ version = "0.1.6"
4
+ description = "Enterprise API key management: rotation, rate-limiting, usage tracking, multi-backend storage"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Parkky", email = "parth.kale.dev@gmail.com" },
9
+ ]
10
+ requires-python = ">=3.10"
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Typing :: Typed",
21
+ ]
22
+ keywords = ["api", "key-management", "rotation", "rate-limiting", "service-handler"]
23
+ dependencies = [
24
+ "pydantic>=2.0",
25
+ "cryptography>=41.0",
26
+ "click>=8.0",
27
+ "rich>=13.0",
28
+ "tabulate>=0.9",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ mongodb = ["motor>=3.0,<4.0"]
33
+ postgresql = ["asyncpg>=0.27,<1.0"]
34
+ sqlite = ["aiosqlite>=0.19,<1.0"]
35
+ all = [
36
+ "motor>=3.0,<4.0",
37
+ "asyncpg>=0.27,<1.0",
38
+ "aiosqlite>=0.19,<1.0",
39
+ ]
40
+
41
+ [project.scripts]
42
+ ash = "api_service_handler.cli:cli"
43
+
44
+ [dependency-groups]
45
+ dev = [
46
+ "pytest>=8.0",
47
+ "pytest-asyncio>=0.23",
48
+ "pytest-cov>=5.0",
49
+ "ruff>=0.4",
50
+ "mypy>=1.10",
51
+ "aiosqlite>=0.19,<1.0",
52
+ ]
53
+
54
+ [build-system]
55
+ requires = ["hatchling"]
56
+ build-backend = "hatchling.build"
57
+
58
+ [tool.pytest.ini_options]
59
+ addopts = "--cov=api_service_handler --cov-report=term-missing"
60
+ asyncio_mode = "auto"
61
+ asyncio_default_fixture_loop_scope = "function"
62
+
63
+ [tool.coverage.run]
64
+ omit = [
65
+ "src/api_service_handler/storage/mongodb.py",
66
+ "src/api_service_handler/storage/postgresql.py"
67
+ ]
68
+
69
+ [tool.ruff]
70
+ target-version = "py310"
71
+ line-length = 100