For the complete documentation index, see llms.txt. This page is also available as Markdown.

36 - Rate Limiting Guide

Overview

OpenAlgo uses Flask-Limiter with a moving-window strategy to protect endpoints from abuse. Different rate limits apply to different endpoint categories based on their sensitivity and resource usage.

Two properties of the current setup matter before reading the numbers below. limiter.py passes no default_limits, so an endpoint without an explicit @limiter.limit(...) decorator is unlimited. And storage_uri is memory://, so counters live in the worker process; a multi-worker deployment enforces each limit once per worker rather than once per install. There are no limiter.exempt registrations anywhere in the codebase, and limiter.init_app(app) runs unconditionally in create_app().

Architecture Diagram

┌───────────────────────────────────────────────────────────────────────────────┐
│                          Rate Limiting Architecture                           │
└───────────────────────────────────────────────────────────────────────────────┘

                           Incoming Request


┌───────────────────────────────────────────────────────────────────────────────┐
│                                 Flask-Limiter                                 │
│                                                                               │
│  ┌─────────────────────────────────────────────────────────────────────────┐  │
│  │                      Configuration                                       │ │
│  │  key_func = get_remote_address   (Rate limit by IP)                     │  │
│  │  storage_uri = "memory://"       (In-memory storage)                    │  │
│  │  strategy = "moving-window"      (Sliding window algorithm)             │  │
│  └─────────────────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────────────────┘


┌───────────────────────────────────────────────────────────────────────────────┐
│                          Endpoint Category Detection                          │
│                                                                               │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐              │
│  │   Login     │ │   API       │ │   Order     │ │  Webhook    │              │
│  │ Endpoints   │ │ Endpoints   │ │ Endpoints   │ │ Endpoints   │              │
│  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘              │
│         │               │               │               │                     │
│         ▼               ▼               ▼               ▼                     │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐              │
│  │ 5/min       │ │ 50/sec      │ │ 10/sec      │ │ 100/min     │              │
│  │ 25/hour     │ │             │ │             │ │             │              │
│  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘              │
└───────────────────────────────────────────────────────────────────────────────┘

                    ┌─────────────┴─────────────┐
                    │                           │
              Under Limit                  Over Limit
                    │                           │
                    ▼                           ▼
           ┌───────────────┐          ┌───────────────┐
           │   Process     │          │   429 Error   │
           │   Request     │          │ Too Many Reqs │
           └───────────────┘          └───────────────┘

Rate Limit Categories

Environment Variables

These are the eight keys .sample.env ships and utils/env_check.py validates:

RESET_RATE_LIMIT is read by blueprints/auth.py but is not in the rate_limit_vars list that utils/env_check.py validates, so a malformed value there is not caught at startup.

Limit Breakdown

Category
Rate Limit
Endpoints
Purpose

Login

5/min, 25/hr

/auth/login, /<broker>/callback

Prevent brute force

Password reset

15/hr

/auth/reset-password

Prevent reset abuse

API

50/sec

/api/v1/quotes, /api/v1/positionbook, etc.

General data access

Order

10/sec

/api/v1/placeorder, /api/v1/modifyorder, /api/v1/cancelorder

Trading rate control

Smart Order

10/sec

/api/v1/placesmartorder

Automated order rate control

Webhook

100/min

/chartink/webhook, /strategy/webhook

External integrations

Strategy

200/min

Strategy CRUD views in blueprints/strategy.py and blueprints/chartink.py

Strategy execution

Code Defaults Differ From The Sample Values

The number that applies when a key is absent from .env is the second argument to os.getenv in the module, not the value in .sample.env. For API_RATE_LIMIT the two disagree:

Default in code
Modules

50 per second

blueprints/admin.py, blueprints/orders.py, blueprints/sandbox.py, restx_api/margin.py

10 per second

the other 32 restx_api/*.py modules, including quotes.py, orderbook.py, holdings.py, funds.py, depth.py, history.py and cancel_all_order.py

Because .sample.env sets API_RATE_LIMIT="50 per second", a standard install gets 50/sec everywhere. Removing the key from .env silently drops most data endpoints to 10/sec. Keep the key present.

Additional Rate Limit Variables

These are read by code but are not part of the validated set. Several are absent from .sample.env altogether.

Variable
Default in code
Applied by

SIP_API_RATE_LIMIT

10 per minute

restx_api/sip.py backtest POST

PORTFOLIO_API_RATE_LIMIT

10 per minute

restx_api/portfolio.py backtest POST

PORTFOLIO_TEARSHEET_RATE_LIMIT

5 per minute

restx_api/portfolio.py tearsheet

GREEKS_RATE_LIMIT

30 per minute

restx_api/option_greeks.py

TELEGRAM_RATE_LIMIT

30 per minute

restx_api/telegram_bot.py

WHATSAPP_RATE_LIMIT

30 per minute

restx_api/whatsapp_bot.py

TELEGRAM_MESSAGE_RATE_LIMIT

10 per minute

blueprints/telegram.py

WHATSAPP_MESSAGE_RATE_LIMIT

10 per minute

blueprints/whatsapp.py

MCP_RATE_LIMIT_READ

60 per minute

blueprints/mcp_http.py per-token scope quota

MCP_RATE_LIMIT_WRITE

50 per minute

blueprints/mcp_http.py per-token scope quota

The MCP HTTP surface also carries two limits that are not environment-configurable: _DISPATCH_RATE_LIMIT = "120 per minute" on /mcp and _SSE_RATE_LIMIT = "5 per minute" on the SSE endpoint, both keyed by token rather than by remote address. None of these apply unless MCP_HTTP_ENABLED is True, since the blueprints are only registered in that case.

Implementation

Limiter Initialization

Location: limiter.py

Applying Rate Limits

Login Endpoint Example:

Order Endpoint Example:

API Endpoint Example:

Each module defines its own constant at import time. There is no shared constant, so the effective limit for a route is whatever that one module read from the environment when it was imported. Changing a rate limit therefore requires a restart, not just an .env edit.

Rate Limit Format

Flask-Limiter also accepts compound limits joined by semicolons, and utils/env_check.py validates that form:

Valid Timeunits

Timeunit
Alias

second

s

minute

m

hour

h

day

d

Examples

Error Handling

429 Response Handler

Location: app.py

Client-Side Handling

Endpoint Limits Map

REST API Endpoints

Endpoint
Rate Limit Variable
Default

/api/v1/placeorder

ORDER_RATE_LIMIT

10/sec

/api/v1/modifyorder

ORDER_RATE_LIMIT

10/sec

/api/v1/cancelorder

ORDER_RATE_LIMIT

10/sec

/api/v1/cancelallorder

API_RATE_LIMIT

50/sec (10/sec if the key is unset)

/api/v1/placesmartorder

SMART_ORDER_RATE_LIMIT

10/sec

/api/v1/quotes

API_RATE_LIMIT

50/sec

/api/v1/multiquotes

API_RATE_LIMIT

50/sec

/api/v1/positionbook

API_RATE_LIMIT

50/sec

/api/v1/orderbook

API_RATE_LIMIT

50/sec

/api/v1/tradebook

API_RATE_LIMIT

50/sec

/api/v1/holdings

API_RATE_LIMIT

50/sec

/api/v1/funds

API_RATE_LIMIT

50/sec

/api/v1/history

API_RATE_LIMIT

50/sec

/api/v1/depth

API_RATE_LIMIT

50/sec

/api/v1/ping

API_RATE_LIMIT

50/sec

/api/v1/intervals

API_RATE_LIMIT

50/sec

/api/v1/optionsmultiorder

ORDER_RATE_LIMIT

10/sec

Authentication Endpoints

Endpoint
Rate Limit Variable
Default

/auth/login

LOGIN_RATE_LIMIT_MIN + HOUR

5/min, 25/hr

/auth/reset-password

RESET_RATE_LIMIT

15/hr

/<broker>/callback

LOGIN_RATE_LIMIT_MIN + HOUR

5/min, 25/hr

blueprints/brlogin.py resolves its two constants through get_login_rate_limit_min() and get_login_rate_limit_hour() in utils/config.py, while blueprints/auth.py reads the same environment variables directly. The defaults match, so behaviour is identical, but the two paths are duplicated rather than sharing one source.

Webhook Endpoints

Endpoint
Rate Limit Variable
Default

/chartink/webhook

WEBHOOK_RATE_LIMIT

100/min

/strategy/webhook

WEBHOOK_RATE_LIMIT

100/min

STRATEGY_RATE_LIMIT is applied to the strategy management views in blueprints/strategy.py and blueprints/chartink.py, not to the webhook receivers. The Flow webhook receivers /flow/webhook/<token> and /flow/webhook/<token>/<symbol> carry no @limiter.limit decorator at all; they are CSRF-exempt and unlimited, so front them with a reverse proxy limit if they are internet-facing.

Moving Window Strategy

Algorithm Benefits

Aspect
Moving Window
Fixed Window

Accuracy

Higher

Lower

Burst protection

Better

Prone to bursts at boundaries

Memory

Slightly higher

Lower

Implementation

More complex

Simpler

Configuration Validation

Location: utils/env_check.py

Validation is fail-fast: an unset or malformed value in that list calls sys.exit(1) before the Flask app is built. RESET_RATE_LIMIT and every variable in the additional table above are outside this check.

Tuning Recommendations

For High-Frequency Trading

For Webhook-Heavy Usage

For Multi-User Deployments

Consider using Redis for distributed rate limiting:

Key Files Reference

File
Purpose

limiter.py

Flask-Limiter construction

app.py

limiter.init_app(app) and the 429 error handler

utils/env_check.py

Rate limit format validation at startup

utils/config.py

get_login_rate_limit_min(), get_login_rate_limit_hour()

restx_api/*.py

API endpoint rate limits

blueprints/auth.py

Login and password-reset rate limits

blueprints/brlogin.py

Broker callback rate limits

blueprints/chartink.py

Chartink webhook and strategy rate limits

blueprints/strategy.py

Strategy webhook and strategy rate limits

blueprints/mcp_http.py

Remote MCP dispatch, SSE and per-scope quotas

Last updated