Rate Limits
To ensure fair usage and system stability, the Priostack API enforces rate limits on a per-API-key basis. Rate limiting is applied across all API endpoints.
Current Limits
| Limit | Value |
|---|---|
| Requests per minute | 300 per API key |
| Window type | Fixed 60-second window |
| Scope | Per API key (not per IP) |
| Long-poll requests (job activation) | Counted as 1 request regardless of wait duration |
Rate Limit Headers
Every API response includes the following headers so you can monitor your usage:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window | 300 |
X-RateLimit-Remaining |
Requests remaining in the current window | 247 |
X-RateLimit-Window |
Window duration in seconds | 60 |
X-RateLimit-Reset |
Unix timestamp (seconds) when the window resets | 1746456780 |
# Inspect rate limit headers
curl -i -H "X-API-Key: your_key" \
https://api.priostack.com/api/v1/topology
HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Window: 60
X-RateLimit-Reset: 1746456780
Content-Type: application/json
...
Handling 429 Responses
When the rate limit is exceeded, the API returns HTTP 429 with an error body. You should stop sending requests and wait until the reset time.
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1746456780
Retry-After: 23
{"error": "rate limit exceeded"}
Retry Logic in Go
func callWithRetry(req *http.Request) (*http.Response, error) {
for attempts := 0; attempts < 5; attempts++ {
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusTooManyRequests {
return resp, nil
}
resp.Body.Close()
// Read reset time from header
resetStr := resp.Header.Get("X-RateLimit-Reset")
reset, _ := strconv.ParseInt(resetStr, 10, 64)
waitUntil := time.Unix(reset, 0)
sleepDuration := time.Until(waitUntil) + time.Second // +1s buffer
log.Printf("Rate limited. Waiting %s until reset.", sleepDuration)
time.Sleep(sleepDuration)
}
return nil, errors.New("exceeded max retry attempts")
}
Retry Logic in Python
import time
import requests
def call_with_retry(url, headers, payload, max_attempts=5):
for attempt in range(max_attempts):
response = requests.post(url, headers=headers, json=payload)
if response.status_code != 429:
return response
reset = int(response.headers.get("X-RateLimit-Reset", time.time() + 60))
wait = max(0, reset - time.time()) + 1 # +1s buffer
print(f"Rate limited. Sleeping {wait:.1f}s...")
time.sleep(wait)
raise Exception("Max retry attempts exceeded")
Tips for High-Volume Usage
1. Use Long-Polling for Job Workers
The GET /api/v1/jobs/activate endpoint supports long-polling with a configurable timeout (default 30 seconds). A single long-poll request that waits 30 seconds counts as only 1 request against your rate limit — far more efficient than rapid polling.
# Long-poll with 30s timeout — only 1 request per 30s
curl -X POST "https://api.priostack.com/api/v1/jobs/activate" \
-H "X-API-Key: your_key" \
-d '{"type": "my-worker", "maxJobsToActivate": 10, "requestTimeout": 30000}'
2. Batch Process Starts
If you need to start many process instances, use the batch endpoint to start multiple instances in a single request:
POST /api/v1/process-instances/batch
[
{"bpmnProcessId": "order-processing", "variables": {"orderId": "1"}},
{"bpmnProcessId": "order-processing", "variables": {"orderId": "2"}},
{"bpmnProcessId": "order-processing", "variables": {"orderId": "3"}}
]
3. Cache Process Definitions
Avoid calling GET /api/v1/process-definitions on every request. Cache the definition key in your application after deployment. The key only changes when you redeploy.
4. Monitor Your Usage
Check the X-RateLimit-Remaining header in your application and log a warning when it drops below 20% of the limit (60 requests). This gives you time to throttle before hitting the limit.