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

  1. Configuration: The user adds their API key as a User Secret (e.g., OPENAI_API_KEY) for a specific pack.
  2. Precedence: The Huitzo SDK (ctx.llm) automatically detects and uses the user-provided key instead of the platform's shared key.
  3. 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

  1. Batch operations when possible - Reduce command count
  2. Use appropriate LLM models - gpt-4o-mini costs less than gpt-4o
  3. Implement caching - Reduce redundant LLM calls
  4. Clean up old storage - Delete data you no longer need

For Platform Administrators

  1. Monitor usage trends - Use Prometheus metrics
  2. Set up alerts - Notify before limits are hit
  3. Review by-pack usage - Identify inefficient packs
  4. Plan capacity - Scale infrastructure with growth