Developer Portal / Rate Limits & Best Practices
Developer Portal

Rate Limits & Best Practices

To ensure platform stability for all users, the eAgenda API enforces rate limits on requests.

Rate limits

TypeLimit
Requests per minuteVaries by plan
Requests per hourVaries by plan

Specific limits depend on your subscribed plan. Contact support for information about your plan’s limits.

Response headers

The API returns informational headers about rate limit usage:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1717524600
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the period
X-RateLimit-RemainingRemaining requests in the current period
X-RateLimit-ResetUnix timestamp when the counter resets

When the limit is reached

If you exceed the limit, the API returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
{
  "detail": "Request limit exceeded. Please try again in 30 seconds."
}

Handling rate limiting

Retry with exponential backoff

import requests
import time

def api_request(url, auth, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, auth=auth)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 30))
            wait = retry_after * (2 ** attempt)  # exponential backoff
            print(f"Rate limit hit. Waiting {wait}s...")
            time.sleep(wait)
            continue

        return response

    raise Exception("Max retries exceeded")

JavaScript

async function apiRequest(url, headers, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get("Retry-After") || "30");
      const wait = retryAfter * Math.pow(2, attempt);
      console.log(`Rate limit hit. Waiting ${wait}s...`);
      await new Promise(resolve => setTimeout(resolve, wait * 1000));
      continue;
    }

    return response;
  }

  throw new Error("Max retries exceeded");
}

Best practices for integrations

1. Minimize unnecessary requests

  • Use filters — Filter by status, start_date, calendar_key instead of fetching everything and filtering locally
  • Smart pagination — Use an appropriate page_size for your use case (e.g., 100 results per page)
  • Avoid excessive polling — Use webhooks instead of querying the API every few seconds

2. Cache when possible

Some data changes infrequently and can be cached:

ResourceSuggested cache time
Accounts (/accounts/)1 hour
Calendars (/calendars/)15 minutes
Services (/services/)15 minutes
Tags (/tags/)30 minutes
Available times1-2 minutes (maximum)
AppointmentsNo cache (use webhooks)

3. Handle errors properly

response = requests.get(url, auth=auth)

match response.status_code:
    case 200:
        data = response.json()
    case 400:
        print(f"Invalid data: {response.json()}")
    case 401:
        print("Invalid credentials")
    case 403:
        print("No permission")
    case 404:
        print("Resource not found")
    case 429:
        print("Rate limit - wait and try again")
    case _:
        print(f"Unexpected error: {response.status_code}")

4. Use timeouts

Always set timeouts to prevent requests from hanging your application:

response = requests.get(url, auth=auth, timeout=30)
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);

const response = await fetch(url, {
  headers,
  signal: controller.signal,
});

5. Logging and monitoring

Log requests to facilitate debugging:

  • Request URL and method
  • Response status code
  • Response time
  • Rate limit headers
  • Errors and retries