Rate Limits
The SDX API enforces rate limits to ensure fair, reliable access for all integrators. Rate limits are generous and free — there are no paid tiers to unlock higher limits.
Default limits
| Scope | Limit |
|---|---|
| Per API key | 1,000 requests per minute |
| Per OAuth2 user | 300 requests per minute |
| Bulk endpoints (CSV upload, batch operations) | 10 requests per minute |
| Report generation | 20 requests per minute |
These limits apply independently. For example, an API key can make 1,000 general requests and 10 bulk requests in the same minute.
Rate limit headers
Every API response includes headers that show your current rate limit status:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit | Maximum requests allowed per window | 1000 |
X-RateLimit-Remaining | Requests remaining in the current window | 847 |
X-RateLimit-Reset | Unix timestamp when the window resets | 1706100060 |
X-RateLimit-Window | Window duration in seconds | 60 |
Handling 429 responses
When you exceed the rate limit, the API returns a 429 Too Many Requests response:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 12 seconds.",
"retry_after": 12
}
}
The response includes a Retry-After header (in seconds) indicating when you can retry.
Recommended retry strategy
Implement exponential backoff with jitter:
import time
import random
def request_with_retry(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 1))
jitter = random.uniform(0, retry_after * 0.1)
time.sleep(retry_after + jitter)
raise Exception("Max retries exceeded")
Best practices
Batch where possible
Instead of making individual requests for each meter reading, use the batch endpoints:
# Instead of 100 individual POST requests:
POST /v1/buildings/bld_123/meters/mtr_456/readings
# Use one batch request:
POST /v1/buildings/bld_123/meters/mtr_456/readings/batch
Cache responses
Benchmark scores and property details change infrequently. Cache these responses and use the If-None-Match header with the ETag value to check for updates:
curl https://api.sdx.dev/v1/buildings/bld_123/benchmarks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "If-None-Match: \"abc123\""
If the data hasn't changed, the API returns 304 Not Modified with no body, which does not count against your rate limit.
Use webhooks
Instead of polling for updates, subscribe to webhooks to receive real-time notifications when data changes.
Requesting higher limits
If your integration legitimately requires higher limits, contact api-support@sdx.dev with a description of your use case. Limit increases are provided free of charge for genuine integration needs.
Sandbox limits
The sandbox environment has the same rate limits as production. This ensures your integration is tested under realistic conditions.