Limits and Best Practices
Information about rate limits, pagination, caching and API best practices
Rate Limiting
Current Limits
- 100 requests per minute per API credential
- The counter resets every 60 seconds (sliding window)
- Rate limiting is applied globally to all endpoints
Response When the Limit Is Exceeded
When you exceed the limits, you will receive:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Limite de requisições excedido. Tente novamente em alguns minutos.",
"retryAfter": 60
}Rate Limiting Best Practices
-
Implement retry with exponential backoff
- Wait for the time given in the
Retry-Afterheader - Use an increasing delay between attempts
- Wait for the time given in the
-
Spread requests over time
- Avoid request bursts
- Use queues for batch operations
-
Cache data that does not change often
- The API caches queries for 30 minutes
- Avoid redundant requests
-
Group multiple operations when possible
- Use endpoints that support batch operations
- Combine filters to reduce the number of calls
Pagination
Parameters
| Parameter | Description | Minimum | Maximum | Default |
|---|---|---|---|---|
limit | Number of records per page | 1 | 5000 | 500 |
page | Page number of the results | 1 | — | 1 |
How to Use
GET /v1/google/ads/campaigns?organizationId=organization_123&limit=1000&page=2Automatic Adjustment
Invalid or out-of-range values are adjusted automatically
- Values below the minimum → set to the minimum
- Values above the maximum → set to the maximum
- Non-numeric values → the default is used
- Decimal values → rounded down (floor)
Adjustment examples:
limit=0→ adjusted to1limit=6000→ adjusted to5000limit=abc→ uses the default500page=0→ adjusted to1page=2.7→ adjusted to2
Paginated Response Example
{
"data": [...],
"meta": {
"page": 1,
"limit": 500,
"hasNextPage": false,
"hasPreviousPage": false
}
}Cache
Cache Configuration
The API uses automatic caching to optimize performance:
- Cache duration: 30 minutes (1800 seconds)
Caching Strategies
-
Query cache
- Complex queries are cached automatically
- The cache is invalidated after 30 minutes or when the data is updated
-
Client-side cache
- Implement a local cache for static data
- Use an appropriate TTL (Time To Live)
- Invalidate the cache when needed
General Best Practices
1. Error Handling
Always handle possible API errors:
async function makeRequest(url, clientId, clientSecret) {
try {
const response = await fetch(url, {
headers: {
'x-client-id': clientId,
'x-client-secret': clientSecret,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
throw new Error(`Rate limit exceeded. Wait ${retryAfter} seconds.`);
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error calling the API:', error);
// Implement retry or fallback logic
throw error;
}
}2. Retry with Exponential Backoff
async function makeRequestWithRetry(url, clientId, clientSecret, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, {
headers: {
'x-client-id': clientId,
'x-client-secret': clientSecret,
'Content-Type': 'application/json',
},
});
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limit reached. Waiting ${retryAfter} seconds...`);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === maxRetries - 1) {
throw error;
}
// Exponential backoff: 2^attempt seconds
const delay = Math.pow(2, attempt) * 1000;
console.log(`Attempt ${attempt + 1} failed. Waiting ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}3. Use Pagination Efficiently
-
Do not fetch all the data at once
- Use a
limitthat fits your use case (recommended: 500-1000) - Avoid a very high
limit, which can cause timeouts
- Use a
-
Implement progressive pagination (lazy loading)
// endpoint: URL including organizationId, e.g. // https://data.v4mos.ai/v1/google/ads/campaigns?organizationId=organization_123 async function* fetchAllPages(endpoint, clientId, clientSecret) { let page = 1; let hasMore = true; while (hasMore) { const url = new URL(endpoint); url.searchParams.set('page', String(page)); url.searchParams.set('limit', '500'); const response = await fetch(url, { headers: { 'x-client-id': clientId, 'x-client-secret': clientSecret, }, }); const data = await response.json(); yield data.data; hasMore = data.meta?.hasNextPage || false; page++; } }
4. Monitoring
-
Log errors and unexpected responses
const logger = { error: (message, context) => console.error(`[ERROR] ${message}`, context), warn: (message, context) => console.warn(`[WARN] ${message}`, context), info: (message, context) => console.log(`[INFO] ${message}`, context), }; -
Monitor response times
- Set up alerts for slow responses (> 5s)
- Identify endpoints with degraded performance
-
Track request quota usage
- Monitor
X-RateLimit-Remainingon every response - Set up alerts when close to the limit (e.g. < 20 requests)
- Monitor
5. Request Optimization
-
Efficient filters: Use date ranges and other filters to reduce the data returned
# Instead of fetching everything and filtering locally GET /v1/facebook/ads/campaigns?organizationId=organization_123&dateStart=2024-01-01&dateEnd=2024-12-31 -
Parallel requests: Limit concurrency so you do not exceed the rate limit
async function fetchInParallel(urls, clientId, clientSecret, maxConcurrent = 10) { const semaphore = { count: maxConcurrent }; const queue = urls.map((url) => ({ url, promise: null, })); return Promise.all( queue.map(async (item) => { while (semaphore.count === 0) { await new Promise((resolve) => setTimeout(resolve, 100)); } semaphore.count--; try { item.promise = fetch(item.url, { headers: { 'x-client-id': clientId, 'x-client-secret': clientSecret, }, }); return await item.promise; } finally { semaphore.count++; } }), ); }
6. Versioning
- Always specify the API version
- All endpoints start with
/v1/ - Watch for announcements about deprecated versions
- Plan migrations ahead of time
- All endpoints start with
7. Timeouts
Configure appropriate timeouts for your requests:
// Example with a 30-second timeout
async function fetchWithTimeout(url, clientId, clientSecret, timeout = 30000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal,
headers: {
'x-client-id': clientId,
'x-client-secret': clientSecret,
'Content-Type': 'application/json',
},
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request aborted due to timeout');
}
throw error;
}
}Timeout recommendations:
- Simple GET requests: 10-15 seconds
- Requests with large pages: 30-60 seconds
- Batch operations: 60-120 seconds
Integration Checklist
Before going to production, make sure that:
- API credentials (
client_idandclient_secret) are generated and stored securely -
organizationIdis configured correctly - Error handling is implemented (including 429)
- Rate limiting is accounted for in the architecture
- Pagination is implemented for large volumes
- Retry with exponential backoff is configured
- Logging and monitoring are configured
- Timeouts are configured appropriately
- Caching is implemented where appropriate
- Tests were run in a development environment
-
X-RateLimit-Remainingmonitoring is implemented
Troubleshooting
Common Problems
Frequent 429 errors:
- Reduce the request frequency
- Implement more aggressive caching
- Use batch endpoints when available
- Contact support for custom limits (enterprise plan)
Timeouts on large requests:
- Reduce the pagination
limit - Use more specific filters (smaller date ranges)
- Implement incremental requests
Inconsistent data:
- Check that you account for the cache (wait 30 minutes after updates)
- Use timestamps for synchronization
- Consider webhooks, if available, for real-time updates
Support
For questions about limits and best practices:
- Review usage patterns on V4MOS.AI
- Contact support for rate limit adjustments
- Request custom monitoring for specific needs