Rate Limits
Glyph implements rate limiting to ensure fair usage and maintain service quality. This guide explains how rate limits work and how to handle them in your application.
Rate Limit Tiers
Section titled “Rate Limit Tiers”Rate limits are based on your subscription tier:
| Tier | Requests/Minute | Monthly PDFs | Price |
|---|---|---|---|
| Free | 5 | 50 | $0 |
| Starter | 20 | 500 | $29/mo |
| Pro | 60 | 5,000 | $99/mo |
| Business | 200 | 25,000 | $299/mo |
| Enterprise | Custom | Unlimited | Contact us |
Types of Limits
Section titled “Types of Limits”Per-Minute Rate Limit
Section titled “Per-Minute Rate Limit”Applies to all API endpoints. Resets every 60 seconds.
Monthly PDF Limit
Section titled “Monthly PDF Limit”Applies only to /v1/generate endpoint. Resets on the 1st of each month.
Rate Limit Headers
Section titled “Rate Limit Headers”Every API response includes rate limit headers:
X-RateLimit-Limit: 60X-RateLimit-Remaining: 45X-RateLimit-Reset: 1705320060| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
For the generate endpoint, additional headers show monthly usage:
X-Monthly-Limit: 1000X-Monthly-Used: 150X-Monthly-Remaining: 850Rate Limit Exceeded
Section titled “Rate Limit Exceeded”Per-Minute Limit Exceeded
Section titled “Per-Minute Limit Exceeded”HTTP Status: 429 Too Many Requests
{ "error": "Rate limit exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 45, "tier": "free", "limit": 10, "windowMs": 60000}The Retry-After header indicates seconds until you can retry:
Retry-After: 45Monthly Limit Exceeded
Section titled “Monthly Limit Exceeded”HTTP Status: 429 Too Many Requests
{ "error": "Monthly PDF limit exceeded", "code": "MONTHLY_LIMIT_EXCEEDED", "limit": 100, "used": 100, "tier": "free", "upgrade": "https://glyph.you/pricing"}Handling Rate Limits
Section titled “Handling Rate Limits”JavaScript with Retry Logic
Section titled “JavaScript with Retry Logic”async function callGlyphAPI(endpoint, data, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { const response = await fetch(`https://api.glyph.you${endpoint}`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
if (response.status === 429) { const retryAfter = parseInt(response.headers.get('Retry-After') || '60'); console.log(`Rate limited. Retrying in ${retryAfter} seconds...`); await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)); continue; }
return response.json(); }
throw new Error('Max retries exceeded');}Python with Exponential Backoff
Section titled “Python with Exponential Backoff”import timeimport requestsfrom requests.exceptions import HTTPError
def call_glyph_api(endpoint, data, max_retries=3): for i in range(max_retries): response = requests.post( f'https://api.glyph.you{endpoint}', headers={ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }, json=data )
if response.status_code == 429: retry_after = int(response.headers.get('Retry-After', 60)) print(f'Rate limited. Retrying in {retry_after} seconds...') time.sleep(retry_after) continue
response.raise_for_status() return response.json()
raise Exception('Max retries exceeded')Best Practices
Section titled “Best Practices”1. Monitor Your Usage
Section titled “1. Monitor Your Usage”Track the rate limit headers in your application:
function logRateLimits(response) { console.log({ limit: response.headers.get('X-RateLimit-Limit'), remaining: response.headers.get('X-RateLimit-Remaining'), reset: new Date(response.headers.get('X-RateLimit-Reset') * 1000) });}2. Implement Request Queuing
Section titled “2. Implement Request Queuing”For high-volume applications, queue requests to stay within limits:
class RateLimitedQueue { constructor(requestsPerMinute) { this.interval = 60000 / requestsPerMinute; this.queue = []; this.processing = false; }
async add(fn) { return new Promise((resolve, reject) => { this.queue.push({ fn, resolve, reject }); this.process(); }); }
async process() { if (this.processing || this.queue.length === 0) return; this.processing = true;
const { fn, resolve, reject } = this.queue.shift(); try { const result = await fn(); resolve(result); } catch (error) { reject(error); }
setTimeout(() => { this.processing = false; this.process(); }, this.interval); }}
// Usageconst queue = new RateLimitedQueue(60); // 60 requests per minute
await queue.add(() => glyphApi.preview(template, data));3. Cache Responses
Section titled “3. Cache Responses”Cache preview responses to avoid redundant API calls:
const previewCache = new Map();
async function getPreview(template, data) { const cacheKey = `${template}-${JSON.stringify(data)}`;
if (previewCache.has(cacheKey)) { return previewCache.get(cacheKey); }
const result = await glyphApi.preview(template, data); previewCache.set(cacheKey, result);
return result;}4. Batch Operations
Section titled “4. Batch Operations”If modifying multiple documents, space out requests:
async function batchModify(sessions, modifications) { const results = [];
for (const [sessionId, prompt] of Object.entries(modifications)) { const result = await glyphApi.modify(sessionId, prompt); results.push(result);
// Wait 1 second between requests to stay well under limit await new Promise(resolve => setTimeout(resolve, 1000)); }
return results;}Upgrading Your Tier
Section titled “Upgrading Your Tier”If you’re consistently hitting rate limits, consider upgrading:
- Go to glyph.you/dashboard
- Navigate to Billing
- Select a higher tier
- Your new limits apply immediately
Questions?
Section titled “Questions?”- Check your current usage in the dashboard
- Contact support@glyph.you for rate limit increases
- Enterprise customers: Contact your account manager