Who this is for: Developers tuning SDK flush intervals, DevOps engineers planning capacity, and architects designing high-throughput ingestion pipelines.
1. Overview
The SWT3 API uses a token bucket rate limiter. Each tenant has a bucket that refills at a fixed rate (determined by subscription tier). Requests consume tokens. When the bucket is empty, requests receive HTTP 429 until tokens refill.
Key properties:
- Per-tenant: Your rate limit is independent of other tenants.
- Per-instance: Limits apply per application server instance. With 2 instances, effective throughput is approximately double.
- Continuous refill: Tokens refill continuously, not in fixed windows. You can burst up to your full rate for one second, then sustain at the configured rate.
- Stale cleanup: Inactive tenant buckets (no requests for 5 minutes) are automatically cleaned up to free memory.
2. Tier Limits
| Tier | Rate (req/s/instance) | Effective (2 instances) | Monthly | Price |
|---|---|---|---|---|
| OPEN | 10 | ~20 | 100 anchors | Free |
| PRO | 50 | ~100 | Unlimited | $499/mo |
| ENCLAVE | 200 | ~400 | Unlimited | $9,500/mo |
| SOVEREIGN | 500 | ~1,000 | Unlimited | Contact us |
The tier is resolved from your API key's tenant record. Tier lookups are cached for 60 seconds to avoid database overhead on every request.
3. Monthly Anchor Quota
OPEN tier tenants are limited to 100 anchors per calendar month. The counter resets on the 1st of each month at 00:00 UTC.
- When you hit the limit, witness requests return
429with the message"Monthly anchor limit reached (100)". - The monthly count is cached for 5 minutes to reduce database load. This means a brief window where counts may be slightly stale.
- Pro, Enclave, and Sovereign tiers have no monthly limit.
4. Public Endpoint Limits
Public endpoints (no authentication required) are rate-limited per IP address:
| Endpoint | Limit | Window |
|---|---|---|
GET /api/v1/verify/public | 60 requests | Per minute |
POST /api/v1/passport/verify | 10 requests | Per minute |
GET /api/v1/passport/status/:id | 60 requests | Per minute |
These limits are generous for typical verification workloads. If you need bulk verification, use the tenant-scoped /api/v1/attest/verify?enclave=true endpoint which verifies all anchors in a single request.
5. How Token Bucket Works
The token bucket algorithm works like this:
- Your bucket starts full (capacity = your tier's rate, e.g., 50 for PRO).
- Each request costs 1 token (batch requests cost 1 token regardless of batch size).
- Tokens refill continuously at your tier's rate (e.g., 50 tokens per second for PRO).
- If the bucket has enough tokens, the request proceeds. If not, it returns 429.
- The
Retry-Afterheader tells you how long until enough tokens refill.
Burst capacity: Your bucket capacity equals your rate. A PRO tenant can send 50 requests in an instant burst, then must wait for refill. This is ideal for SDK flush cycles that send buffered anchors in a batch.
6. Handling 429 Responses
When rate limited, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 2
{ "error": "Rate limit exceeded. Retry after 2s.", "retry_after_seconds": 2 }
Implement exponential backoff with the Retry-After value as the base:
import time, requests
def witness_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
"https://sovereign.tenova.io/api/v1/witness",
json=payload,
headers={"Authorization": f"Bearer {API_KEY}"}
)
if resp.status_code != 429:
return resp
retry_after = int(resp.headers.get("Retry-After", 1))
wait = retry_after * (2 ** attempt) # exponential backoff
time.sleep(min(wait, 30)) # cap at 30 seconds
return resp # final attempt result
async function witnessWithRetry(payload: object, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const resp = await fetch("https://sovereign.tenova.io/api/v1/witness", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (resp.status !== 429) return resp;
const retryAfter = parseInt(resp.headers.get("Retry-After") ?? "1");
const wait = Math.min(retryAfter * Math.pow(2, attempt), 30);
await new Promise(r => setTimeout(r, wait * 1000));
}
}
7. Optimization Strategies
Use the Batch Endpoint
POST /api/v1/witness/batch accepts up to 500 payloads per request and counts as a single request against your rate limit. This is the highest-leverage optimization.
- OPEN tier: 10 batch requests/second = 5,000 anchors/second (theoretical)
- PRO tier: 50 batch requests/second = 25,000 anchors/second (theoretical)
Increase SDK Flush Interval
The SDK buffers anchors and sends them in batches. A larger flush_interval means more anchors per batch, fewer HTTP requests:
# Default: flush every 30 seconds witness = Witness(flush_interval=30) # High-throughput: flush every 60 seconds (more anchors per batch) witness = Witness(flush_interval=60)
Distribute Across Keys
Rate limits are per-tenant (not per-key). Multiple API keys under the same tenant share the same bucket. To increase throughput, upgrade your tier.
8. Capacity Planning
| Tier | Max Anchors/Day | Typical Use Case |
|---|---|---|
| OPEN | ~100 (monthly limit) | Evaluation, proof of concept |
| PRO | ~4.3M | Single AI system, moderate volume |
| ENCLAVE | ~17.3M | Multiple AI systems, enterprise volume |
| SOVEREIGN | ~43.2M | Fleet-scale, real-time inference witnessing |
These are per-second rate limits extrapolated to 24 hours. Actual throughput depends on payload size, network latency, and batch utilization.
9. Monitoring
SDK-Level
- Monitor dead-letter queue size. A growing queue means the SDK cannot flush fast enough (rate limited or network issue).
- The Python SDK logs retry attempts at the
WARNINGlevel.
API-Level
- Count 429 responses in your logs. If the rate is consistent, consider upgrading your tier or increasing flush_interval.
- Monitor the health endpoint:
curl https://sovereign.tenova.io/api/v1/health. If health degrades, rate limits may tighten.
Dashboard-Level
- The AI Witness page shows anchor ingestion rates and model coverage.
- OPEN tier users see a retention countdown banner showing days until oldest anchor expires.
See also: API Reference -- Rate Limits | Error Codes Reference | Troubleshooting FAQ -- API