Custom integration (any language)
The FastAPI SDK is a convenience layer over three HTTP endpoints — the SDK API. If you run Go, Node, Rails, Laravel or anything else, you can implement the same pattern yourself. This guide documents the endpoints and the caching/reporting pattern that makes the integration fast and robust.
Authentication
All SDK API calls authenticate with your tenant secret key (mas_..., from the tenant's Overview tab):
Authorization: Bearer mas_...Base URL: https://api.microauth.com. A suspended tenant receives 403 on every SDK API call.
Keys are matched by hash
Your customers send you their API key (map_...). You never forward the raw key to MicroAuth — you always work with its SHA-256 hex digest (sha256("map_...") → 64 lowercase hex chars). The snapshot contains key hashes, and the verify endpoint takes a key_hash query parameter.
The pattern in one diagram
your API process MicroAuth
┌──────────────────────────┐
│ in-memory cache │ every ~30s GET /sdk/v1/snapshot
│ keys + customers ◄─────┼───────────────────────────────────►
│ │ cache miss GET /sdk/v1/keys/verify
│ per-request: │───────────────────────────────────►
│ hash key → lookup → │
│ enforce → count │ every ~15s POST /sdk/v1/usage
│ usage counters ───────┼───────────────────────────────────►
└──────────────────────────┘Never call MicroAuth on the hot path. Serve requests from the cache; talk to MicroAuth in the background.
1. GET /sdk/v1/snapshot
Returns everything you need to authenticate and enforce limits locally: all active customers with their effective limits, and all active key hashes.
curl https://api.microauth.com/sdk/v1/snapshot \
-H "Authorization: Bearer mas_..."{
"generated_at": "2026-08-03T18:00:00Z",
"billable_status_codes": [200],
"customers": [
{
"id": "c6f7e9a2-...",
"status": "active",
"credit_balance_micro": 12500000,
"month_requests": 48210,
"effective": {
"rps": 10,
"price_per_request_micro": 500,
"monthly_quota": 1000000,
"source": "plan",
"billing_model": "subscription"
}
}
],
"keys": [
{
"id": "9b1d3f60-...",
"key_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"customer_id": "c6f7e9a2-..."
}
]
}Notes:
effective.monthly_quotais omitted when the customer has no request cap.effective.sourceiscustom,planorpayg— which layer of the limit resolution won.- Refresh every ~30 seconds. The endpoint is rate limited to 60 requests/minute per tenant; polling faster returns
429and buys you nothing. - Build two lookup maps from the response:
key_hash → keyandcustomer_id → customer.
2. GET /sdk/v1/keys/verify
For keys that aren't in your cached snapshot (e.g. created seconds ago). Resolves one key hash to its customer and limits:
curl "https://api.microauth.com/sdk/v1/keys/verify?key_hash=$(printf '%s' "map_..." | shasum -a 256 | cut -d' ' -f1)" \
-H "Authorization: Bearer mas_..."Valid key:
{
"valid": true,
"key_id": "9b1d3f60-...",
"billable_status_codes": [200],
"customer": { "id": "c6f7e9a2-...", "status": "active", "credit_balance_micro": 12500000, "month_requests": 48210, "effective": { "rps": 10, "price_per_request_micro": 500, "source": "plan", "billing_model": "subscription" } }
}Unknown or revoked key: { "valid": false } (HTTP 200 — the call succeeded, the key didn't).
Guidelines:
- Only call this on a cache miss, and de-duplicate concurrent misses for the same hash (single-flight), so a burst can't stampede MicroAuth.
- Negatively cache invalid hashes for ~30 seconds; a flood of garbage keys should be absorbed by your cache, not forwarded.
- Rate limit: 600 requests/minute per tenant.
3. POST /sdk/v1/usage
Report billable requests in batches. MicroAuth records usage and charges each customer's prepaid balance at their effective per-request price.
curl -X POST https://api.microauth.com/sdk/v1/usage \
-H "Authorization: Bearer mas_..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "key_id": "9b1d3f60-...", "requests": 42, "period_start": "2026-08-03T18:00:00Z" }
]
}'{ "accepted": 1, "rejected": 0 }Rules and semantics:
- Only count billable responses — those whose HTTP status is in
billable_status_codesfrom the snapshot (default[200]). Don't charge your customers for your own500s. - Aggregate locally as one counter per
key_idper hour, flush every ~15 seconds.period_startis bucketed to the hour server-side, and buckets are summed additively — so multiple workers reporting the same hour is correct by design. - Limits: 1–1000 items per call, 1–10,000,000 requests per item, 600 calls/minute per tenant.
rejectedcounts items that couldn't be applied (unknown key, suspended customer, overflow). Rejected items should not be retried blindly — a suspended customer's usage stays rejected.- If MicroAuth is unreachable, keep the counters and retry with backoff. Usage reporting is additive per hour bucket, so late reports are fine — but don't re-send items that were already accepted (that would double-charge; dedupe on your side per flush).
Local enforcement checklist
On each incoming request to your API:
- Read the key from your header (e.g.
X-API-Key); missing →401. hash = sha256_hex(key); look it up in the snapshot; miss → verify endpoint (single-flight) → still invalid →401.- Customer
status != "active"→403. billing_model != "none"andcredit_balance_micro <= 0→402(account for usage you've counted locally since the last snapshot).monthly_quotaset andmonth_requests+ local count ≥ quota →429.- Enforce
effective.rpsper customer (token bucket or fixed window; use Redis if you run many workers) → over →429withRetry-After. - Serve the request. If the response status is billable, increment the counter for
(key_id, current_hour).
Minimal reference implementation (Python, ~no framework)
import hashlib, threading, time, requests
BASE = "https://api.microauth.com"
HEADERS = {"Authorization": "Bearer mas_..."}
snapshot = {"keys": {}, "customers": {}, "billable": {200}}
usage = {} # (key_id, hour_iso) -> count
lock = threading.Lock()
def sync_loop():
while True:
r = requests.get(f"{BASE}/sdk/v1/snapshot", headers=HEADERS, timeout=5)
if r.ok:
d = r.json()
with lock:
snapshot["keys"] = {k["key_hash"]: k for k in d["keys"]}
snapshot["customers"] = {c["id"]: c for c in d["customers"]}
snapshot["billable"] = set(d["billable_status_codes"])
time.sleep(30)
def report_loop():
while True:
time.sleep(15)
with lock:
items = [{"key_id": k, "requests": n, "period_start": h}
for (k, h), n in usage.items() if n > 0]
pending = dict(usage); usage.clear()
if items:
r = requests.post(f"{BASE}/sdk/v1/usage", headers=HEADERS,
json={"items": items}, timeout=5)
if not r.ok: # put the counts back and retry next tick
with lock:
for kh, n in pending.items():
usage[kh] = usage.get(kh, 0) + n
def authenticate(api_key: str):
h = hashlib.sha256(api_key.encode()).hexdigest()
with lock:
key = snapshot["keys"].get(h)
cust = snapshot["customers"].get(key["customer_id"]) if key else None
if not key or not cust:
return None # -> 401 (add the verify-endpoint fallback here)
if cust["status"] != "active":
return "suspended" # -> 403
return key, cust
def record(key_id: str, status: int):
if status in snapshot["billable"]:
hour = time.strftime("%Y-%m-%dT%H:00:00Z", time.gmtime())
with lock:
usage[(key_id, hour)] = usage.get((key_id, hour), 0) + 1(Production code should add the verify fallback, negative caching, RPS limiting and graceful-shutdown flushing — exactly what the FastAPI SDK does for you.)
See also
- External credits & billing — granting credits from your own billing system.
- The API reference — full request/response schemas for the SDK endpoints under the SDK tag.