Rate Limits
The Logproof API implements rate limiting to ensure fair usage and maintain service quality for all users. Rate limits are applied per API key for API requests and per token for widget requests.
Rate Limit Overview
| Endpoint Type | Rate Limit | Window |
|---|---|---|
| API Endpoints | 100 requests | Per minute per API key |
| Widget Endpoints | 120 requests | Per minute per token |
Note: The batch events endpoint counts as a single request regardless of the number of events included in the batch, making it an efficient way to log multiple events.
Rate Limit Headers
Every API response includes headers that provide information about your current rate limit status:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum number of requests allowed in the current window |
X-RateLimit-Remaining |
Number of requests remaining in the current window |
Retry-After |
Number of seconds to wait before retrying (only present when rate limited) |
curl -i https://api.logproof.com/v1/events \
-H "Authorization: Bearer lp_test_..." \
-H "Content-Type: application/json" \
-d '{"type": "user.login", "actor": {"id": "user_123"}}'
# Response Headers
HTTP/2 200
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
Content-Type: application/json
HTTP/2 200
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
Content-Type: application/json
{
"id": "evt_abc123",
"type": "user.login",
...
}
Rate Limit Exceeded Response
When you exceed the rate limit, the API returns a 429 Too Many Requests response with the Retry-After header indicating when you can retry:
curl -i https://api.logproof.com/v1/events \
-H "Authorization: Bearer lp_test_..."
# Response (429)
HTTP/2 429
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 45
Content-Type: application/json
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 45 seconds."
}
}
HTTP/2 429
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
Retry-After: 45
Content-Type: application/json
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 45 seconds."
}
}
{
"error": {
"type": "rate_limit_exceeded",
"message": "Rate limit exceeded. Please retry after 45 seconds."
}
}
Best Practices
Follow these best practices to stay within rate limits and optimize your API usage:
1. Use the Batch Endpoint
The batch events endpoint allows you to send multiple events in a single request, which counts as only one request against your rate limit:
curl https://api.logproof.com/v1/events/batch \
-H "Authorization: Bearer lp_test_..." \
-H "Content-Type: application/json" \
-d '{
"events": [
{"type": "user.login", "actor": {"id": "user_123"}},
{"type": "file.upload", "actor": {"id": "user_123"}},
{"type": "document.view", "actor": {"id": "user_456"}}
]
}'
const events = [
{type: 'user.login', actor: {id: 'user_123'}},
{type: 'file.upload', actor: {id: 'user_123'}},
{type: 'document.view', actor: {id: 'user_456'}}
];
// Single request for multiple events
const response = await fetch('https://api.logproof.com/v1/events/batch', {
method: 'POST',
headers: {
'Authorization': 'Bearer lp_test_...',
'Content-Type': 'application/json'
},
body: JSON.stringify({events})
});
import requests
events = [
{'type': 'user.login', 'actor': {'id': 'user_123'}},
{'type': 'file.upload', 'actor': {'id': 'user_123'}},
{'type': 'document.view', 'actor': {'id': 'user_456'}}
]
# Single request for multiple events
response = requests.post(
'https://api.logproof.com/v1/events/batch',
headers={'Authorization': 'Bearer lp_test_...'},
json={'events': events}
)
2. Implement Client-Side Caching
Cache API responses when appropriate to reduce the number of requests. For example, cache audit log queries for a short period:
# Use conditional requests with ETag headers curl https://api.logproof.com/v1/events \ -H "Authorization: Bearer lp_test_..." \ -H "If-None-Match: \"abc123etag\"" # Returns 304 Not Modified if content hasn't changed
const cache = new Map();
const CACHE_TTL = 60000; // 1 minute
async function getEvents() {
const cacheKey = 'events_list';
const cached = cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const response = await fetch('https://api.logproof.com/v1/events', {
headers: {'Authorization': 'Bearer lp_test_...'}
});
const data = await response.json();
cache.set(cacheKey, {data, timestamp: Date.now()});
return data;
}
import requests
from datetime import datetime, timedelta
cache = {}
CACHE_TTL = timedelta(minutes=1)
def get_events():
cache_key = 'events_list'
if cache_key in cache:
cached_data, timestamp = cache[cache_key]
if datetime.now() - timestamp < CACHE_TTL:
return cached_data
response = requests.get(
'https://api.logproof.com/v1/events',
headers={'Authorization': 'Bearer lp_test_...'}
)
data = response.json()
cache[cache_key] = (data, datetime.now())
return data
3. Use Webhooks Instead of Polling
Instead of repeatedly polling the API to check for new events or changes, configure webhooks to receive real-time notifications:
Webhooks are more efficient: They eliminate the need for frequent polling requests and provide real-time updates when events occur.
# Configure webhook endpoint instead of polling
curl https://api.logproof.com/v1/webhooks \
-H "Authorization: Bearer lp_test_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/logproof",
"events": ["event.created", "export.completed"]
}'
// Configure webhook instead of polling
await fetch('https://api.logproof.com/v1/webhooks', {
method: 'POST',
headers: {
'Authorization': 'Bearer lp_test_...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://your-app.com/webhooks/logproof',
events: ['event.created', 'export.completed']
})
});
# Configure webhook instead of polling
import requests
response = requests.post(
'https://api.logproof.com/v1/webhooks',
headers={'Authorization': 'Bearer lp_test_...'},
json={
'url': 'https://your-app.com/webhooks/logproof',
'events': ['event.created', 'export.completed']
}
)
4. Implement Exponential Backoff
When you receive a rate limit error, implement exponential backoff to automatically retry with increasing delays:
# Simple retry with backoff in bash
retry_count=0
max_retries=3
while [ $retry_count -lt $max_retries ]; do
response=$(curl -s -w "\n%{http_code}" \
https://api.logproof.com/v1/events \
-H "Authorization: Bearer lp_test_...")
status_code=$(echo "$response" | tail -n1)
if [ "$status_code" = "429" ]; then
retry_count=$((retry_count + 1))
sleep $((2 ** retry_count))
else
break
fi
done
async function callAPIWithRetry(maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch('https://api.logproof.com/v1/events', {
headers: {'Authorization': 'Bearer lp_test_...'}
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
const delay = Math.min(retryAfter * 1000, Math.pow(2, attempt) * 1000);
console.log(`Rate limited. Retrying in ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
import requests
import time
def call_api_with_retry(max_retries=3):
for attempt in range(max_retries):
response = requests.post(
'https://api.logproof.com/v1/events',
headers={'Authorization': 'Bearer lp_test_...'},
json={'type': 'user.login'}
)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
delay = min(retry_after, 2 ** attempt)
print(f'Rate limited. Retrying in {delay}s')
time.sleep(delay)
continue
return response
raise Exception('Max retries exceeded')
5. Monitor Rate Limit Headers
Always check the rate limit headers in responses to proactively slow down requests before hitting the limit:
Proactive Monitoring: Check X-RateLimit-Remaining before making requests. If it's low, consider implementing a delay or using the batch endpoint.
# Check rate limit headers curl -i https://api.logproof.com/v1/events \ -H "Authorization: Bearer lp_test_..." | grep "X-RateLimit" # Output: # X-RateLimit-Limit: 100 # X-RateLimit-Remaining: 15
const response = await fetch('https://api.logproof.com/v1/events', {
headers: {'Authorization': 'Bearer lp_test_...'}
});
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const limit = parseInt(response.headers.get('X-RateLimit-Limit'));
if (remaining < limit * 0.1) {
console.warn(`Only ${remaining} requests remaining in this window`);
// Consider slowing down or batching requests
}
response = requests.get(
'https://api.logproof.com/v1/events',
headers={'Authorization': 'Bearer lp_test_...'}
)
remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
limit = int(response.headers.get('X-RateLimit-Limit', 100))
if remaining < limit * 0.1:
print(f'Only {remaining} requests remaining in this window')
# Consider slowing down or batching requests
Higher Rate Limits
Need higher rate limits? If your application requires higher rate limits, please contact our support team to discuss custom rate limit options for your use case.