Rate Limiting & Subscriptions
Rate Limiting & Subscriptions¶
This document covers usage tracking, rate limiting, subscription tiers, and billing integration for the Huitzo platform.
Overview¶
Huitzo tracks resource usage to ensure fair access and enable subscription-based billing:
┌─────────────────────────────────────────────────────────────────┐
│ Usage Tracking Layer │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Commands │ │ Storage │ │ LLM Tokens │ │
│ │ Executed │ │ Used │ │ Consumed │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │
│ │ │ │ │
│ └────────────────┼──────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Redis Usage Counters ││
│ │ • Per-tenant counters with monthly rollover ││
│ │ • Real-time aggregation for dashboard display ││
│ └─────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Subscription Enforcement ││
│ │ • SaaS: Stripe-based limits ││
│ │ • Self-hosted: Unlimited (customer manages infra) ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
Metrics Tracked¶
Command Execution¶
| Metric | Description | Tracked Per |
|---|---|---|
commands_executed |
Total commands run | Tenant, User, Pack |
commands_succeeded |
Successfully completed | Tenant, User, Pack |
commands_failed |
Failed executions | Tenant, User, Pack |
execution_time_seconds |
Total execution time | Tenant, User, Pack |
Storage Usage¶
| Metric | Description | Tracked Per |
|---|---|---|
storage_bytes |
Current storage used | Tenant, User, Pack |
storage_operations |
Read/write operations | Tenant, User |
storage_keys |
Number of stored keys | Tenant, User, Pack |
LLM Token Consumption¶
| Metric | Description | Tracked Per |
|---|---|---|
llm_input_tokens |
Tokens in prompts | Tenant, User, Pack, Model |
llm_output_tokens |
Tokens in responses | Tenant, User, Pack, Model |
llm_requests |
Total LLM API calls | Tenant, User, Pack, Model |
Concurrent Jobs¶
| Metric | Description | Tracked Per |
|---|---|---|
concurrent_jobs |
Currently running commands | Tenant, User |
peak_concurrent_jobs |
Max concurrent in period | Tenant |
Note: In v2, concurrent jobs are tracked for observability but not enforced. Future versions may enforce limits at subscription tier level.
Tracking Infrastructure¶
Redis Counter Structure¶
Usage counters are stored in Redis with automatic monthly rollover:
# Key pattern: huitzo:usage:{tenant_id}:{metric}:{period}
# Examples:
huitzo:usage:org_123:commands_executed:2026-01
huitzo:usage:org_123:llm_tokens:2026-01
huitzo:usage:org_123:storage_bytes:current
Counter Operations¶
# Internal SDK implementation (for reference)
async def increment_usage(tenant_id: str, metric: str, amount: int = 1):
"""Increment a usage counter."""
period = datetime.now().strftime("%Y-%m")
key = f"huitzo:usage:{tenant_id}:{metric}:{period}"
await redis.incrby(key, amount)
async def get_usage(tenant_id: str, metric: str, period: str = None) -> int:
"""Get current usage for a metric."""
period = period or datetime.now().strftime("%Y-%m")
key = f"huitzo:usage:{tenant_id}:{metric}:{period}"
return int(await redis.get(key) or 0)
Real-time Aggregation¶
The platform aggregates usage in real-time for dashboard display:
# Usage summary returned by GET /api/v1/usage
{
"period": "2026-01",
"tenant_id": "org_123",
"usage": {
"commands_executed": 1523,
"storage_bytes": 52428800, # 50 MB
"llm_tokens": {
"input": 125000,
"output": 87500,
"total": 212500
},
"concurrent_jobs": {
"current": 3,
"peak": 12
}
},
"limits": {
"commands_per_month": null, # null = unlimited for this tier
"storage_bytes": 1073741824, # 1 GB
"llm_tokens_per_month": 1000000,
"concurrent_jobs": 10 # tracked, not enforced in v2
},
"percentage_used": {
"storage": 4.88,
"llm_tokens": 21.25
}
}
Subscription Tiers¶
SaaS Pricing (Initial Launch)¶
Huitzo launches with a single subscription tier:
| Tier | Price | Included |
|---|---|---|
| Pro | $50/month | Commands: Unlimited, Storage: 10 GB, LLM Tokens: 1M/month |
Tier Limits¶
# Subscription tier configuration
SUBSCRIPTION_TIERS = {
"pro": {
"name": "Pro",
"price_monthly": 50,
"limits": {
"commands_per_month": None, # Unlimited
"storage_bytes": 10 * 1024**3, # 10 GB
"llm_tokens_per_month": 1_000_000, # 1M tokens
"concurrent_jobs": 10, # Tracked, not enforced
"packs_installed": None, # Unlimited
},
"features": {
"api_access": True,
"custom_domains": False,
"priority_support": False,
}
}
}
Future Tiers (Roadmap)¶
| Tier | Price | Commands | Storage | LLM Tokens |
|---|---|---|---|---|
| Free | $0 | 100/month | 100 MB | 10K/month |
| Pro | $50/month | Unlimited | 10 GB | 1M/month |
| Team | $200/month | Unlimited | 100 GB | 5M/month |
| Enterprise | Custom | Unlimited | Custom | Custom |
Bring Your Own Key (BYOK)¶
Users can provide their own API keys for LLM providers (e.g., OpenAI, Anthropic) to bypass Huitzo's monthly token limits.
How it Works¶
- Configuration: The user adds their API key as a User Secret (e.g.,
OPENAI_API_KEY) for a specific pack. - Precedence: The Huitzo SDK (
ctx.llm) automatically detects and uses the user-provided key instead of the platform's shared key. - Billing:
- Usage via BYO keys consumes 0 Huitzo tokens.
- The user is billed directly by the LLM provider (e.g., directly by OpenAI) for that usage.
- Huitzo platform rate limits (requests per minute) still apply to ensure stability, but monthly quotas do not.
This allows heavy users to exceed the standard subscription limits (1M/month on Pro) by paying providers directly for the excess usage.
Self-Hosted Mode¶
Self-hosted deployments have no usage limits. The customer manages their own infrastructure and is responsible for resource allocation.
# Self-hosted: All limits disabled
if settings.deployment_mode == "self_hosted":
limits = {
"commands_per_month": None,
"storage_bytes": None,
"llm_tokens_per_month": None,
"concurrent_jobs": None,
}
Self-hosted customers: - Pay a flat license fee (not usage-based) - Manage their own PostgreSQL/Redis capacity - Configure their own LLM API keys and budgets - Set internal limits via environment variables if desired
Stripe Integration (SaaS)¶
Architecture¶
Stripe is the source of truth for subscription status:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Customer │────▶│ Stripe │────▶│ Huitzo │
│ Portal │ │ Checkout │ │ Webhook │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ Database │
│ (tenant │
│ status) │
└─────────────┘
Webhook Events¶
Huitzo listens for these Stripe webhook events:
| Event | Action |
|---|---|
checkout.session.completed |
Activate subscription |
customer.subscription.updated |
Update tier/limits |
customer.subscription.deleted |
Downgrade to free/disable |
invoice.payment_failed |
Mark payment issue, notify |
invoice.paid |
Clear payment issue flag |
Subscription Status¶
class SubscriptionStatus(Enum):
ACTIVE = "active" # Paid and current
PAST_DUE = "past_due" # Payment failed, grace period
CANCELED = "canceled" # Canceled, access until period end
EXPIRED = "expired" # Access revoked
Grace Period¶
When payment fails:
1. Subscription marked past_due
2. 7-day grace period begins
3. Daily email reminders sent
4. After 7 days: subscription marked expired, access restricted
Rate Limiting¶
API Rate Limits¶
| Endpoint | Limit | Window |
|---|---|---|
/api/v1/commands/* |
100 req | 1 minute |
/api/v1/storage/* |
200 req | 1 minute |
/api/v1/* (other) |
1000 req | 1 minute |
Rate Limit Headers¶
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706012345
Rate Limit Response¶
HTTP/1.1 429 Too Many Requests
Retry-After: 30
{
"error": {
"type": "RateLimitError",
"message": "Rate limit exceeded. Retry after 30 seconds.",
"code": "RATE_LIMITED",
"retry_after": 30
}
}
Configuration¶
# Environment variables
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW=60 # seconds
RATE_LIMIT_STORAGE=redis # redis | memory
Usage Dashboard¶
API Endpoints¶
# Get current usage
GET /api/v1/usage
# Get usage history
GET /api/v1/usage/history?months=3
# Get usage breakdown by pack
GET /api/v1/usage/by-pack
# Get usage breakdown by user (admin only)
GET /api/v1/usage/by-user
Example Response¶
{
"current_period": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-31T23:59:59Z"
},
"usage": {
"commands": {
"executed": 1523,
"limit": null,
"by_pack": {
"analytics": 892,
"notifications": 631
}
},
"storage": {
"used_bytes": 52428800,
"limit_bytes": 10737418240,
"percent_used": 0.49
},
"llm_tokens": {
"used": 212500,
"limit": 1000000,
"percent_used": 21.25,
"by_model": {
"gpt-4o-mini": 180000,
"claude-3-haiku": 32500
}
}
},
"subscription": {
"tier": "pro",
"status": "active",
"current_period_end": "2026-02-01T00:00:00Z"
}
}
Overage Handling¶
Soft Limits (Default)¶
By default, usage continues past limits with warnings:
# Soft limit behavior
if usage > limit:
# Log warning
log.warning(f"Tenant {tenant_id} exceeded {metric} limit")
# Send notification (once per day)
await notify_overage(tenant_id, metric)
# Continue processing
Hard Limits (Enterprise Option)¶
Enterprise customers can enable hard limits:
# Hard limit behavior (if enabled)
if usage > limit and tenant.hard_limits_enabled:
raise UsageLimitExceeded(
metric=metric,
current=usage,
limit=limit,
message=f"Monthly {metric} limit exceeded"
)
Best Practices¶
For Pack Developers¶
- Batch operations when possible - Reduce command count
- Use appropriate LLM models - gpt-4o-mini costs less than gpt-4o
- Implement caching - Reduce redundant LLM calls
- Clean up old storage - Delete data you no longer need
For Platform Administrators¶
- Monitor usage trends - Use Prometheus metrics
- Set up alerts - Notify before limits are hit
- Review by-pack usage - Identify inefficient packs
- Plan capacity - Scale infrastructure with growth
Related Documentation¶
- Configuration Reference – Environment variables
- Observability Guide – Monitoring setup
- Architecture Overview – System design