Skip to content

Rate Limiting

Note: The Teachify Admin API is currently under development and not yet available for public use. This documentation is provided for preview purposes only.

Overview

The Teachify Admin API implements rate limiting to ensure system stability and fair usage across all users. Rate limits are applied per API key and vary based on the type of operation being performed.

Rate Limit Tiers

Rate limits are differentiated by operation type to balance system resources while providing a good developer experience:

Operation TypeLimitWindowTypical Use Cases
Mutations200 requestsper minuteCreating, updating, or deleting resources
Queries600 requestsper minuteReading data, listing resources

These limits are designed to accommodate:

  • Batch operations (e.g., enrolling multiple students)
  • Data synchronization workflows
  • Dashboard and reporting applications
  • Real-time data queries

How Rate Limiting Works

Identification

Rate limits are tracked per API key. Each API key has its own independent rate limit counters for mutations and queries.

Time Windows

Rate limits use a sliding window of 1 minute:

  • The counter tracks requests in the past 60 seconds
  • Once a request is older than 60 seconds, it no longer counts toward your limit
  • This provides smooth, continuous access rather than hard resets at fixed intervals

Operation Detection

The API automatically detects whether a request is a mutation or query:

  • Mutation: GraphQL query string starts with mutation
  • Query: All other GraphQL operations (including introspection queries)

Example:

# This counts as a MUTATION (200/minute limit)
mutation {
enrollStudentToCourse(input: { ... }) {
enrollment { id }
}
}
# This counts as a QUERY (600/minute limit)
query {
courses(first: 50) {
nodes { id name }
}
}

Rate Limit Headers

When you exceed the rate limit, the API returns a 429 status code with headers to help you track your usage:

Response Headers (429 Only)

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 45
X-RateLimit-Limit: 200
X-RateLimit-Reset: 1699012860
  • Retry-After: Number of seconds you should wait before making another request
  • X-RateLimit-Limit: The maximum number of requests allowed in the current window
  • X-RateLimit-Reset: Unix timestamp indicating when the rate limit window resets

Error Response Format

When you exceed a rate limit, you’ll receive a structured GraphQL error response:

{
"errors": [{
"message": "Rate limit exceeded for mutations. Please retry after 45 seconds.",
"extensions": {
"code": "RATE_LIMIT_EXCEEDED",
"operation_type": "mutations",
"limit": 200,
"retry_after": 45,
"reset_at": 1699012860
}
}]
}

Error Response Fields

FieldDescription
messageHuman-readable error message
codeError code: RATE_LIMIT_EXCEEDED
operation_typeEither mutations or queries
limitThe rate limit that was exceeded
retry_afterSeconds to wait before retrying
reset_atUnix timestamp when the limit resets

Best Practices

To avoid hitting rate limits:

  • Respect the Retry-After header - Always wait the specified time before retrying a 429 response
  • Implement retry logic - Use exponential backoff with a maximum retry limit for production applications
  • Cache query responses - Reduce API calls by caching frequently accessed data
  • Batch operations - Use GraphQL aliases to combine multiple mutations into a single request
  • Use appropriate pagination - Fetch 50-100 items per page instead of making many small requests
  • Spread out non-urgent operations - Add delays between background tasks to stay under limits

Common Scenarios

High-Volume Data Synchronization: Use pagination (50-100 items/page) with 1-2 second delays between requests. Cache data when possible and run during off-peak hours.

Real-Time Dashboards: Cache aggressively (30-60 seconds), use webhooks instead of polling when available, and implement smart polling intervals.

Batch Operations: Group related operations into single requests using GraphQL aliases. Process in batches of 20-50 items with delays between batches.

Increasing Rate Limits

The default rate limits are designed to accommodate the vast majority of use cases. However, if you have a legitimate need for higher limits:

  1. Contact our support team with details about your use case
  2. Provide information about:
    • Your application’s purpose
    • Expected request patterns
    • Peak usage times
    • Number of users/schools affected
  3. Demonstrate that you’ve implemented best practices (caching, batching, etc.)

We review rate limit increase requests on a case-by-case basis.

Configuration

For self-hosted deployments, rate limits can be adjusted in config/initializers/rack_attack.rb:

module AdminGraphqlRateLimits
MUTATIONS_LIMIT = 200 # requests per minute (default: 200)
QUERIES_LIMIT = 600 # requests per minute (default: 600)
PERIOD = 1.minute # time window
end

After modifying these values, restart your Rails server for the changes to take effect.

Testing Rate Limits

When developing your integration, you can test rate limit handling:

Terminal window
# Test script to trigger rate limits (for testing only)
for i in {1..250}; do
curl -X POST https://teachify.io/admin/graphql \
-H "Content-Type: application/json" \
-H "X-Teachify-API-Key: your_test_key" \
-d '{"query":"mutation { enrollStudentToCourse(input: {courseId: \"test\", email: \"test@example.com\"}) { enrollment { id } } }"}' \
&
done

Note: Only test rate limits in a development or staging environment, never in production.

FAQ

Q: Are rate limits shared across multiple API keys?

No. Each API key has its own independent rate limit counters.

Q: Do failed requests count toward my rate limit?

Yes. All requests count toward your rate limit, regardless of whether they succeed or fail.

Q: Can I check my current rate limit usage without making a request?

No. Rate limit information is only provided in response headers after making a request.

Q: What happens to requests made exactly when the limit resets?

The sliding window approach means there’s no hard reset. As older requests age out (after 60 seconds), your available quota gradually increases.

Q: Are GraphQL introspection queries counted?

Yes, introspection queries count as queries and use your query rate limit (600/min).

Q: Do retried requests count multiple times?

Yes. Each request attempt counts toward your limit, including retries.

Summary

  • Mutations: 200/minute per API key
  • Queries: 600/minute per API key
  • Always respect the Retry-After header when receiving a 429 response
  • Implement retry logic with exponential backoff
  • Cache responses and batch operations to reduce API calls