Rate Limits
The Project Expedition API enforces rate limits to ensure fair usage and system stability.
Daily Limits
| Environment | Daily Limit | Notes |
|---|---|---|
| Production | 5,000 calls/day | Can be increased upon request |
| Staging | 2,000 calls/day | For development only |
Rate Limit Response
When you exceed the daily limit, the API returns:
{ "error": "Rate limit exceeded. Please try again tomorrow or contact support."}HTTP Status: 429 Too Many Requests
Caching Best Practices
Implement caching to minimize API calls and improve performance:
Tour Catalog (/return_tours)
Cache Duration: 1 week
Tour catalog data (product descriptions, images, logistics) changes infrequently.
// Example caching strategyconst CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 1 week in ms
const getCachedTours = async function(country) { const cacheKey = `tours_${country}`; const cached = cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) { return cached.data; }
const response = await fetch(`${BASE_URL}/return_tours?country=${country}`, { headers: { 'access-token': TOKEN } }); const data = await response.json();
cache.set(cacheKey, { data, timestamp: Date.now() }); return data;};Availability (/return_availability)
Cache Duration: Do NOT cache
Availability and pricing can change in real-time. Always call this endpoint fresh.
Locations (/return_countries)
Cache Duration: 1 month
Geographic data (countries, regions, towns, hubs) rarely changes.
const LOCATIONS_CACHE_TTL = 30 * 24 * 60 * 60 * 1000; // 30 daysHandling Rate Limits
Implement exponential backoff when approaching limits:
const callWithRetry = async function(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const response = await fetch(url, options);
if (response.status === 429) { // Rate limited - wait and retry const waitTime = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s await new Promise(resolve => setTimeout(resolve, waitTime)); continue; }
return response; }
throw new Error('Rate limit exceeded after retries');};Bulk Operations
For bulk operations (syncing catalogs, etc.):
- Add delays - Wait 100-200ms between requests
- Use pagination - Process in smaller batches
- Schedule off-peak - Run during low-traffic hours (UTC nighttime)
const syncAllTours = async function(countries) { for (const country of countries) { const tours = await fetchTours(country); await processAndStore(tours);
// Wait 200ms before next request await new Promise(resolve => setTimeout(resolve, 200)); }};Monitoring Usage
Track your API usage to avoid hitting limits:
let dailyCallCount = 0;
const trackApiCall = function() { dailyCallCount++;
if (dailyCallCount >= 4500) { console.warn('Approaching daily rate limit:', dailyCallCount); }};Recommended Architecture
| Data Type | Fetch Strategy | Cache Duration |
|---|---|---|
| Tour catalog | Weekly sync | 7 days |
| Locations | Monthly sync | 30 days |
| Availability | Real-time | Never |
| Bookings | As needed | N/A |
| Cruises | Weekly sync | 7 days |
Need Higher Limits?
If your integration requires higher rate limits:
- Describe your use case and expected volume
- Explain your caching strategy
- Contact partners@projectexpedition.com
Most limit increases are approved for partners with proper caching implementations.