Every request to the Predexy API is subject to rate limiting. Limits are applied per tier based on how you authenticate, and every response — including successful ones — includes headers that tell you exactly where you stand. Understanding these limits upfront lets you design integrations that stay well within bounds and recover gracefully when they don’t.
Rate limit tiers
Predexy enforces three distinct rate limit tiers. The tier that applies to your request depends on how you authenticate.
| Tier | Authentication | Requests per minute | Burst |
|---|
| Product read | Unauthenticated (product session) | 120 | 40 |
| Authenticated session | Session JWT (predex_siwe cookie or bearer) | 60 | 20 |
| API Key | X-API-Key: pdx_... header | 600 | 50 |
The burst value is the maximum number of requests you can send in a short burst before the per-minute window takes effect. API Key access provides the highest throughput — 600 requests per minute with a burst of 50 — making it the right choice for automated polling and data pipelines.
The Authenticated session tier (60 req/min) is lower than the Product read tier (120 req/min) because it is scoped to the Developer Console and operational routes. Use an API key for external integrations that need higher throughput.
Every API response includes the following headers regardless of status code. Read these headers to track your remaining capacity and plan retries precisely.
| Header | Type | Description |
|---|
X-RateLimit-Limit | integer | Maximum requests allowed in the current window |
X-RateLimit-Remaining | integer | Requests remaining in the current window |
X-RateLimit-Reset | integer (Unix timestamp) | When the current window resets and your limit replenishes |
The -i flag prints response headers alongside the body. Use it to inspect your current rate limit state:
curl -i -H "X-API-Key: pdx_your_key_here" \
"https://api.predexy.com/api/v1/external/arbitrage/opportunities"
You’ll see headers like:
HTTP/2 200
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 1714147260
X-RateLimit-Reset is a Unix timestamp. Subtract the current time from it to calculate how many seconds remain before your window resets.
When you exceed the limit
When your request rate exceeds the allowed limit, the API returns a 429 Too Many Requests status with the following response body:
{
"status": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again in 42 seconds."
}
The message field includes a human-readable countdown to help with debugging. The X-RateLimit-Reset header on the 429 response tells you the exact Unix timestamp when you can resume.
Do not retry immediately after a 429. Sending additional requests before the window resets does not reset the clock and will continue to return 429s.
Handling 429s in Python with exponential backoff
The safest approach is to check X-RateLimit-Remaining proactively and pause before you hit zero. When you do receive a 429, read X-RateLimit-Reset from the response headers and wait until that timestamp before retrying. The example below combines both strategies with exponential backoff as a fallback:
import time
import requests
API_KEY = "pdx_your_key_here"
BASE_URL = "https://api.predexy.com"
def fetch_opportunities(max_retries: int = 5) -> dict:
url = f"{BASE_URL}/api/v1/external/arbitrage/opportunities"
headers = {"X-API-Key": API_KEY}
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
# Proactive: pause if remaining capacity is low
remaining = int(response.headers.get("X-RateLimit-Remaining", 1))
if remaining < 10 and response.status_code == 200:
reset_at = int(response.headers.get("X-RateLimit-Reset", 0))
wait = max(0, reset_at - int(time.time()))
if wait > 0:
time.sleep(wait)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
reset_at = int(response.headers.get("X-RateLimit-Reset", 0))
wait = max(1, reset_at - int(time.time()))
print(f"Rate limited. Waiting {wait}s before retry {attempt + 1}/{max_retries}.")
time.sleep(wait)
continue
# For non-429 errors, use exponential backoff
backoff = 2 ** attempt
print(f"Request failed ({response.status_code}). Retrying in {backoff}s.")
time.sleep(backoff)
raise RuntimeError("Max retries exceeded.")
Custom rate limits on API keys
When you create an API key in the Developer Console, you can set a custom rate_limit field. This overrides the default 600 req/min ceiling for that specific key and can be set anywhere from 1 to 10,000 requests per minute:
{
"name": "High-Volume Scanner",
"rate_limit": 1200
}
This is useful when you have multiple keys serving different workloads and want to allocate capacity explicitly. The custom limit is reflected in the X-RateLimit-Limit header on every response for that key.
Use the classification=actionable filter when polling the arbitrage endpoint. Filtering to only actionable opportunities reduces response payload size and the request frequency you need to stay current — a straightforward way to get more value from each request within your limit.