Skip to content

Rate Limits

Understand API rate limits, quota management, and best practices for optimizing your API usage across different subscription tiers.

Overview

The arrow Trading API implements rate limiting to ensure fair usage, maintain system stability, and provide consistent performance for all users. Rate limits vary by subscription tier and are applied on a per-endpoint basis with additional burst capacity for temporary traffic spikes.

Rate Limit Tiers

Basic Tier

The Basic tier is ideal for individual traders and small-scale applications with moderate API usage.

Endpoint Category Requests per Minute Burst Limit Daily Quota
Orders 30 5 10,000
Positions 60 10 20,000
Holdings 60 10 20,000
Funds 60 10 20,000
Margin Calculation 60 10 20,000
Historical Data 30 5 5,000
Realtime Market Data 120 20 50,000
Realtime Order Data 120 20 50,000

Basic Tier Features

  • Monthly Cost: Free or Low-cost entry tier
  • Concurrent Connections: Up to 2
  • WebSocket Connections: 1 active connection
  • Best For: Individual traders, hobby projects, testing

Premium Tier

The Premium tier is designed for active traders, professional applications, and growing businesses requiring higher throughput.

Endpoint Category Requests per Minute Burst Limit Daily Quota
Orders 150 25 100,000
Positions 300 50 200,000
Holdings 300 50 200,000
Funds 300 50 200,000
Margin Calculation 300 50 200,000
Historical Data 150 25 50,000
Realtime Market Data 600 100 500,000
Realtime Order Data 600 100 500,000

Premium Tier Features

  • Monthly Cost: Mid-tier pricing
  • Concurrent Connections: Up to 10
  • WebSocket Connections: 3 active connections
  • Priority Support: Email support with 24-hour response time
  • Best For: Active traders, algorithmic trading, small trading firms

Enterprise Tier

The Enterprise tier offers the highest limits for institutional traders, brokerages, and large-scale applications requiring maximum performance.

Endpoint Category Requests per Minute Burst Limit Daily Quota
Orders 500 100 1,000,000
Positions 1000 100 2,000,000
Holdings 1000 100 2,000,000
Funds 1000 100 2,000,000
Margin Calculation 1000 100 2,000,000
Historical Data 500 100 500,000
Realtime Market Data 2000 200 Unlimited
Realtime Order Data 2000 200 Unlimited

Enterprise Tier Features

  • Monthly Cost: Custom pricing
  • Concurrent Connections: Unlimited
  • WebSocket Connections: 10+ active connections
  • Dedicated Support: Phone and email support with 2-hour response time
  • Custom Solutions: Dedicated infrastructure, custom rate limits available
  • Best For: Institutional traders, brokerages, fintech platforms, high-frequency trading

Rate Limit Details

Understanding Rate Limits

Rate limits are applied using a sliding window algorithm that tracks requests over a rolling time period.

Requests per Minute

The primary rate limit specifies the maximum number of requests allowed within any 60-second window.

Example: With a 60 requests/minute limit: - You can make 60 requests at 10:00:00 - At 10:00:30, you can make additional requests if fewer than 60 were made in the previous 60 seconds - The limit resets continuously as the time window slides forward

Burst Limit

Burst limits allow temporary spikes in traffic beyond the per-minute rate, useful for handling sudden data requirements.

Example: With a 60 requests/minute limit and 10 burst capacity: - You can make up to 10 requests simultaneously - But still cannot exceed 60 requests total within any 60-second window - Burst capacity is ideal for initial data loads or periodic batch operations

Daily Quota

Daily quotas reset at 00:00 IST (Indian Standard Time) and provide an upper bound on total daily usage.

Quota Management

Monitor your daily quota usage carefully. Once exhausted, API requests will be rejected until the quota resets at midnight IST.


Rate Limit Headers

Every API response includes headers indicating your current rate limit status:

HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1678901234
X-RateLimit-Burst-Limit: 10
X-RateLimit-Burst-Remaining: 8
X-DailyQuota-Limit: 20000
X-DailyQuota-Remaining: 15847
X-DailyQuota-Reset: 1678924800

Response Headers Explained

Header Description
X-RateLimit-Limit Maximum requests allowed per minute
X-RateLimit-Remaining Remaining requests in current window
X-RateLimit-Reset Unix timestamp when the rate limit resets
X-RateLimit-Burst-Limit Maximum burst capacity
X-RateLimit-Burst-Remaining Remaining burst capacity
X-DailyQuota-Limit Maximum daily requests allowed
X-DailyQuota-Remaining Remaining requests in daily quota
X-DailyQuota-Reset Unix timestamp when daily quota resets (midnight IST)

Rate Limit Exceeded Response

When you exceed rate limits, the API returns a 429 Too Many Requests status code:

{
  "status": "error",
  "message": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "details": {
    "limit": 60,
    "remaining": 0,
    "resetAt": "2024-03-15T10:15:30+0530",
    "retryAfter": 45
  }
}

Error Response Fields

Field Description
status Always "error" for rate limit errors
message Human-readable error description
code Error code (RATE_LIMIT_EXCEEDED or DAILY_QUOTA_EXCEEDED)
details.limit The rate limit that was exceeded
details.remaining Requests remaining (0 when exceeded)
details.resetAt Timestamp when the limit resets
details.retryAfter Seconds to wait before retrying

Best Practices

1. Monitor Rate Limit Headers

Always check rate limit headers in responses to track your usage and avoid hitting limits:

    const response = await fetch('https://api.arrow.trading/user/holdings', {
      headers: {
        'appID': APP_ID,
        'token': TOKEN
      }
    });

    // Check rate limit headers
    const remaining = response.headers.get('X-RateLimit-Remaining');
    const resetTime = response.headers.get('X-RateLimit-Reset');

    console.log(`Requests remaining: ${remaining}`);
    console.log(`Resets at: ${new Date(resetTime * 1000)}`);

    if (remaining < 10) {
      console.warn('Approaching rate limit!');
    }
    response = requests.get(
        'https://api.arrow.trading/user/holdings',
        headers={'appID': APP_ID, 'token': TOKEN}
    )

    # Check rate limit headers
    remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
    reset_time = int(response.headers.get('X-RateLimit-Reset', 0))

    print(f'Requests remaining: {remaining}')
    print(f'Resets at: {datetime.fromtimestamp(reset_time)}')

    if remaining < 10:
        print('Warning: Approaching rate limit!')

2. Implement Exponential Backoff

When you receive a 429 error, implement exponential backoff with jitter:

    async function fetchWithRetry(url, options, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        const response = await fetch(url, options);

        if (response.status === 429) {
          const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
          const jitter = Math.random() * 1000;
          const delay = (retryAfter * 1000) + jitter;

          console.log(`Rate limited. Retrying after ${delay}ms`);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }

        return response;
      }

      throw new Error('Max retries exceeded');
    }
    import time
    import random

    def fetch_with_retry(url, headers, max_retries=3):
        for i in range(max_retries):
            response = requests.get(url, headers=headers)

            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 2 ** i))
                jitter = random.uniform(0, 1)
                delay = retry_after + jitter

                print(f'Rate limited. Retrying after {delay}s')
                time.sleep(delay)
                continue

            return response

        raise Exception('Max retries exceeded')

3. Cache Responses

Cache data that doesn't change frequently to reduce API calls:

// Simple in-memory cache with TTL
class APICache {
  constructor(ttl = 60000) { // 60 seconds default
    this.cache = new Map();
    this.ttl = ttl;
  }

  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;

    if (Date.now() > item.expiry) {
      this.cache.delete(key);
      return null;
    }

    return item.data;
  }

  set(key, data) {
    this.cache.set(key, {
      data,
      expiry: Date.now() + this.ttl
    });
  }
}

// Usage
const cache = new APICache(60000); // Cache for 60 seconds

async function getHoldings() {
  const cached = cache.get('holdings');
  if (cached) return cached;

  const response = await fetch('https://api.arrow.trading/user/holdings', {
    headers: { 'appID': APP_ID, 'token': TOKEN }
  });

  const holdings = await response.json();
  cache.set('holdings', holdings);
  return holdings;
}

4. Batch Requests When Possible

Use batch endpoints to reduce the number of API calls:

// Instead of individual margin calls
const margins = [];
for (const order of orders) {
  const margin = await calculateOrderMargin(order); // Multiple API calls
  margins.push(margin);
}

// Use basket margin endpoint
const basketMargin = await calculateBasketMargin(orders); // Single API call

5. Use WebSockets for Real-time Data

For real-time market data and order updates, use WebSocket connections instead of polling:

// ❌ Avoid polling (wastes rate limits)
setInterval(async () => {
  const positions = await fetchPositions();
  updateUI(positions);
}, 1000);

// ✅ Use WebSocket for real-time updates
const ws = new WebSocket('wss://api.arrow.trading/stream');
ws.onmessage = (event) => {
  const update = JSON.parse(event.data);
  updateUI(update);
};

6. Implement Request Queuing

Queue requests to stay within rate limits:

class RateLimiter {
  constructor(requestsPerMinute) {
    this.requestsPerMinute = requestsPerMinute;
    this.queue = [];
    this.timestamps = [];
  }

  async execute(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.queue.length === 0) return;

    // Remove timestamps older than 1 minute
    const now = Date.now();
    this.timestamps = this.timestamps.filter(t => now - t < 60000);

    // Check if we can make a request
    if (this.timestamps.length < this.requestsPerMinute) {
      const { fn, resolve, reject } = this.queue.shift();
      this.timestamps.push(now);

      try {
        const result = await fn();
        resolve(result);
      } catch (error) {
        reject(error);
      }

      // Process next item
      setTimeout(() => this.processQueue(), 0);
    } else {
      // Wait until we can make the next request
      const oldestTimestamp = this.timestamps[0];
      const delay = 60000 - (now - oldestTimestamp);
      setTimeout(() => this.processQueue(), delay);
    }
  }
}

// Usage
const limiter = new RateLimiter(60);

async function safeFetch(url, options) {
  return limiter.execute(() => fetch(url, options));
}


Upgrading Your Tier

When to Upgrade

Consider upgrading to a higher tier when you:

  • Consistently hit rate limits during trading hours
  • Need higher burst capacity for algorithmic strategies
  • Require more daily quota for historical data analysis
  • Need priority support for production applications
  • Want to scale your trading operations

How to Upgrade

Tier Upgrades

To upgrade your subscription tier:

  1. Visit the arrow Trading Dashboard
  2. Navigate to Account Settings > Subscription
  3. Select your desired tier
  4. Complete the payment process
  5. Your new limits are applied immediately

Custom Enterprise Plans

For custom requirements beyond Enterprise tier limits, contact our sales team:

  • Email: enterprise@arrow.trading
  • Phone: +91-XXXX-XXXXXX
  • Custom Features: Dedicated infrastructure, SLA guarantees, custom endpoints

Rate Limit FAQs

Do rate limits apply per API key or per account?

Rate limits are applied per API key (appID + token combination). If you have multiple API keys, each has its own independent rate limit.

Are WebSocket connections counted against rate limits?

WebSocket connections have separate connection limits but do not count against REST API rate limits. However, initial connection requests are counted.

What happens if I exceed my daily quota?

Once your daily quota is exhausted, all API requests will return 429 errors with code DAILY_QUOTA_EXCEEDED until midnight IST when the quota resets.

Can I request a temporary rate limit increase?

Enterprise customers can request temporary rate limit increases for specific events (e.g., earnings season, IPOs). Contact support at least 48 hours in advance.

Do failed requests count against rate limits?

Yes, all requests count against rate limits regardless of success or failure, including requests that return 4xx or 5xx errors.

Are there separate limits for market hours vs. non-market hours?

No, rate limits are consistent throughout the day. However, market data endpoints may have reduced data availability outside trading hours.


Monitoring Your Usage

Usage Dashboard

Track your API usage in real-time through the arrow Trading Dashboard:

  • View current rate limit consumption
  • Monitor daily quota usage
  • Analyze usage patterns by endpoint
  • Set up alerts for approaching limits
  • Download usage reports

Usage Alerts

Configure alerts to notify you when:

  • Rate limit consumption exceeds 80%
  • Daily quota usage exceeds 80%
  • Multiple 429 errors occur
  • Unusual usage patterns detected

Alert Configuration

Set up usage alerts in Dashboard > Settings > API Alerts


Contact Support

If you have questions about rate limits or need assistance:

  • Basic Tier: Email support@arrow.trading
  • Premium Tier: Email premium@arrow.trading (24-hour response)
  • Enterprise Tier: Call dedicated support line or email enterprise@arrow.trading (2-hour response)

For urgent production issues, Enterprise customers can contact the 24/7 emergency support line.