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

  1. Implement retry with exponential backoff

    • Wait for the time given in the Retry-After header
    • Use an increasing delay between attempts
  2. Spread requests over time

    • Avoid request bursts
    • Use queues for batch operations
  3. Cache data that does not change often

    • The API caches queries for 30 minutes
    • Avoid redundant requests
  4. Group multiple operations when possible

    • Use endpoints that support batch operations
    • Combine filters to reduce the number of calls

Pagination

Parameters

ParameterDescriptionMinimumMaximumDefault
limitNumber of records per page15000500
pagePage number of the results1—1

How to Use

GET /v1/google/ads/campaigns?organizationId=organization_123&limit=1000&page=2

Automatic 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 to 1
  • limit=6000 → adjusted to 5000
  • limit=abc → uses the default 500
  • page=0 → adjusted to 1
  • page=2.7 → adjusted to 2

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

  1. Query cache

    • Complex queries are cached automatically
    • The cache is invalidated after 30 minutes or when the data is updated
  2. 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 limit that fits your use case (recommended: 500-1000)
    • Avoid a very high limit, which can cause timeouts
  • 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-Remaining on every response
    • Set up alerts when close to the limit (e.g. < 20 requests)

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

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_id and client_secret) are generated and stored securely
  • organizationId is 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-Remaining monitoring 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

On this page