Rate Limits

Understand API rate limits, monitoring, best practices, and how to handle rate limit errors effectively

Rate limits

The GetPaidHQ API implements rate limiting to ensure fair usage and maintain service quality for all customers. Rate limits are applied per API key and reset every minute.

Current limits

Rate limits vary by plan tier:

PlanRequests per minuteBurst allowance
Starter500100
Standard1,000200
Pro5,0001,000
EnterpriseCustomCustom

Rate limit headers

Every API response includes rate limit information in the headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1642681260
X-RateLimit-Burst-Limit: 200
X-RateLimit-Burst-Remaining: 150

Header descriptions

  • X-RateLimit-Limit: Maximum requests per minute for your plan
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when the limit resets
  • X-RateLimit-Burst-Limit: Maximum burst requests allowed
  • X-RateLimit-Burst-Remaining: Burst requests remaining

Rate limit exceeded

When you exceed your rate limit, the API returns an HTTP 429 status code:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642681320
Retry-After: 60

{
  "error": {
    "type": "rate_limit_error",
    "message": "Rate limit exceeded. Retry after 60 seconds."
  }
}

Best practices

Implement exponential backoff

When you receive a 429 response, implement exponential backoff:

async function makeRequestWithBackoff(apiCall, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      if (error.status === 429 && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}

Monitor rate limit headers

Track your usage to avoid hitting limits:

function checkRateLimit(response) {
  const remaining = parseInt(response.headers['x-ratelimit-remaining']);
  const reset = parseInt(response.headers['x-ratelimit-reset']);
  
  if (remaining < 10) {
    const resetTime = new Date(reset * 1000);
    console.warn(`Rate limit low: ${remaining} requests remaining until ${resetTime}`);
  }
}

Batch operations efficiently

Group related operations to reduce API calls:

// ❌ Inefficient: Multiple API calls
for (const customer of customers) {
  await getpaidhq.customers.retrieve(customer.id);
}

// ✅ Efficient: Single batch call
const customerIds = customers.map(c => c.id);
const customers = await getpaidhq.customers.list({
  ids: customerIds,
  limit: 100
});

Use webhooks instead of polling

Instead of frequently polling for updates, use webhooks:

// ❌ Inefficient: Polling every minute
setInterval(async () => {
  const invoices = await getpaidhq.invoices.list({
    status: 'unpaid',
    limit: 100
  });
  processUnpaidInvoices(invoices);
}, 60000);

// ✅ Efficient: Use webhooks
app.post('/webhooks/getpaidhq', (req, res) => {
  const event = req.body;
  if (event.type === 'invoice.payment_failed') {
    processFailedPayment(event.data);
  }
  res.status(200).send('OK');
});

Cache frequently accessed data

Cache data that doesn't change often:

const cache = new Map();
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

async function getCustomer(customerId) {
  const cacheKey = `customer_${customerId}`;
  const cached = cache.get(cacheKey);
  
  if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
    return cached.data;
  }
  
  const customer = await getpaidhq.customers.retrieve(customerId);
  cache.set(cacheKey, {
    data: customer,
    timestamp: Date.now()
  });
  
  return customer;
}

Enterprise rate limits

Enterprise customers can request custom rate limits based on their usage patterns:

  • Higher base limits: Up to 50,000 requests per minute
  • Burst capacity: Custom burst allowances for traffic spikes
  • Dedicated endpoints: Optional dedicated API endpoints
  • Priority support: Faster response times for rate limit issues

Contact your account manager to discuss custom rate limits.

Rate limiting by endpoint

Some endpoints have additional limits:

Webhook endpoints

  • Creation: 10 webhooks per minute
  • Updates: 20 webhook updates per minute
  • Deletion: 5 webhook deletions per minute

File uploads

  • Invoice uploads: 50 files per minute
  • Document uploads: 100 files per minute
  • Size limits: 10MB per file

Bulk operations

  • Batch imports: 5 operations per minute
  • Bulk updates: 10 operations per minute
  • Export requests: 3 requests per minute

Monitoring and alerts

Dashboard monitoring

Monitor your API usage in the GetPaidHQ dashboard:

  • Real-time usage: Current rate limit consumption
  • Historical data: Usage patterns over time
  • Alert thresholds: Configure alerts at 80% and 90% usage
  • Usage analytics: Identify optimization opportunities

API monitoring

Implement monitoring in your application:

// Track rate limit metrics
const rateLimitMetrics = {
  requestsPerMinute: 0,
  burstUsage: 0,
  throttledRequests: 0
};

function trackRateLimit(response) {
  rateLimitMetrics.requestsPerMinute++;
  
  const remaining = parseInt(response.headers['x-ratelimit-remaining']);
  const burstRemaining = parseInt(response.headers['x-ratelimit-burst-remaining']);
  
  if (remaining < 50) {
    // Alert: Approaching rate limit
    sendAlert('Rate limit warning', { remaining });
  }
  
  if (response.status === 429) {
    rateLimitMetrics.throttledRequests++;
    sendAlert('Rate limit exceeded', { timestamp: Date.now() });
  }
}

Error handling

Handle rate limit errors gracefully:

import { GetPaidHQError } from '@getpaidhq/node';

try {
  const customer = await getpaidhq.customers.create(customerData);
} catch (error) {
  if (error instanceof GetPaidHQError && error.type === 'rate_limit_error') {
    // Rate limit exceeded
    const retryAfter = error.headers['retry-after'];
    console.log(`Rate limited. Retry after ${retryAfter} seconds`);
    
    // Implement retry logic or queue the request
    await scheduleRetry(customerData, retryAfter);
  } else {
    throw error;
  }
}

Getting help

If you're experiencing rate limiting issues:

  1. Review your usage patterns in the dashboard
  2. Implement the best practices outlined above
  3. Consider upgrading your plan for higher limits
  4. Contact support for custom limit discussions

For Enterprise customers, contact your dedicated support team for immediate assistance with rate limiting concerns.