Short answer: The AICredits Management API (beta) lets a script with an akm- key check the wallet, create and revoke sk-live- inference keys, pull usage and transactions, and manage teams, with scopes controlling what each key can do. Send an Idempotency-Key on every write. Everything below is untested against the live API.
Disclosure: the aicredits.in links are affiliate links and I use the service. This API is beta. Endpoints and fields below follow AICredits' management API docs as read on 16 Oct 2026; the Python was not run against the live API, so test with a throwaway key and a tiny budget first.
Clicking through a dashboard works for one key. It stops working at the tenth project, or when someone leaves and you need to revoke their keys before lunch. The Management API is AICredits' answer: the same account operations, scriptable, behind a separate class of key.
This guide shows the key model, the endpoints worth knowing, and four small scripts: a pre-job wallet check, per-project key provisioning, key rotation and a spend export.
What is a management key, and how is it different?
| Inference key | Management key | |
|---|---|---|
| Prefix | sk-live-... | akm-... |
| Calls | POST /v1/chat/completions and other inference endpoints | Management API only |
| Scopes | None | Yes, no wildcard |
| Can call the other's endpoints | No | No |
The separation is the security feature. A leaked inference key can burn credits up to its budget; a leaked management key can create more keys, so give it only the scopes it needs. Keys are scoped to your own account and last at most one year. Create one on the Management API page in the dashboard (beta): name it, pick scopes, copy the akm- value, which is shown once.
Which scopes exist?
| Scope | Grants |
|---|---|
keys:read | List inference keys |
keys:write | Create, update, revoke inference keys |
wallet:read | Wallet balance and sufficiency checks |
usage:read | Usage logs, stats, cost per model |
transactions:read | Credit, debit, refund history |
team:read | List teams and members |
team:write | Create teams; invite, allocate, remove members |
A missing scope returns 403; a missing, expired or wrong-type key returns 401. Using an sk- key here is a 401. The management key has its own rate limit, 60 RPM by default, with the usual X-RateLimit-* headers.
Script 1: check the wallet before a batch job
GET /api/v1/mgmt/wallet with ?required_inr= returns a verdict. The documented response includes balance_inr, low_balance, max_wallet_balance_inr, required_inr and sufficient.
import os
import sys
import requests
BASE = "https://api.aicredits.in/api/v1/mgmt"
MGMT = {"Authorization": f"Bearer {os.environ['AICREDITS_MGMT_KEY']}"} # scope: wallet:read
def wallet_ok(required_inr: float) -> bool:
r = requests.get(f"{BASE}/wallet", headers=MGMT, params={"required_inr": required_inr}, timeout=30)
r.raise_for_status()
data = r.json()
print(f"balance INR {data['balance_inr']}, need {required_inr}, sufficient={data['sufficient']}")
return bool(data["sufficient"])
if __name__ == "__main__":
if not wallet_ok(float(sys.argv[1])):
sys.exit("Top up before running this job")
Run it at the top of a nightly pipeline: python wallet_check.py 250. Not run against the live API.
Script 2: one key per project, with budget and rate limit
POST /api/v1/mgmt/keys takes name, budget (must be above zero) and rate_limit (capped at 600 RPM on this path). It returns {"api_key": "sk-live-..."} once. This function sends an idempotency key so a retried network call can't create a duplicate:
import uuid
import requests
def create_project_key(mgmt_headers: dict, project: str, budget_inr: int, rpm: int) -> str:
r = requests.post(
"https://api.aicredits.in/api/v1/mgmt/keys",
headers={**mgmt_headers, "Content-Type": "application/json",
"Idempotency-Key": f"create-{project}-{uuid.uuid4()}"},
json={"name": project, "budget": budget_inr, "rate_limit": rpm},
timeout=30,
)
r.raise_for_status()
return r.json()["api_key"]
A caution on that uuid4: a random idempotency key per call protects against nothing if your own code retries by calling the function again. Derive the key from stable inputs (project and date) and persist the secret the moment you receive it. The replay behaviour is documented to last 24 hours.
Pick budgets with the numbers in the per-key budgets post.
Script 3: the rotate-keys-per-project pattern
Rotation means a new key goes live before the old one dies, so there is no outage. A safe order with the documented endpoints:
GET /keysto find the current key'sidfor the project.POST /keysto create the replacement with the same budget and RPM.- Write the new secret to your secret manager and deploy.
- Watch
GET /usage(filtered by?model=if useful) until traffic on the old key stops. DELETE /keys/{id}to revoke the old one.
import os
import requests
BASE = "https://api.aicredits.in/api/v1/mgmt"
H = {"Authorization": f"Bearer {os.environ['AICREDITS_MGMT_KEY']}"} # keys:read + keys:write
def find_key_id(name: str):
keys = requests.get(f"{BASE}/keys", headers=H, timeout=30)
keys.raise_for_status()
payload = keys.json()
items = payload.get("keys", payload) if isinstance(payload, dict) else payload
return next((k["id"] for k in items if k.get("name") == name), None)
def revoke(key_id: str):
requests.delete(f"{BASE}/keys/{key_id}", headers=H, timeout=30).raise_for_status()
The list response shape is not shown in the docs I read, so find_key_id guesses at it defensively; check the real JSON before relying on it. Never revoke before the new secret is deployed and verified.
Script 4: monthly usage export for finance
GET /usage/export returns the per-request log as CSV (up to 10,000 rows); usage/stats returns totals; usage/daily returns cost per day for 30 days; usage/cost-per-model groups by model.
import os
import requests
H = {"Authorization": f"Bearer {os.environ['AICREDITS_MGMT_KEY']}"} # usage:read
r = requests.get("https://api.aicredits.in/api/v1/mgmt/usage/export", headers=H, timeout=60)
r.raise_for_status()
open("aicredits_usage.csv", "wb").write(r.content)
print(len(r.content), "bytes saved")
This pairs with the finance steps in the GST invoice checklist.
How do teams and idempotent invites work?
Team endpoints live under /teams (scopes team:read and team:write). The doc's warning is worth repeating: inviting a member does not send an email or create an account. The person must sign up with the exact address to be linked, and repeated invites for the same email add to the allocation unless you use an idempotency key or the absolute form.
curl -X POST https://api.aicredits.in/api/v1/mgmt/teams/TEAM_ID/members \
-H "Authorization: Bearer akm-your-management-key" \
-H "Idempotency-Key: invite-2026-10-16-alice" \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","allocate_inr":200}'
To change an allocation later, use the allocate endpoint with {"set": true, "amount_inr": 300}, the idempotent form the docs recommend from scripts. It never drops below what the member already spent.
Error handling you should write on day one
| Status | Meaning | Script behaviour |
|---|---|---|
| 401 | Missing, invalid, expired key, or an sk- key | Fail loudly; do not retry |
| 403 | Scope missing | Fix the key's scopes |
| 404 | Not found or not yours | Check the id |
| 422 | Idempotency key reused with a different body | Fix the bug; do not "retry" |
| 429 | Management rate limit | Back off using the reset header |
| 503 | API temporarily disabled | Retry later, alert if long |
A minimal thin client to absorb beta changes
Because the surface may change, funnel every call through one function. When a path or field moves, you edit one place:
import os
import requests
class Mgmt:
base = "https://api.aicredits.in/api/v1/mgmt"
def __init__(self, key=None):
self.h = {"Authorization": f"Bearer {key or os.environ['AICREDITS_MGMT_KEY']}"}
def call(self, method, path, idem=None, **kw):
headers = dict(self.h)
if idem:
headers["Idempotency-Key"] = idem
r = requests.request(method, self.base + path, headers=headers, timeout=30, **kw)
if r.status_code == 429:
raise RuntimeError(f"rate limited, reset in {r.headers.get('X-RateLimit-Reset')}s")
r.raise_for_status()
return r
Untested, like the rest. Usage: Mgmt().call("GET", "/wallet", params={"required_inr": 100}).json().
Beta caveats and safe habits
- It is beta: wrap calls in one small client module so a changed field name is a one-line fix.
- Keep
akm-keys out of CI logs and client code, use the narrowest scopes, and revoke any key you are not using. - Test against a throwaway key with a budget of a few rupees before touching production keys.
- Related tool: the same
akm-key powers the read-only MCP server. - Context for the whole service lives in the AICredits review, and key hygiene in the leak prevention checklist.
Bottom line
The Management API turns key and wallet chores into a script: check the wallet before jobs, one key per project, rotate on a schedule, export usage monthly. Treat it as beta, keep scopes narrow, send idempotency keys on writes. To get a management key you need an account first, so sign up here, top up a small amount and try it on a throwaway key.



