Rate Limits¶
Dxtra rate-limits public-facing endpoints to protect service stability. Authenticated API calls are not rate-limited.
Current Limits¶
| Endpoint Type | Limit | Window | Notes |
|---|---|---|---|
Webhook endpoints (conduit.dxtra.ai) | 1,000 requests | 15 minutes | Per IP address |
| GraphQL API (authenticated) | No limit | -- | Requires valid JWT and webhook secret |
| Health checks | No limit | -- | Always allowed |
Authenticated Requests Bypass Rate Limits
GraphQL actions called through the API with a valid JWT token are not rate-limited. Rate limiting only applies to direct HTTP requests to public webhook endpoints without authentication.
Handling Rate Limit Responses¶
When you exceed the rate limit, the API returns HTTP 429 Too Many Requests.
const makeRequest = async (query, variables, retries = 3) => {
const response = await fetch('https://api.dxtra.ai/v1/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`,
'X-Hasura-Role': 'user'
},
body: JSON.stringify({ query, variables })
});
if (response.status === 429 && retries > 0) {
const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : 1000;
await new Promise(resolve => setTimeout(resolve, delay));
return makeRequest(query, variables, retries - 1);
}
return response.json();
};
Best Practices¶
Cache JWT Tokens¶
JWT tokens are valid for 15 minutes. Cache them and reuse across requests instead of requesting a new token for each call.
Request Only Needed Fields¶
GraphQL lets you request exactly the fields you need. Smaller responses are faster to process.
Use Exponential Backoff for Retries¶
When requests fail with 429, 500, 502, or 503 status codes, retry with increasing delays:
const retryWithBackoff = async (fn, maxRetries = 5) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = Math.pow(2, attempt - 1) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
Use GraphQL Subscriptions Instead of Polling¶
For real-time data, use GraphQL subscriptions rather than polling the API repeatedly. Subscriptions push updates to your client when data changes, eliminating unnecessary requests.
Avoid: Requesting all fields when not needed¶
query GetDataControllersHeavy { dataControllers { id title createdAt updatedAt did webhookSecret users { id email role } dataSubjects { id did dataProcessingActivities { typeId triggeredAt } } } }
### 5. Batch Operations
```graphql title="Batch Mutation Example"
# Use Hasura's standard bulk insert for batch operations
mutation InsertMultipleActivities($objects: [dataProcessingActivities_insert_input!]!) {
insertDataProcessingActivities(objects: $objects) {
affected_rows
returning {
id
dataSubjectId
triggeredAt
}
}
}
Monitoring Your Usage¶
Track Your Request Patterns¶
const requestMetrics = {
requests: 0,
errors: 0,
startTime: Date.now()
};
const trackRequest = async (requestFn) => {
requestMetrics.requests++;
try {
const result = await requestFn();
return result;
} catch (error) {
requestMetrics.errors++;
throw error;
}
};
// Log metrics periodically
setInterval(() => {
const elapsed = Date.now() - requestMetrics.startTime;
const requestsPerMinute = (requestMetrics.requests / elapsed) * 60000;
const errorRate = requestMetrics.errors / requestMetrics.requests;
console.log(`API Usage: ${requestsPerMinute.toFixed(1)} req/min, ${(errorRate * 100).toFixed(1)}% error rate`);
}, 60000);
Rate Limit Headers
When rate limits are exceeded, you'll receive an HTTP 429 response. Use exponential backoff to retry after a delay.
Related Documentation¶
- Error Handling - Handle API errors gracefully
- Authentication - Manage JWT token lifecycle
- GraphQL Reference - Optimize your queries