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 Type | Limit | Window | Typical Use Cases |
|---|---|---|---|
| Mutations | 200 requests | per minute | Creating, updating, or deleting resources |
| Queries | 600 requests | per minute | Reading 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 RequestsContent-Type: application/jsonRetry-After: 45X-RateLimit-Limit: 200X-RateLimit-Reset: 1699012860Retry-After: Number of seconds you should wait before making another requestX-RateLimit-Limit: The maximum number of requests allowed in the current windowX-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
| Field | Description |
|---|---|
message | Human-readable error message |
code | Error code: RATE_LIMIT_EXCEEDED |
operation_type | Either mutations or queries |
limit | The rate limit that was exceeded |
retry_after | Seconds to wait before retrying |
reset_at | Unix timestamp when the limit resets |
Best Practices
To avoid hitting rate limits:
- Respect the
Retry-Afterheader - 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:
- Contact our support team with details about your use case
- Provide information about:
- Your application’s purpose
- Expected request patterns
- Peak usage times
- Number of users/schools affected
- 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 windowendAfter 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:
# 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 } } }"}' \ &doneNote: 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-Afterheader when receiving a 429 response - Implement retry logic with exponential backoff
- Cache responses and batch operations to reduce API calls