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.