Skip to main content

Rate Limiting

:::caution Planned — not yet available This page describes an API-wide rate-limiting scheme that is not yet implemented. The public REST API does not currently return X-RateLimit-* headers or 429 responses for general endpoints; 429 is used today only by streaming (SSE) endpoints and billing spend-limits. The limits, headers, and examples below illustrate a planned feature. :::

The Tourfold API implements rate limiting to ensure fair usage and maintain service quality for all users. Understanding and properly handling rate limits is crucial for building reliable applications.

How Rate Limiting Works​

Rate limits are applied per API key or user token and are tracked using a sliding window approach. Different endpoints may have different limits based on their resource intensity.

Rate Limit Headers​

Every API response includes rate limit information in the headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1642233600
X-RateLimit-Window: 3600
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingNumber of requests remaining in the current window
X-RateLimit-ResetUnix timestamp when the current window resets
X-RateLimit-WindowWindow size in seconds

Rate Limit Tiers​

TierRequests per HourBurst LimitDescription
Standard1,0001,000Production applications
EnterpriseCustomCustomCustom limits for enterprise customers

Default Limits​

General Endpoints​

  • Read operations: 1,000 requests per hour
  • Write operations: 500 requests per hour
  • Search operations: 200 requests per hour

Specific Endpoint Limits​

EndpointLimitWindowNotes
GET /api/v2/tours1,0001 hourList tours
POST /api/v2/tours5001 hourCreate tour
GET /api/v2/drivers1,0001 hourList drivers

Handling Rate Limits​

Rate Limit Response​

When you exceed the rate limit, you'll receive a 429 Too Many Requests response:

{
"type": "https://tourfold.com/problems/rate-limit-exceeded",
"title": "Rate limit exceeded",
"detail": "You have exceeded the rate limit for this endpoint",
"data": {
"limit": 1000,
"remaining": 0,
"reset_time": 1642233600,
"retry_after": 3600,
"window_size": 3600
}
}

Retry After Header​

The response also includes a Retry-After header indicating when you can make requests again:

Retry-After: 3600

Best Practices​

Implementing Exponential Backoff​

#!/bin/bash

make_api_request() {
local url="$1"
local retries="${2:-3}"

response=$(curl -s -w "%{http_code}" -o /tmp/response.json "$url")
http_code="${response: -3}"

if [ "$http_code" -eq 429 ]; then
retry_after=$(curl -s -I "$url" | grep -i "retry-after" | cut -d' ' -f2 | tr -d '\r')

if [ "$retries" -gt 0 ]; then
echo "Rate limited. Waiting $retry_after seconds before retry..."
sleep "$retry_after"
make_api_request "$url" $((retries - 1))
else
echo "Max retries exceeded"
exit 1
fi
else
cat /tmp/response.json
fi
}

# Usage example
make_api_request "https://api.tourfold.com/api/v2/tours" 3

Monitoring Rate Limits​

#!/bin/bash

check_rate_limit() {
local url="$1"

# Make a request and capture headers
response=$(curl -s -I -H "Authorization: Bearer YOUR_TOKEN" "$url")

# Extract rate limit headers
limit=$(echo "$response" | grep -i "x-ratelimit-limit" | cut -d' ' -f2 | tr -d '\r')
remaining=$(echo "$response" | grep -i "x-ratelimit-remaining" | cut -d' ' -f2 | tr -d '\r')
reset=$(echo "$response" | grep -i "x-ratelimit-reset" | cut -d' ' -f2 | tr -d '\r')

if [ -n "$limit" ] && [ -n "$remaining" ]; then
usage_percentage=$(( (limit - remaining) * 100 / limit ))

echo "Rate Limit Usage: ${usage_percentage}%"
echo "Remaining: $remaining / $limit"
echo "Reset: $(date -d @$reset)"

if [ "$usage_percentage" -gt 80 ]; then
echo "WARNING: High rate limit usage!"
fi
fi
}

# Usage example
check_rate_limit "https://api.tourfold.com/api/v2/tours"

Caching Responses​

#!/bin/bash

# Simple file-based caching
cache_dir="/tmp/tourfold_cache"
mkdir -p "$cache_dir"

get_cached_response() {
local url="$1"
local cache_file="$cache_dir/$(echo "$url" | md5sum | cut -d' ' -f1)"

# Check if cache exists and is less than 5 minutes old
if [ -f "$cache_file" ] && [ $(($(date +%s) - $(stat -c %Y "$cache_file"))) -lt 300 ]; then
echo "Using cached response for: $url"
cat "$cache_file"
else
echo "Fetching fresh data for: $url"
response=$(curl -s -H "Authorization: Bearer YOUR_TOKEN" "$url")
echo "$response" > "$cache_file"
echo "$response"
fi
}

# Usage example
get_cached_response "https://api.tourfold.com/api/v2/tours"

Rate Limit Strategies​

1. Request Batching​

Instead of making multiple individual requests, batch them when possible:

# Instead of multiple individual requests
for tour_id in tour_123 tour_456 tour_789; do
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://api.tourfold.com/api/v2/tours/$tour_id"
done

# Use batch endpoint
curl -X POST "https://api.tourfold.com/api/v2/tours/batch" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"tour_ids": ["tour_123", "tour_456", "tour_789"]
}'

2. Pagination Optimization​

Use appropriate page sizes to reduce the number of requests:

# Use larger page sizes when possible
curl "https://api.tourfold.com/api/v2/tours?page=0&size=100" \
-H "Authorization: Bearer YOUR_TOKEN"

3. Webhook Integration​

Use webhooks instead of polling for real-time updates:

# Instead of polling
while true; do
curl "https://api.tourfold.com/api/v2/tours?updated_since=$last_check" \
-H "Authorization: Bearer YOUR_TOKEN"
sleep 60
done

# Register a webhook endpoint
curl -X POST "https://api.tourfold.com/api/v2/webhook-endpoints" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"display_name": "Tour lifecycle listener",
"endpoint_url": "https://your-server.com/webhook",
"signature_scheme": "HMAC_SHA256",
"enabled": true,
"retry": true,
"subscriptions": ["device.updated", "user.updated"]
}'

Monitoring and Alerts​

Setting Up Alerts​

#!/bin/bash

# Simple rate limit monitoring script
monitor_rate_limits() {
local url="$1"
local log_file="/tmp/rate_limit_monitor.log"

# Get current usage
response=$(curl -s -I -H "Authorization: Bearer YOUR_TOKEN" "$url")
limit=$(echo "$response" | grep -i "x-ratelimit-limit" | cut -d' ' -f2 | tr -d '\r')
remaining=$(echo "$response" | grep -i "x-ratelimit-remaining" | cut -d' ' -f2 | tr -d '\r')

if [ -n "$limit" ] && [ -n "$remaining" ]; then
usage_percentage=$(( (limit - remaining) * 100 / limit ))

# Log usage
echo "$(date): Usage: ${usage_percentage}% (${remaining}/${limit})" >> "$log_file"

# Alert if usage is critical
if [ "$usage_percentage" -gt 90 ]; then
echo "CRITICAL: Rate limit usage at ${usage_percentage}%!" | tee -a "$log_file"
# Send alert (email, Slack, etc.)
fi
fi
}

# Monitor every 5 minutes
while true; do
monitor_rate_limits "https://api.tourfold.com/api/v2/tours"
sleep 300
done

Upgrading Your Rate Limits​

Requesting Higher Limits​

Contact our support team to request higher rate limits:

  1. Provide usage metrics: Show current usage patterns
  2. Explain use case: Describe your application's needs
  3. Demonstrate need: Show why higher limits are required

Enterprise Plans​

Enterprise customers can get:

  • Custom rate limits
  • Dedicated infrastructure
  • Priority support
  • SLA guarantees

Common Mistakes to Avoid​

❌ Don't Ignore Rate Limits​

# Bad: No rate limit handling
curl "https://api.tourfold.com/api/v2/tours"

❌ Don't Retry Immediately​

# Bad: Immediate retry
if [ $http_code -eq 429 ]; then
curl "https://api.tourfold.com/api/v2/tours" # This will fail again
fi

✅ Do Handle Rate Limits Properly​

# Good: Proper rate limit handling
make_request() {
response=$(curl -s -w "%{http_code}" -o /tmp/response.json \
-H "Authorization: Bearer YOUR_TOKEN" \
"https://api.tourfold.com/api/v2/tours")
http_code="${response: -3}"

if [ "$http_code" -eq 429 ]; then
retry_after=$(curl -s -I "https://api.tourfold.com/api/v2/tours" | \
grep -i "retry-after" | cut -d' ' -f2 | tr -d '\r')
echo "Rate limited. Waiting $retry_after seconds..."
sleep "$retry_after"
make_request
else
cat /tmp/response.json
fi
}

Testing Rate Limits​

Controlled Test Environment​

Test retry handling locally or against a private non-production environment provided for your integration. Keep the API base URL configurable and do not load-test the production API.

Load Testing​

#!/bin/bash

# Simple load test
load_test() {
local endpoint="$1"
local count="${2:-100}"
local rate_limited=0

echo "Testing $count requests to $endpoint..."

for ((i=1; i<=count; i++)); do
response=$(curl -s -w "%{http_code}" -o /dev/null \
-H "Authorization: Bearer YOUR_TOKEN" \
"$endpoint")
http_code="${response: -3}"

if [ "$http_code" -eq 429 ]; then
((rate_limited++))
fi

# Small delay to avoid overwhelming
sleep 0.1
done

echo "Rate limited requests: $rate_limited out of $count"
}

# Usage example
load_test "https://api.tourfold.com/api/v2/tours" 50

Next steps​