{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Rate Limiting","description":"Sprinklr offers robust, convenient, and simple RESTful Web service APIs to integrate data from Sprinklr to any external system.","keywords":["Sprinklr API","Sprinklr Developer Portal","OAuth 2.0","RESTful API","Social Media API","Customer Experience API","Unified-CXM","Social Listening API"]},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"rate-limiting","__idx":0},"children":["Rate Limiting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Sprinklr API implements rate limiting to ensure fair usage and maintain optimal performance for all users. Understanding how rate limits work and how to work within them is essential for building reliable, production-ready integrations."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"why-rate-limiting-matters","__idx":1},"children":["Why rate limiting matters"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rate limiting protects the API infrastructure and ensures that all customers get consistent, reliable performance. Without rate limits, a single integration making too many requests could degrade performance for everyone."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["For your integration, rate limits mean:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You need to design for efficiency from the start"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You should implement retry logic and backoff strategies"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You'll want to monitor your usage to avoid hitting limits"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You may need to cache data and batch operations strategically"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The good news? With the right approach, you'll rarely hit rate limits, and when you do, your application will handle it gracefully and automatically recover."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Think of rate limiting like traffic lights on a busy highway. They prevent congestion and keep everything moving smoothly for everyone. Your job is to time your acceleration and plan your route efficiently."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-sprinklr-api-rate-limiting-works","__idx":2},"children":["How Sprinklr API rate limiting works"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Sprinklr API uses a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["sliding window"]}," rate limit algorithm. This means your rate limit is calculated based on your requests over a rolling time period, not a fixed window that resets at specific times."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example of sliding window:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"If your limit is 60 requests per minute:\n- At 10:00:00, you make 60 requests\n- At 10:00:30, you can make more requests (oldest ones are now outside the 1-minute window)\n- You don't have to wait until 10:01:00 to make more requests\n\nCompare to fixed window:\n- At 10:00:00, you make 60 requests\n- You must wait until 10:01:00 before making any more requests\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This sliding window approach is more forgiving and allows for smoother request distribution."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"understanding-rate-limit-headers","__idx":3},"children":["Understanding rate limit headers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Every API response"]}," includes three headers that tell you your current rate limit status:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"HTTP/1.1 200 OK\nX-RateLimit-Limit: 1000\nX-RateLimit-Remaining: 847\nX-RateLimit-Reset: 1709564820\nContent-Type: application/json\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Header"},"children":["Header"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Example"},"children":["Example"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-RateLimit-Limit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Maximum requests allowed in the current time window"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1000"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-RateLimit-Remaining"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Number of requests you can still make in this window"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["847"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-RateLimit-Reset"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unix timestamp (seconds) when the limit fully resets"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1709564820"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"reading-rate-limit-headers-in-code","__idx":4},"children":["Reading rate limit headers in code"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Node.js:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const response = await fetch('https://api-v3.sprinklr.com/api/v3/account', {\n  headers: {\n    'Authorization': `Bearer ${accessToken}`,\n    'Key': apiKey\n  }\n});\n\nconst limit = response.headers.get('X-RateLimit-Limit');\nconst remaining = response.headers.get('X-RateLimit-Remaining');\nconst reset = response.headers.get('X-RateLimit-Reset');\n\nconsole.log(`Rate limit: ${remaining}/${limit} requests remaining`);\nconsole.log(`Resets at: ${new Date(reset * 1000).toISOString()}`);\n\n// Check if you're getting close to the limit\nif (remaining < limit * 0.1) {  // Less than 10% remaining\n  console.warn('Approaching rate limit! Consider slowing down requests.');\n}\n\nconst data = await response.json();\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Python:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import requests\nfrom datetime import datetime\n\nresponse = requests.get(\n    'https://api-v3.sprinklr.com/api/v3/account',\n    headers={\n        'Authorization': f'Bearer {access_token}',\n        'Key': api_key\n    }\n)\n\nlimit = int(response.headers['X-RateLimit-Limit'])\nremaining = int(response.headers['X-RateLimit-Remaining'])\nreset = int(response.headers['X-RateLimit-Reset'])\n\nprint(f'Rate limit: {remaining}/{limit} requests remaining')\nprint(f'Resets at: {datetime.fromtimestamp(reset).isoformat()}')\n\n# Check if approaching limit\nif remaining < limit * 0.1:\n    print('WARNING: Approaching rate limit!')\n\ndata = response.json()\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["PHP:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n$ch = curl_init('https://api-v3.sprinklr.com/api/v3/account');\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\n    'Authorization: Bearer ' . $accessToken,\n    'Key: ' . $apiKey\n]);\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_HEADER, true);\n\n$response = curl_exec($ch);\n$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);\n$headers = substr($response, 0, $headerSize);\n$body = substr($response, $headerSize);\n\n// Parse headers\npreg_match('/X-RateLimit-Limit: (\\d+)/', $headers, $limitMatch);\npreg_match('/X-RateLimit-Remaining: (\\d+)/', $headers, $remainingMatch);\npreg_match('/X-RateLimit-Reset: (\\d+)/', $headers, $resetMatch);\n\n$limit = $limitMatch[1];\n$remaining = $remainingMatch[1];\n$reset = $resetMatch[1];\n\necho \"Rate limit: {$remaining}/{$limit} requests remaining\\n\";\necho \"Resets at: \" . date('Y-m-d H:i:s', $reset) . \"\\n\";\n\nif ($remaining < $limit * 0.1) {\n    echo \"WARNING: Approaching rate limit!\\n\";\n}\n\n$data = json_decode($body, true);\n?>\n","lang":"php"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Java:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","header":{"controls":{"copy":{}}},"source":"import java.net.http.*;\nimport java.time.Instant;\n\nHttpClient client = HttpClient.newHttpClient();\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api-v3.sprinklr.com/api/v3/account\"))\n    .header(\"Authorization\", \"Bearer \" + accessToken)\n    .header(\"Key\", apiKey)\n    .build();\n\nHttpResponse<String> response = client.send(request,\n    HttpResponse.BodyHandlers.ofString());\n\nint limit = Integer.parseInt(\n    response.headers().firstValue(\"X-RateLimit-Limit\").orElse(\"0\"));\nint remaining = Integer.parseInt(\n    response.headers().firstValue(\"X-RateLimit-Remaining\").orElse(\"0\"));\nlong reset = Long.parseLong(\n    response.headers().firstValue(\"X-RateLimit-Reset\").orElse(\"0\"));\n\nSystem.out.println(\"Rate limit: \" + remaining + \"/\" + limit +\n    \" requests remaining\");\nSystem.out.println(\"Resets at: \" +\n    Instant.ofEpochSecond(reset).toString());\n\n// Check if approaching limit\nif (remaining < limit * 0.1) {\n    System.out.println(\"WARNING: Approaching rate limit!\");\n}\n\nString data = response.body();\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"rate-limit-tiers","__idx":5},"children":["Rate limit tiers"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your rate limit depends on your Sprinklr account tier. Different tiers get different request allowances to match their usage needs."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Tier"},"children":["Tier"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Requests per Minute"},"children":["Requests per Minute"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Requests per Hour"},"children":["Requests per Hour"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Daily Requests"},"children":["Daily Requests"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Best For"},"children":["Best For"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Standard"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["60"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["3,000"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["72,000"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Small teams, development/testing"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Professional"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["120"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["7,200"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["172,800"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Mid-size integrations, scheduled jobs"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Enterprise"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["300"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["18,000"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["432,000"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["High-volume production systems"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Custom"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Custom"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Contact your account manager"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"how-to-check-your-tier","__idx":6},"children":["How to check your tier"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your tier is determined by your Sprinklr account settings. Check with your account administrator or contact your Sprinklr account manager to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Find out your current tier"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Understand what usage patterns fit your tier"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Upgrade your tier if you're consistently hitting limits"]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," Rate limits are applied ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["per access token"]},", not per account. If you have multiple integrations, each with its own token, they each get the full rate limit."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-happens-when-you-hit-the-rate-limit","__idx":7},"children":["What happens when you hit the rate limit"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you exceed your rate limit, you'll receive an HTTP ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429 Too Many Requests"]}," response:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"HTTP/1.1 429 Too Many Requests\nX-RateLimit-Limit: 60\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1709564880\nRetry-After: 15\nContent-Type: application/json\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"success\": false,\n  \"errors\": [\n    {\n      \"code\": \"RATE_LIMIT_EXCEEDED\",\n      \"message\": \"Rate limit exceeded. You have made too many requests in the current time window.\",\n      \"details\": {\n        \"limit\": 60,\n        \"window\": \"1 minute\",\n        \"resetAt\": \"2024-03-04T16:48:00Z\"\n      }\n    }\n  ]\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["What to do when you get a 429:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Don't panic"]}," - This is a normal part of API usage"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Retry-After"]}," header"]}," - Tells you how many seconds to wait"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Wait the recommended time"]}," - Or use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-RateLimit-Reset"]}," timestamp"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retry the request"]}," - It should succeed after waiting"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Review your request patterns"]}," - Consider optimization strategies"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"strategies-to-avoid-hitting-rate-limits","__idx":8},"children":["Strategies to avoid hitting rate limits"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"1-use-batch-operations-most-effective","__idx":9},"children":["1. Use batch operations (most effective)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The single most effective way to stay under rate limits is to use batch endpoints whenever possible."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["❌ Don't make individual requests:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// 50 API calls, takes 50 of your rate limit\nconst accounts = [];\nfor (const accountId of accountIds) {\n  const response = await fetch(\n    `https://api-v3.sprinklr.com/api/v3/account?accountId=${accountId}`,\n    { headers: { 'Authorization': `Bearer ${token}`, 'Key': key } }\n  );\n  const data = await response.json();\n  accounts.push(data.data);\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["✅ Do use batch endpoints:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// 1 API call, takes 1 of your rate limit\nconst response = await fetch(\n  'https://api-v3.sprinklr.com/api/v3/account/batch',\n  {\n    method: 'POST',\n    headers: {\n      'Authorization': `Bearer ${token}`,\n      'Key': key,\n      'Content-Type': 'application/json'\n    },\n    body: JSON.stringify({\n      accountIds: accountIds  // Up to 50 IDs\n    })\n  }\n);\nconst data = await response.json();\nconst accounts = data.data;\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Impact:"]}," 50x reduction in API calls. If you're on the Standard tier (60 requests/minute), that's the difference between processing 60 accounts per minute vs 3,000 accounts per minute."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"2-implement-intelligent-caching","__idx":10},"children":["2. Implement intelligent caching"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Cache responses for data that doesn't change frequently. Not everything needs to be fetched in real-time."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Node.js caching example:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"class SprinklrCache {\n  constructor() {\n    this.cache = new Map();\n    this.ttls = new Map();\n  }\n\n  set(key, value, ttlSeconds = 300) {  // Default 5 minutes\n    this.cache.set(key, value);\n    this.ttls.set(key, Date.now() + (ttlSeconds * 1000));\n  }\n\n  get(key) {\n    const ttl = this.ttls.get(key);\n\n    // Check if expired\n    if (!ttl || Date.now() > ttl) {\n      this.cache.delete(key);\n      this.ttls.delete(key);\n      return null;\n    }\n\n    return this.cache.get(key);\n  }\n\n  clear() {\n    this.cache.clear();\n    this.ttls.clear();\n  }\n}\n\n// Usage\nconst cache = new SprinklrCache();\n\nasync function getAccount(accountId) {\n  // Check cache first\n  const cacheKey = `account:${accountId}`;\n  const cached = cache.get(cacheKey);\n  if (cached) {\n    console.log('Cache hit!');\n    return cached;\n  }\n\n  // Fetch from API\n  console.log('Cache miss, fetching from API...');\n  const response = await fetch(\n    `https://api-v3.sprinklr.com/api/v3/account?accountId=${accountId}`,\n    {\n      headers: {\n        'Authorization': `Bearer ${accessToken}`,\n        'Key': apiKey\n      }\n    }\n  );\n  const data = await response.json();\n\n  // Cache for 5 minutes\n  cache.set(cacheKey, data.data, 300);\n\n  return data.data;\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["What to cache and for how long:"]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Data Type"},"children":["Data Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Recommended TTL"},"children":["Recommended TTL"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Rationale"},"children":["Rationale"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Account metadata"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["30-60 minutes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Changes infrequently"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User profiles"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["15-30 minutes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["May change but not constantly"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Messages/posts"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1-5 minutes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["More dynamic content"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cases"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1-2 minutes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Changes as agents work on them"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Configuration data"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1-24 hours"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rarely changes"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3-implement-request-queuing","__idx":11},"children":["3. Implement request queuing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Queue your requests to ensure you never exceed your rate limit, even during traffic spikes."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Production-ready request queue:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"class RateLimitedQueue {\n  constructor(requestsPerMinute = 60) {\n    this.requestsPerMinute = requestsPerMinute;\n    this.queue = [];\n    this.requestTimes = [];\n    this.processing = false;\n  }\n\n  async add(requestFn) {\n    return new Promise((resolve, reject) => {\n      this.queue.push({ requestFn, resolve, reject });\n      this.process();\n    });\n  }\n\n  async process() {\n    // Prevent multiple process loops\n    if (this.processing) return;\n    this.processing = true;\n\n    while (this.queue.length > 0) {\n      // Clean up old request times (older than 1 minute)\n      const oneMinuteAgo = Date.now() - 60000;\n      this.requestTimes = this.requestTimes.filter(time => time > oneMinuteAgo);\n\n      // Check if we can make another request\n      if (this.requestTimes.length >= this.requestsPerMinute) {\n        // Calculate wait time until oldest request expires\n        const oldestRequest = this.requestTimes[0];\n        const waitTime = 60000 - (Date.now() - oldestRequest) + 100; // +100ms buffer\n\n        console.log(`Rate limit reached. Waiting ${waitTime}ms...`);\n        await new Promise(resolve => setTimeout(resolve, waitTime));\n        continue;\n      }\n\n      // Process next request\n      const { requestFn, resolve, reject } = this.queue.shift();\n      this.requestTimes.push(Date.now());\n\n      try {\n        const result = await requestFn();\n        resolve(result);\n      } catch (error) {\n        reject(error);\n      }\n\n      // Small delay between requests to be gentle\n      await new Promise(resolve => setTimeout(resolve, 50));\n    }\n\n    this.processing = false;\n  }\n\n  getQueueSize() {\n    return this.queue.length;\n  }\n\n  getRecentRequestCount() {\n    const oneMinuteAgo = Date.now() - 60000;\n    return this.requestTimes.filter(time => time > oneMinuteAgo).length;\n  }\n}\n\n// Usage\nconst queue = new RateLimitedQueue(60); // Standard tier: 60 req/min\n\n// All requests go through the queue\nasync function fetchAccount(accountId) {\n  return queue.add(async () => {\n    const response = await fetch(\n      `https://api-v3.sprinklr.com/api/v3/account?accountId=${accountId}`,\n      {\n        headers: {\n          'Authorization': `Bearer ${accessToken}`,\n          'Key': apiKey\n        }\n      }\n    );\n    return response.json();\n  });\n}\n\n// Make 100 requests - they'll be automatically rate-limited\nconst promises = accountIds.slice(0, 100).map(id => fetchAccount(id));\nconst results = await Promise.all(promises);\nconsole.log(`Processed ${results.length} accounts without hitting rate limit`);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"4-implement-exponential-backoff","__idx":12},"children":["4. Implement exponential backoff"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you do hit a rate limit (or any temporary error), retry with exponential backoff."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exponential backoff implementation:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"async function withExponentialBackoff(fn, maxRetries = 5) {\n  let lastError;\n\n  for (let attempt = 0; attempt < maxRetries; attempt++) {\n    try {\n      return await fn();\n    } catch (error) {\n      lastError = error;\n\n      // Check if it's a rate limit error\n      if (error.response?.status === 429) {\n        const resetHeader = error.response.headers.get('X-RateLimit-Reset');\n        const retryAfter = error.response.headers.get('Retry-After');\n\n        let waitTime;\n        if (retryAfter) {\n          // Use Retry-After header if provided\n          waitTime = parseInt(retryAfter) * 1000;\n        } else if (resetHeader) {\n          // Calculate from reset timestamp\n          waitTime = (parseInt(resetHeader) * 1000) - Date.now();\n        } else {\n          // Exponential backoff: 1s, 2s, 4s, 8s, 16s\n          waitTime = Math.min(Math.pow(2, attempt) * 1000, 30000);\n        }\n\n        console.log(`Rate limit hit. Waiting ${waitTime}ms before retry ${attempt + 1}/${maxRetries}`);\n        await new Promise(resolve => setTimeout(resolve, waitTime));\n        continue;\n      }\n\n      // For other errors, use exponential backoff\n      if (attempt < maxRetries - 1) {\n        const waitTime = Math.pow(2, attempt) * 1000;\n        console.log(`Request failed. Waiting ${waitTime}ms before retry ${attempt + 1}/${maxRetries}`);\n        await new Promise(resolve => setTimeout(resolve, waitTime));\n      }\n    }\n  }\n\n  throw lastError;\n}\n\n// Usage\nconst account = await withExponentialBackoff(async () => {\n  const response = await fetch(\n    `https://api-v3.sprinklr.com/api/v3/account?accountId=${accountId}`,\n    {\n      headers: {\n        'Authorization': `Bearer ${accessToken}`,\n        'Key': apiKey\n      }\n    }\n  );\n\n  if (!response.ok) {\n    const error = new Error(`HTTP ${response.status}`);\n    error.response = response;\n    throw error;\n  }\n\n  return response.json();\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"5-reduce-polling-frequency","__idx":13},"children":["5. Reduce polling frequency"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you're polling for updates, consider these alternatives:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["❌ Don't poll aggressively:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Polls every 5 seconds - uses 720 requests/hour\nsetInterval(async () => {\n  const cases = await fetchNewCases();\n  processNewCases(cases);\n}, 5000);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["✅ Do use webhooks (best option):"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Webhooks push updates to you - uses 0 API requests\napp.post('/webhooks/sprinklr/case-created', (req, res) => {\n  const caseData = req.body;\n  processNewCase(caseData);\n  res.sendStatus(200);\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["✅ Or increase polling interval:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Polls every 60 seconds - uses 60 requests/hour (12x reduction)\nsetInterval(async () => {\n  const cases = await fetchNewCases();\n  processNewCases(cases);\n}, 60000);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["✅ Or use smart polling with caching:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"// Only poll if data might have changed\nlet lastModified = null;\n\nsetInterval(async () => {\n  const response = await fetch(\n    'https://api-v3.sprinklr.com/api/v3/cases/search',\n    {\n      method: 'POST',\n      headers: {\n        'Authorization': `Bearer ${accessToken}`,\n        'Key': apiKey,\n        'If-Modified-Since': lastModified || ''\n      },\n      body: JSON.stringify({ status: 'OPEN' })\n    }\n  );\n\n  // 304 Not Modified - no new data, no processing needed\n  if (response.status === 304) {\n    console.log('No new cases');\n    return;\n  }\n\n  lastModified = response.headers.get('Last-Modified');\n  const cases = await response.json();\n  processNewCases(cases.data);\n}, 30000);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"6-distribute-requests-over-time","__idx":14},"children":["6. Distribute requests over time"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Instead of making all your requests at once, spread them out evenly."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Spread requests over time:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"async function processInBatches(items, processFn, batchSize = 10, delayMs = 1000) {\n  const results = [];\n\n  for (let i = 0; i < items.length; i += batchSize) {\n    const batch = items.slice(i, i + batchSize);\n\n    console.log(`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(items.length / batchSize)}`);\n\n    // Process batch in parallel\n    const batchResults = await Promise.all(\n      batch.map(item => processFn(item))\n    );\n\n    results.push(...batchResults);\n\n    // Wait before next batch (except for the last one)\n    if (i + batchSize < items.length) {\n      await new Promise(resolve => setTimeout(resolve, delayMs));\n    }\n  }\n\n  return results;\n}\n\n// Process 1000 accounts in batches of 10, with 1 second between batches\n// Total time: ~100 seconds, stays well under rate limit\nconst accounts = await processInBatches(\n  accountIds,\n  id => fetchAccount(id),\n  10,   // 10 at a time\n  1000  // 1 second between batches\n);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"building-a-production-ready-api-client","__idx":15},"children":["Building a production-ready API client"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's a complete example that combines all these strategies:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"class SprinklrAPIClient {\n  constructor(accessToken, apiKey, options = {}) {\n    this.accessToken = accessToken;\n    this.apiKey = apiKey;\n    this.baseUrl = 'https://api-v3.sprinklr.com';\n\n    // Configuration\n    this.requestsPerMinute = options.requestsPerMinute || 60;\n    this.cacheTTL = options.cacheTTL || 300000; // 5 minutes\n    this.maxRetries = options.maxRetries || 5;\n\n    // Components\n    this.cache = new Map();\n    this.queue = new RateLimitedQueue(this.requestsPerMinute);\n    this.metrics = {\n      totalRequests: 0,\n      cacheHits: 0,\n      cacheMisses: 0,\n      rateLimitHits: 0\n    };\n  }\n\n  async request(method, endpoint, options = {}) {\n    const { useCache = true, cacheTTL = this.cacheTTL, ...fetchOptions } = options;\n    const cacheKey = `${method}:${endpoint}:${JSON.stringify(fetchOptions.body || {})}`;\n\n    // Check cache for GET requests\n    if (method === 'GET' && useCache) {\n      const cached = this.getCached(cacheKey);\n      if (cached) {\n        this.metrics.cacheHits++;\n        console.log('Cache hit:', endpoint);\n        return cached;\n      }\n      this.metrics.cacheMisses++;\n    }\n\n    // Queue the request\n    const response = await this.queue.add(async () => {\n      return withExponentialBackoff(async () => {\n        this.metrics.totalRequests++;\n\n        const response = await fetch(`${this.baseUrl}${endpoint}`, {\n          method,\n          headers: {\n            'Authorization': `Bearer ${this.accessToken}`,\n            'Key': this.apiKey,\n            'Content-Type': 'application/json',\n            ...fetchOptions.headers\n          },\n          ...fetchOptions\n        });\n\n        // Log rate limit status\n        const remaining = response.headers.get('X-RateLimit-Remaining');\n        const limit = response.headers.get('X-RateLimit-Limit');\n        console.log(`Rate limit: ${remaining}/${limit}`);\n\n        // Warning if approaching limit\n        if (remaining && limit && parseInt(remaining) < parseInt(limit) * 0.1) {\n          console.warn('WARNING: Less than 10% of rate limit remaining!');\n        }\n\n        if (response.status === 429) {\n          this.metrics.rateLimitHits++;\n          const error = new Error('Rate limit exceeded');\n          error.response = response;\n          throw error;\n        }\n\n        if (!response.ok) {\n          const error = new Error(`HTTP ${response.status}`);\n          error.response = response;\n          throw error;\n        }\n\n        return response.json();\n      }, this.maxRetries);\n    });\n\n    // Cache successful GET responses\n    if (method === 'GET' && useCache && response.success) {\n      this.setCached(cacheKey, response, cacheTTL);\n    }\n\n    return response;\n  }\n\n  async get(endpoint, params = {}, options = {}) {\n    const queryString = new URLSearchParams(params).toString();\n    const url = queryString ? `${endpoint}?${queryString}` : endpoint;\n    return this.request('GET', url, options);\n  }\n\n  async post(endpoint, body, options = {}) {\n    return this.request('POST', endpoint, {\n      ...options,\n      body: JSON.stringify(body)\n    });\n  }\n\n  getCached(key) {\n    const cached = this.cache.get(key);\n    if (!cached) return null;\n\n    if (Date.now() > cached.expiresAt) {\n      this.cache.delete(key);\n      return null;\n    }\n\n    return cached.data;\n  }\n\n  setCached(key, data, ttl) {\n    this.cache.set(key, {\n      data,\n      expiresAt: Date.now() + ttl\n    });\n  }\n\n  clearCache() {\n    this.cache.clear();\n    console.log('Cache cleared');\n  }\n\n  getMetrics() {\n    const cacheHitRate = this.metrics.totalRequests > 0\n      ? (this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses) * 100).toFixed(2)\n      : 0;\n\n    return {\n      ...this.metrics,\n      cacheHitRate: `${cacheHitRate}%`,\n      queueSize: this.queue.getQueueSize(),\n      recentRequests: this.queue.getRecentRequestCount()\n    };\n  }\n}\n\n// Usage\nconst client = new SprinklrAPIClient(accessToken, apiKey, {\n  requestsPerMinute: 60,  // Standard tier\n  cacheTTL: 300000,       // 5 minutes\n  maxRetries: 5\n});\n\n// Make requests - all the complexity is handled automatically\ntry {\n  // GET request with automatic caching\n  const account = await client.get('/api/v3/account', { accountId: '600057112' });\n\n  // POST request (not cached)\n  const messages = await client.post('/api/v3/message/search', {\n    platform: 'TWITTER',\n    limit: 50\n  });\n\n  // Check metrics\n  console.log('Client metrics:', client.getMetrics());\n  // Output: { totalRequests: 2, cacheHits: 0, cacheMisses: 1, rateLimitHits: 0,\n  //           cacheHitRate: '0.00%', queueSize: 0, recentRequests: 2 }\n\n} catch (error) {\n  console.error('API request failed:', error);\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"monitoring-your-rate-limit-usage","__idx":16},"children":["Monitoring your rate limit usage"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tracking your API usage helps you identify optimization opportunities before you hit limits."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Simple usage tracking:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"class APIUsageTracker {\n  constructor() {\n    this.requests = [];\n  }\n\n  logRequest(endpoint, statusCode) {\n    this.requests.push({\n      endpoint,\n      statusCode,\n      timestamp: Date.now()\n    });\n\n    // Keep only last 24 hours\n    const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);\n    this.requests = this.requests.filter(r => r.timestamp > oneDayAgo);\n  }\n\n  getStats(timeWindowMinutes = 60) {\n    const cutoff = Date.now() - (timeWindowMinutes * 60 * 1000);\n    const recent = this.requests.filter(r => r.timestamp > cutoff);\n\n    const endpointCounts = {};\n    const statusCounts = {};\n\n    for (const request of recent) {\n      endpointCounts[request.endpoint] = (endpointCounts[request.endpoint] || 0) + 1;\n      statusCounts[request.statusCode] = (statusCounts[request.statusCode] || 0) + 1;\n    }\n\n    return {\n      totalRequests: recent.length,\n      timeWindow: `${timeWindowMinutes} minutes`,\n      requestsPerMinute: (recent.length / timeWindowMinutes).toFixed(2),\n      topEndpoints: Object.entries(endpointCounts)\n        .sort((a, b) => b[1] - a[1])\n        .slice(0, 5),\n      statusCodes: statusCounts,\n      rateLimitErrors: statusCounts[429] || 0\n    };\n  }\n\n  exportReport() {\n    const stats = this.getStats(60);\n    console.log('\\n=== API Usage Report (Last Hour) ===');\n    console.log(`Total requests: ${stats.totalRequests}`);\n    console.log(`Requests per minute: ${stats.requestsPerMinute}`);\n    console.log(`Rate limit errors: ${stats.rateLimitErrors}`);\n    console.log('\\nTop endpoints:');\n    stats.topEndpoints.forEach(([endpoint, count]) => {\n      console.log(`  ${endpoint}: ${count} requests`);\n    });\n    console.log('\\nStatus codes:');\n    Object.entries(stats.statusCodes).forEach(([code, count]) => {\n      console.log(`  ${code}: ${count}`);\n    });\n  }\n}\n\n// Usage\nconst tracker = new APIUsageTracker();\n\n// Log every API request\nasync function makeAPIRequest(endpoint) {\n  try {\n    const response = await fetch(`https://api-v3.sprinklr.com${endpoint}`, {\n      headers: {\n        'Authorization': `Bearer ${accessToken}`,\n        'Key': apiKey\n      }\n    });\n\n    tracker.logRequest(endpoint, response.status);\n    return response.json();\n  } catch (error) {\n    tracker.logRequest(endpoint, error.response?.status || 0);\n    throw error;\n  }\n}\n\n// Export report every hour\nsetInterval(() => {\n  tracker.exportReport();\n}, 60 * 60 * 1000);\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"optimization-checklist","__idx":17},"children":["Optimization checklist"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before deploying your integration to production, verify you've implemented these rate limit optimizations:"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"required-do-these-first","__idx":18},"children":["Required (Do these first)"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use batch endpoints"]}," wherever available (up to 50 resources per request)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement exponential backoff"]}," for retry logic"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Monitor rate limit headers"]}," in all responses"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Handle 429 errors"]}," gracefully with automatic retry"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Log rate limit metrics"]}," to track usage patterns"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recommended-do-these-for-production","__idx":19},"children":["Recommended (Do these for production)"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement request caching"]}," with appropriate TTLs"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use a request queue"]}," to prevent bursts from hitting limits"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Replace polling with webhooks"]}," where possible"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Distribute requests evenly"]}," over time instead of bursting"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Set up alerting"]}," when approaching rate limits"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"advanced-do-these-for-high-volume-systems","__idx":20},"children":["Advanced (Do these for high-volume systems)"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement circuit breakers"]}," to prevent cascading failures"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use multiple access tokens"]}," if you need higher throughput"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Optimize cache invalidation"]}," strategy"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Monitor cache hit rates"]}," and adjust TTLs"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"input","attributes":{"checked":false,"type":"checkbox","readOnly":true},"children":[]}," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Load test"]}," your integration to verify it stays under limits"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting-rate-limit-issues","__idx":21},"children":["Troubleshooting rate limit issues"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"issue-constantly-hitting-rate-limits","__idx":22},"children":["Issue: Constantly hitting rate limits"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Symptoms:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Frequent 429 errors"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Slow response times due to retries"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Requests taking much longer than expected"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solutions:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use batch operations"]}," - Are you making individual requests when you could batch?"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement caching"]}," - Are you requesting the same data repeatedly?"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Reduce polling frequency"]}," - Can you use webhooks or poll less often?"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Upgrade your tier"]}," - Contact your account manager if you've optimized but still need more capacity"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"issue-sporadic-rate-limit-errors","__idx":23},"children":["Issue: Sporadic rate limit errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Symptoms:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Occasional 429 errors"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Happens during specific operations"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Works most of the time"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solutions:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement request queuing"]}," - You're likely sending bursts of requests"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Add exponential backoff"]}," - Automatic retry will handle occasional limits"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Spread requests over time"]}," - Don't process large batches all at once"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"issue-different-rate-limits-than-expected","__idx":24},"children":["Issue: Different rate limits than expected"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Symptoms:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Getting rate limited sooner than tier indicates"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Limit headers show unexpected values"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solutions:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check your account tier"]}," - Verify with your Sprinklr administrator"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check per-endpoint limits"]}," - Some endpoints may have stricter limits"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Verify you're not sharing tokens"]}," - Each token has its own limit counter"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Contact support"]}," - There may be custom limits on your account"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":25},"children":["Next steps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Now that you understand rate limiting, explore these related topics:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/error-handling"},"children":["Error Handling"]}]}," - Handle rate limit errors and other API errors properly"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/best-practices"},"children":["Best Practices"]}]}," - More optimization strategies for production systems"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/webhooks"},"children":["Webhooks"]}]}," - Replace polling with real-time event notifications"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/api-basics"},"children":["API Basics"]}]}," - Learn about batch operations and other core patterns"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"need-help","__idx":26},"children":["Need help?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you're consistently hitting rate limits even after optimization:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Review your integration"]}," - Are you following all the strategies above?"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Contact your account manager"]}," - Discuss upgrading your tier"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Reach out to support"]}," - We can help identify optimization opportunities"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Support resources:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Technical support: ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://support.sprinklr.com"},"children":["support.sprinklr.com"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["API documentation: This guide and related docs"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Account management: Contact your Sprinklr account manager"]}]}]},"headings":[{"value":"Rate Limiting","id":"rate-limiting","depth":1},{"value":"Why rate limiting matters","id":"why-rate-limiting-matters","depth":2},{"value":"How Sprinklr API rate limiting works","id":"how-sprinklr-api-rate-limiting-works","depth":2},{"value":"Understanding rate limit headers","id":"understanding-rate-limit-headers","depth":2},{"value":"Reading rate limit headers in code","id":"reading-rate-limit-headers-in-code","depth":3},{"value":"Rate limit tiers","id":"rate-limit-tiers","depth":2},{"value":"How to check your tier","id":"how-to-check-your-tier","depth":3},{"value":"What happens when you hit the rate limit","id":"what-happens-when-you-hit-the-rate-limit","depth":2},{"value":"Strategies to avoid hitting rate limits","id":"strategies-to-avoid-hitting-rate-limits","depth":2},{"value":"1. Use batch operations (most effective)","id":"1-use-batch-operations-most-effective","depth":3},{"value":"2. Implement intelligent caching","id":"2-implement-intelligent-caching","depth":3},{"value":"3. Implement request queuing","id":"3-implement-request-queuing","depth":3},{"value":"4. Implement exponential backoff","id":"4-implement-exponential-backoff","depth":3},{"value":"5. Reduce polling frequency","id":"5-reduce-polling-frequency","depth":3},{"value":"6. Distribute requests over time","id":"6-distribute-requests-over-time","depth":3},{"value":"Building a production-ready API client","id":"building-a-production-ready-api-client","depth":2},{"value":"Monitoring your rate limit usage","id":"monitoring-your-rate-limit-usage","depth":2},{"value":"Optimization checklist","id":"optimization-checklist","depth":2},{"value":"Required (Do these first)","id":"required-do-these-first","depth":3},{"value":"Recommended (Do these for production)","id":"recommended-do-these-for-production","depth":3},{"value":"Advanced (Do these for high-volume systems)","id":"advanced-do-these-for-high-volume-systems","depth":3},{"value":"Troubleshooting rate limit issues","id":"troubleshooting-rate-limit-issues","depth":2},{"value":"Issue: Constantly hitting rate limits","id":"issue-constantly-hitting-rate-limits","depth":3},{"value":"Issue: Sporadic rate limit errors","id":"issue-sporadic-rate-limit-errors","depth":3},{"value":"Issue: Different rate limits than expected","id":"issue-different-rate-limits-than-expected","depth":3},{"value":"Next steps","id":"next-steps","depth":2},{"value":"Need help?","id":"need-help","depth":2}],"frontmatter":{"seo":{"title":"Rate Limiting"}},"lastModified":"2026-10-27T09:16:56.933Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/rate-limiting","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}