Rate Limiting & Quotas

Per-Minute Rate Limiting

Each plan has a per-minute request limit. Exceeding this limit returns HTTP 429.

Quota System

API usage is measured in blocks. Each request deducts blocks from your monthly block budget based on the block range requested.

Cost formula: max(100, round(block_range × network_discount × aggregate_discount))

  • block_range: block_end - block_start, or estimated from time range using blocks-per-day
  • network_discount: 1.0 (default), 0.2 (ARB)
  • aggregate_discount: 0.5 for /aggregate endpoints, 1.0 otherwise
  • Minimum cost: 100 blocks per request

Calculate Cost Before Querying

Use the POST /v1/calculate-cost endpoint to preview how many blocks a query will cost without deducting from your quota. This lets you validate block ranges and plan usage before committing.

curl -X POST -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" 
  -d '{"query": "/v1/erc20/events/transfer?network=ETH&block_start=24000000&block_end=24010000&token=USDT"}' 
  "https://api.defistream.dev/v1/calculate-cost"

Response:

{
  "query": "/v1/erc20/events/transfer?network=ETH&block_start=24000000&block_end=24010000&token=USDT",
  "cost": 10000,
  "quota_remaining": 500000,
  "quota_remaining_after": 490000
}

Response Headers

Every response includes these headers:

HeaderDescription
X-RateLimit-LimitRequests allowed per minute
X-RateLimit-RemainingRemaining monthly block budget
X-Request-CostBlocks deducted for this request
X-RateLimit-ResetSeconds until rate limit window resets
Retry-AfterSeconds to wait (only on 429 responses)

Handling Rate Limits

When you receive a 429 response:

  1. Read the Retry-After header
  2. Wait that many seconds before retrying
  3. Implement exponential backoff for repeated failures
import time
import requests

def fetch_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            time.sleep(retry_after)
            continue
        return response
    raise Exception("Max retries exceeded")