Skip to content

Tiered Rate Limiting — Different Limits for Free, Pro, and Enterprise Plans

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Tiered Rate Limiting. We cover key concepts, practical examples, and best practices to help you master this topic.

Tiered rate limiting assigns different rate limits, quotas, and features to different subscription tiers, allowing API providers to monetize access by offering graduated service levels from free to enterprise.

What You'll Learn

  • How to design a multi-tier rate limiting Strategy
  • How to map API keys to tiers and enforce tier-specific limits
  • How to handle tier upgrades and downgrades gracefully

Why It Matters

Free tiers attract developers and build adoption. Pro tiers generate revenue. Enterprise tiers provide high-volume access for large customers. Tiered rate limiting enforces these boundaries, ensuring each customer gets the service level they pay for while incentivizing upgrades.

Real-World Use

DodaTech's API offers three tiers: Free (10 req/min, 1000 req/day), Pro ($49/mo, 100 req/min, 50000 req/day), and Enterprise (custom pricing, 1000 req/min, unlimited quota). Tiered rate limiting enforces these limits and returns appropriate headers showing the current tier.

flowchart LR
    A["API Request"] --> B{"Identify Tier"}
    B -->|"Free"| C["10 req/min\n1000 req/day"]
    B -->|"Pro"| D["100 req/min\n50000 req/day"]
    B -->|"Enterprise"| E["1000 req/min\nCustom quota"]
    C --> F{"Within\nlimits?"}
    D --> F
    E --> F
    F -->|Yes| G["Process"]
    F -->|No| H["429 + Upgrade\nsuggestion"]
    style C fill:#fef3c7,stroke:#d97706
    style D fill:#dbeafe,stroke:#2563eb
    style E fill:#bbf7d0,stroke:#16a34a
    style H fill:#fecaca,stroke:#dc2626

Tier Configuration

TIER_CONFIG = {
    "free": {
        "rate_limit": 10,
        "rate_window": 60,
        "daily_quota": 1000,
        "monthly_quota": 10000,
        "features": ["basic_search", "public_data"],
        "price_monthly": 0
    },
    "pro": {
        "rate_limit": 100,
        "rate_window": 60,
        "daily_quota": 50000,
        "monthly_quota": 500000,
        "features": ["advanced_search", "export", "webhooks"],
        "price_monthly": 49
    },
    "enterprise": {
        "rate_limit": 1000,
        "rate_window": 60,
        "daily_quota": 500000,
        "monthly_quota": None,  # Unlimited
        "features": ["all", "dedicated_support", "sla", "custom_limits"],
        "price_monthly": "custom"
    }
}

def get_tier_config(api_key):
    """Look up the tier for a given API key from the database"""
    # In production, query your database
    tier_map = {
        "key_free_abc": "free",
        "key_pro_def": "pro",
        "key_ent_ghi": "enterprise"
    }
    tier = tier_map.get(api_key, "free")
    return TIER_CONFIG[tier], tier

Tier Enforcement Middleware

from flask import Flask, request, jsonify, g
import redis
from functools import wraps

app = Flask(__name__)
r = redis.Redis(host='localhost', port=6379, db=0)

def enforce_tier_limits(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        api_key = request.headers.get('X-API-Key')
        if not api_key:
            return jsonify({"error": "missing_api_key"}), 401

        config, tier = get_tier_config(api_key)
        if not config:
            return jsonify({"error": "invalid_api_key"}), 403

        rate_key = f"rl:tier:{api_key}:{int(__import__('time').time() / config['rate_window'])}"
        current = r.get(rate_key)

        if current and int(current) >= config["rate_limit"]:
            upgrade_tiers = {"free": "pro", "pro": "enterprise"}
            suggestion = upgrade_tiers.get(tier)

            return jsonify({
                "error": "rate_limit_exceeded",
                "tier": tier,
                "limit": config["rate_limit"],
                "upgrade_suggestion": suggestion,
                "upgrade_url": f"https://doda.example.com/upgrade?to={suggestion}"
            }), 429

        r.incr(rate_key)
        r.expire(rate_key, config["rate_window"])

        g.tier = tier
        g.tier_config = config
        return f(*args, **kwargs)
    return decorated

Feature Gating by Tier

def requires_feature(feature_name):
    """Decorator that checks if the current tier has access to a feature"""
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            allowed_features = g.tier_config["features"]
            if "all" not in allowed_features and feature_name not in allowed_features:
                return jsonify({
                    "error": "feature_not_available",
                    "feature": feature_name,
                    "tier": g.tier,
                    "message": f"Upgrade to access {feature_name}",
                    "upgrade_url": f"https://doda.example.com/upgrade"
                }), 402
            return f(*args, **kwargs)
        return decorated
    return decorator

@app.route('/api/export')
@enforce_tier_limits
@requires_feature("export")
def export_data():
    return jsonify({"data": "exported data"})

Common Mistakes

1. Making Tiers Too Similar

If free and pro tiers have nearly identical limits, few users upgrade. Ensure meaningful differences in limits and features.

2. Not Communicating Upgrade Paths

Rate limit errors should include upgrade suggestions. A clear upgrade path converts frustrated users into paying customers.

3. Hardcoding Tier Configurations

Store tier configurations in a database or config file, not in code. This allows changing limits without deploying new code.

4. Ignoring Enterprise Custom Limits

Enterprise customers often need custom limits. Support per-customer overrides that bypass standard tier configurations.

5. Not Testing Tier Boundaries

Test that each tier's limits are enforced correctly. A bug that gives Pro users Free limits leads to support tickets and churn.

Practice Questions

  1. What tiers are common for API rate limiting?
  2. How do you map an API key to a tier?
  3. Why should rate limit errors suggest upgrades?
  4. How do you handle custom enterprise limits?
  5. What is feature gating by tier?

Answers

  1. Free, Pro, Enterprise (with graduated limits). 2. Look up the tier in a database by the API key. 3. To convert limited users into paying customers. 4. Support per-customer overrides in the tier configuration. 5. Restricting access to certain features based on the subscription tier.

Challenge

Build a tier management service that allows admin users to create custom tiers with arbitrary rate limits, quotas, and feature flags, assigns API keys to tiers, and provides an API for checking what tier a key belongs to.

FAQ

What is tiered rate limiting?

Rate limiting where different subscription tiers have different limits and features.

How many tiers should an API have?

Three is common: Free, Pro, Enterprise. Add more if your market segments justify it.

Can enterprise customers have custom limits?

Yes. Support per-customer limit overrides that bypass the standard tier configuration.

How do upgrades affect rate limits?

When a customer upgrades, their tier mapping changes and higher limits take effect immediately.

Should rate limit errors suggest upgrades?

Yes. Include an upgrade suggestion and link to help convert limited users.

Mini Project

Create a complete tiered rate limiting system with: a YAML-based tier configuration, a FastAPI middleware that enforces limits per tier, a Stripe integration for upgrades, Webhook handling for tier changes, and a customer dashboard showing current usage vs tier limits with upgrade prompts.

What's Next

  • Learn about rate limit headers for client-side consumption
  • Explore the retry-after header for client backoff
  • Continue to rate limit testing strategies with load testing tools

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro