Skip to content
Last updated: 2026-04-06
Reference

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.

JavaScript
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.

GraphQL
# Request only what you need
query {
  dataControllers {
    id
    title
    createdAt
  }
}

Use Exponential Backoff for Retries

When requests fail with 429, 500, 502, or 503 status codes, retry with increasing delays:

JavaScript
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 } } } }

Text Only
### 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

Simple Usage Tracking
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.