Rate Limiting
Clemta APIs implement rate limiting to ensure fair usage and system stability. This page explains how rate limiting works across all our APIs.
Rate Limit Types
Clemta APIs employ different rate limiting strategies depending on the endpoint:
- API Key-based Rate Limiting: Applied to most endpoints
- Resource-based Rate Limiting: Applied to specific resources (e.g., per company ID)
All API responses include headers that provide information about rate limits:
Displays your API key’s rate limit (e.g., “100 requests per minute”)
Number of requests remaining in the current time window
Maximum number of requests allowed per minute
Unix timestamp when the rate limit resets
Indicates which rate limit type is being applied (“api_key” or “resource”)
When a rate limit is exceeded, you’ll also receive:
Seconds to wait before making another request (typically 60 seconds)
Rate Limit Errors
When a rate limit is exceeded, the API will respond with:
- HTTP Status Code:
429 Too Many Requests
- Error Code:
RATE_LIMIT_EXCEEDED
- Error Message: “Rate limit exceeded”
{
"success": false,
"message": "Rate limit exceeded",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"details": "Too many requests. Please try again later."
}
}
Best Practices
Implement exponential backoff
Use a retry strategy with exponential backoff when you receive 429.
Monitor your usage
Track X-RateLimit-Remaining and slow down before hitting limits.
Use conditional requests
Use If-Modified-Since for GET endpoints.
Use pagination
Paginate list endpoints to reduce load.
1. Implement Exponential Backoff
package main
import (
"fmt"
"math"
"net/http"
"strconv"
"time"
)
func makeRequestWithRetry(url string, apiKey string, maxRetries int) (*http.Response, error) {
client := &http.Client{}
for attempt := 0; attempt < maxRetries; attempt++ {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("X-API-KEY", apiKey)
resp, err := client.Do(req)
if err != nil {
if attempt == maxRetries-1 {
return nil, err
}
continue
}
if resp.StatusCode == http.StatusTooManyRequests { // 429
retryAfter := resp.Header.Get("Retry-After")
var delay time.Duration
if retryAfter != "" {
if seconds, err := strconv.Atoi(retryAfter); err == nil {
delay = time.Duration(seconds) * time.Second
}
}
if delay == 0 {
ms := math.Min(1000*math.Pow(2, float64(attempt)), 60000)
delay = time.Duration(ms) * time.Millisecond
}
fmt.Printf("Rate limited. Waiting %v before retry %d\n", delay, attempt+1)
time.Sleep(delay)
continue
}
return resp, nil
}
return nil, fmt.Errorf("max retries exceeded")
}
package main
import (
"fmt"
"net/http"
"strconv"
"time"
)
func makeRequestWithMonitoring(url string, apiKey string) (*http.Response, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("X-API-KEY", apiKey)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
remaining := resp.Header.Get("X-RateLimit-Remaining")
reset := resp.Header.Get("X-RateLimit-Reset")
limit := resp.Header.Get("X-RateLimit-Limit")
fmt.Printf("Rate limit: %s/%s remaining\n", remaining, limit)
if remaining != "" && reset != "" {
if r, err := strconv.Atoi(remaining); err == nil && r < 10 {
if rs, err := strconv.ParseInt(reset, 10, 64); err == nil {
wait := (rs * 1000) - time.Now().UnixMilli()
if wait > 0 {
fmt.Printf("Waiting %dms before next request\n", wait)
time.Sleep(time.Duration(wait) * time.Millisecond)
}
}
}
}
return resp, nil
}
3. Use Conditional Requests
package main
import (
"fmt"
"net/http"
)
func makeConditionalRequest(url string, apiKey string, lastModified string) (*http.Response, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("X-API-KEY", apiKey)
if lastModified != "" {
req.Header.Set("If-Modified-Since", lastModified)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode == http.StatusNotModified { // 304
fmt.Println("No changes since last request")
} else {
fmt.Printf("Last-Modified: %s\n", resp.Header.Get("Last-Modified"))
}
return resp, nil
}
Successful Request
X-RateLimit-API: 100 requests per minute
X-RateLimit-Remaining: 95
X-RateLimit-Limit: 100
X-RateLimit-Reset: 1640995200
X-RateLimit-Type: api_key
Rate Limited Request
X-RateLimit-API: 100 requests per minute
X-RateLimit-Remaining: 0
X-RateLimit-Limit: 100
X-RateLimit-Reset: 1640995200
X-RateLimit-Type: api_key
Retry-After: 60