[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F7SRV7PQ2SFE6MP3FMHVAD":3,"skill-same:01M6F7SRV7PQ2SFE6MP3FMHVAD":81},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":58,"commitSha":59,"skillDirectory":60,"forksCount":61,"repositoryHtmlUrl":62,"repositoryHomepageUrl":63,"ownerHtmlUrl":64,"ownerBlogUrl":6,"ownerDescription":65,"updatedAt":66,"install":67,"resources":80},"01M6F7SRV7PQ2SFE6MP3FMHVAD","brightdata-rate-limits",null,"Implement Bright Data rate limiting, backoff, and idempotency patterns.\nUse when handling rate limit errors, implementing retry logic,\nor optimizing API request throughput for Bright Data.\nTrigger with phrases like \"brightdata rate limit\", \"brightdata throttling\",\n\"brightdata 429\", \"brightdata retry\", \"brightdata backoff\".","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbrightdata-rate-limits\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"brightdata rate limits","brightdata","use","429","backoff","retry","throughput","trigger","io","patterns","implement","scraping","edit","write","read","api","throttling","saas","data","code","2026-11-10 10:12:30",false,"---\nname: brightdata-rate-limits\ndescription: |\n  Implement Bright Data rate limiting, backoff, and idempotency patterns.\n  Use when handling rate limit errors, implementing retry logic,\n  or optimizing API request throughput for Bright Data.\n  Trigger with phrases like \"brightdata rate limit\", \"brightdata throttling\",\n  \"brightdata 429\", \"brightdata retry\", \"brightdata backoff\".\nallowed-tools: Read, Write, Edit\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\ntags: [saas, scraping, data, brightdata]\ncompatible-with: claude-code\n---\n\n# Bright Data Rate Limits\n\n## Overview\n\nHandle Bright Data rate limits and concurrent request limits. Unlike traditional API rate limits, Bright Data limits are per-zone and based on concurrent connections and requests per second. The Web Scraper API trigger endpoint is limited to 20 requests\u002Fmin and 60 requests\u002Fhour.\n\n## Prerequisites\n\n- Bright Data zone configured\n- Understanding of async\u002Fawait patterns\n- p-queue or similar concurrency library\n\n## Instructions\n\n### Step 1: Understand Bright Data Rate Limits\n\n| Product | Concurrent Limit | Per-Minute | Notes |\n|---------|-----------------|------------|-------|\n| Residential Proxy | Based on plan | No hard cap | Charged per GB |\n| Web Unlocker | Based on plan | No hard cap | Charged per request |\n| Scraping Browser | Based on plan sessions | No hard cap | Charged per session |\n| SERP API | Based on plan | No hard cap | Charged per search |\n| Web Scraper API (trigger) | N\u002FA | 20\u002Fmin, 60\u002Fhr | Async collections |\n| Datasets API | N\u002FA | 20\u002Fmin | Snapshot requests |\n\n### Step 2: Implement Concurrent Request Limiter\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Flimiter.ts\nimport PQueue from 'p-queue';\n\n\u002F\u002F Match concurrency to your Bright Data plan limits\nconst scrapeQueue = new PQueue({\n  concurrency: 10,       \u002F\u002F Max concurrent proxy requests\n  interval: 1000,        \u002F\u002F Per second\n  intervalCap: 20,       \u002F\u002F Max 20 requests per second\n  timeout: 120000,       \u002F\u002F Kill after 2 min\n  throwOnTimeout: true,\n});\n\nexport async function queuedScrape(url: string): Promise\u003Cstring> {\n  return scrapeQueue.add(async () => {\n    const client = getBrightDataClient();\n    const response = await client.get(url);\n    return response.data;\n  });\n}\n\n\u002F\u002F Monitor queue health\nscrapeQueue.on('active', () => {\n  console.log(`Queue: ${scrapeQueue.size} waiting, ${scrapeQueue.pending} active`);\n});\n```\n\n### Step 3: Exponential Backoff for Proxy Errors\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Fbackoff.ts\nexport async function scrapeWithBackoff(\n  url: string,\n  config = { maxRetries: 5, baseDelay: 2000, maxDelay: 60000 }\n): Promise\u003Cstring> {\n  for (let attempt = 0; attempt \u003C= config.maxRetries; attempt++) {\n    try {\n      const client = getBrightDataClient();\n      const response = await client.get(url);\n      return response.data;\n    } catch (error: any) {\n      const status = error.response?.status;\n      const luminatiError = error.response?.headers?.['x-luminati-error'];\n\n      \u002F\u002F Only retry on transient errors\n      const retryable = [502, 503, 429].includes(status)\n        || error.code === 'ETIMEDOUT'\n        || luminatiError === 'ip_banned';\n\n      if (attempt === config.maxRetries || !retryable) throw error;\n\n      const delay = Math.min(\n        config.baseDelay * Math.pow(2, attempt) + Math.random() * 1000,\n        config.maxDelay\n      );\n      console.log(`[${luminatiError || status}] Retry ${attempt + 1} in ${delay.toFixed(0)}ms`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n### Step 4: Web Scraper API Rate Limiter\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Ftrigger-limiter.ts — 20\u002Fmin, 60\u002Fhr for trigger endpoint\nconst triggerQueue = new PQueue({\n  concurrency: 1,\n  interval: 60000,       \u002F\u002F Per minute\n  intervalCap: 20,       \u002F\u002F 20 triggers per minute\n});\n\nlet hourlyCount = 0;\nsetInterval(() => { hourlyCount = 0; }, 3600000); \u002F\u002F Reset hourly\n\nexport async function rateLimitedTrigger(\n  datasetId: string,\n  urls: string[]\n): Promise\u003Cany> {\n  if (hourlyCount >= 55) { \u002F\u002F Leave buffer\n    throw new Error('Approaching hourly trigger limit (60\u002Fhr). Wait before triggering.');\n  }\n\n  return triggerQueue.add(async () => {\n    hourlyCount++;\n    const response = await fetch(\n      `https:\u002F\u002Fapi.brightdata.com\u002Fdatasets\u002Fv3\u002Ftrigger?dataset_id=${datasetId}&format=json`,\n      {\n        method: 'POST',\n        headers: {\n          'Authorization': `Bearer ${process.env.BRIGHTDATA_API_TOKEN}`,\n          'Content-Type': 'application\u002Fjson',\n        },\n        body: JSON.stringify(urls.map(url => ({ url }))),\n      }\n    );\n    return response.json();\n  });\n}\n```\n\n### Step 5: Batch URLs to Minimize Triggers\n\n```typescript\n\u002F\u002F Instead of triggering per-URL, batch into single triggers\nasync function batchTrigger(urls: string[], batchSize = 100) {\n  const batches = [];\n  for (let i = 0; i \u003C urls.length; i += batchSize) {\n    batches.push(urls.slice(i, i + batchSize));\n  }\n\n  console.log(`Triggering ${urls.length} URLs in ${batches.length} batches`);\n\n  for (const batch of batches) {\n    await rateLimitedTrigger('gd_dataset_id', batch);\n  }\n}\n```\n\n## Output\n\n- Concurrent request limiter matching plan limits\n- Exponential backoff handling X-Luminati error headers\n- Web Scraper API trigger rate limiter (20\u002Fmin, 60\u002Fhr)\n- URL batching to minimize trigger count\n\n## Error Handling\n\n| Signal | Meaning | Action |\n|--------|---------|--------|\n| HTTP 429 | Concurrent limit exceeded | Queue requests with p-queue |\n| HTTP 502 + `ip_banned` | IP blocked by target | Retry (auto-rotates IP) |\n| HTTP 502 + `target_site_blocked` | Anti-bot blocked | Switch to Scraping Browser |\n| `ETIMEDOUT` | Connection timeout | Retry with longer timeout |\n| Hourly trigger limit | 60 triggers\u002Fhr exceeded | Batch URLs into fewer triggers |\n\n## Resources\n\n- [Bright Data Proxy Limits](https:\u002F\u002Fdocs.brightdata.com\u002Fgeneral\u002Faccount\u002Fproxy-infrastructure)\n- [Web Scraper API Limits](https:\u002F\u002Fdocs.brightdata.com\u002Fscraping-automation\u002Fweb-data-apis\u002Fweb-scraper-api\u002Ftrigger-a-collection)\n- [p-queue Documentation](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fp-queue)\n\n## Next Steps\n\nFor security configuration, see `brightdata-security-basics`.\n\n","# Bright Data Rate Limits\n\n## Overview\n\nHandle Bright Data rate limits and concurrent request limits. Unlike traditional API rate limits, Bright Data limits are per-zone and based on concurrent connections and requests per second. The Web Scraper API trigger endpoint is limited to 20 requests\u002Fmin and 60 requests\u002Fhour.\n\n## Prerequisites\n\n- Bright Data zone configured\n- Understanding of async\u002Fawait patterns\n- p-queue or similar concurrency library\n\n## Instructions\n\n### Step 1: Understand Bright Data Rate Limits\n\n| Product | Concurrent Limit | Per-Minute | Notes |\n|---------|-----------------|------------|-------|\n| Residential Proxy | Based on plan | No hard cap | Charged per GB |\n| Web Unlocker | Based on plan | No hard cap | Charged per request |\n| Scraping Browser | Based on plan sessions | No hard cap | Charged per session |\n| SERP API | Based on plan | No hard cap | Charged per search |\n| Web Scraper API (trigger) | N\u002FA | 20\u002Fmin, 60\u002Fhr | Async collections |\n| Datasets API | N\u002FA | 20\u002Fmin | Snapshot requests |\n\n### Step 2: Implement Concurrent Request Limiter\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Flimiter.ts\nimport PQueue from 'p-queue';\n\n\u002F\u002F Match concurrency to your Bright Data plan limits\nconst scrapeQueue = new PQueue({\n  concurrency: 10,       \u002F\u002F Max concurrent proxy requests\n  interval: 1000,        \u002F\u002F Per second\n  intervalCap: 20,       \u002F\u002F Max 20 requests per second\n  timeout: 120000,       \u002F\u002F Kill after 2 min\n  throwOnTimeout: true,\n});\n\nexport async function queuedScrape(url: string): Promise\u003Cstring> {\n  return scrapeQueue.add(async () => {\n    const client = getBrightDataClient();\n    const response = await client.get(url);\n    return response.data;\n  });\n}\n\n\u002F\u002F Monitor queue health\nscrapeQueue.on('active', () => {\n  console.log(`Queue: ${scrapeQueue.size} waiting, ${scrapeQueue.pending} active`);\n});\n```\n\n### Step 3: Exponential Backoff for Proxy Errors\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Fbackoff.ts\nexport async function scrapeWithBackoff(\n  url: string,\n  config = { maxRetries: 5, baseDelay: 2000, maxDelay: 60000 }\n): Promise\u003Cstring> {\n  for (let attempt = 0; attempt \u003C= config.maxRetries; attempt++) {\n    try {\n      const client = getBrightDataClient();\n      const response = await client.get(url);\n      return response.data;\n    } catch (error: any) {\n      const status = error.response?.status;\n      const luminatiError = error.response?.headers?.['x-luminati-error'];\n\n      \u002F\u002F Only retry on transient errors\n      const retryable = [502, 503, 429].includes(status)\n        || error.code === 'ETIMEDOUT'\n        || luminatiError === 'ip_banned';\n\n      if (attempt === config.maxRetries || !retryable) throw error;\n\n      const delay = Math.min(\n        config.baseDelay * Math.pow(2, attempt) + Math.random() * 1000,\n        config.maxDelay\n      );\n      console.log(`[${luminatiError || status}] Retry ${attempt + 1} in ${delay.toFixed(0)}ms`);\n      await new Promise(r => setTimeout(r, delay));\n    }\n  }\n  throw new Error('Unreachable');\n}\n```\n\n### Step 4: Web Scraper API Rate Limiter\n\n```typescript\n\u002F\u002F src\u002Fbrightdata\u002Ftrigger-limiter.ts — 20\u002Fmin, 60\u002Fhr for trigger endpoint\nconst triggerQueue = new PQueue({\n  concurrency: 1,\n  interval: 60000,       \u002F\u002F Per minute\n  intervalCap: 20,       \u002F\u002F 20 triggers per minute\n});\n\nlet hourlyCount = 0;\nsetInterval(() => { hourlyCount = 0; }, 3600000); \u002F\u002F Reset hourly\n\nexport async function rateLimitedTrigger(\n  datasetId: string,\n  urls: string[]\n): Promise\u003Cany> {\n  if (hourlyCount >= 55) { \u002F\u002F Leave buffer\n    throw new Error('Approaching hourly trigger limit (60\u002Fhr). Wait before triggering.');\n  }\n\n  return triggerQueue.add(async () => {\n    hourlyCount++;\n    const response = await fetch(\n      `https:\u002F\u002Fapi.brightdata.com\u002Fdatasets\u002Fv3\u002Ftrigger?dataset_id=${datasetId}&format=json`,\n      {\n        method: 'POST',\n        headers: {\n          'Authorization': `Bearer ${process.env.BRIGHTDATA_API_TOKEN}`,\n          'Content-Type': 'application\u002Fjson',\n        },\n        body: JSON.stringify(urls.map(url => ({ url }))),\n      }\n    );\n    return response.json();\n  });\n}\n```\n\n### Step 5: Batch URLs to Minimize Triggers\n\n```typescript\n\u002F\u002F Instead of triggering per-URL, batch into single triggers\nasync function batchTrigger(urls: string[], batchSize = 100) {\n  const batches = [];\n  for (let i = 0; i \u003C urls.length; i += batchSize) {\n    batches.push(urls.slice(i, i + batchSize));\n  }\n\n  console.log(`Triggering ${urls.length} URLs in ${batches.length} batches`);\n\n  for (const batch of batches) {\n    await rateLimitedTrigger('gd_dataset_id', batch);\n  }\n}\n```\n\n## Output\n\n- Concurrent request limiter matching plan limits\n- Exponential backoff handling X-Luminati error headers\n- Web Scraper API trigger rate limiter (20\u002Fmin, 60\u002Fhr)\n- URL batching to minimize trigger count\n\n## Error Handling\n\n| Signal | Meaning | Action |\n|--------|---------|--------|\n| HTTP 429 | Concurrent limit exceeded | Queue requests with p-queue |\n| HTTP 502 + `ip_banned` | IP blocked by target | Retry (auto-rotates IP) |\n| HTTP 502 + `target_site_blocked` | Anti-bot blocked | Switch to Scraping Browser |\n| `ETIMEDOUT` | Connection timeout | Retry with longer timeout |\n| Hourly trigger limit | 60 triggers\u002Fhr exceeded | Batch URLs into fewer triggers |\n\n## Resources\n\n- [Bright Data Proxy Limits](https:\u002F\u002Fdocs.brightdata.com\u002Fgeneral\u002Faccount\u002Fproxy-infrastructure)\n- [Web Scraper API Limits](https:\u002F\u002Fdocs.brightdata.com\u002Fscraping-automation\u002Fweb-data-apis\u002Fweb-scraper-api\u002Ftrigger-a-collection)\n- [p-queue Documentation](https:\u002F\u002Fgithub.com\u002Fsindresorhus\u002Fp-queue)\n\n## Next Steps\n\nFor security configuration, see `brightdata-security-basics`.\n\n",{"name":5,"tags":52,"author":53,"license":15,"version":54,"description":55,"allowed-tools":56,"compatible-with":57},[44,38,45,28],"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Implement Bright Data rate limiting, backoff, and idempotency patterns.\nUse when handling rate limit errors, implementing retry logic,\nor optimizing API request throughput for Bright Data.\nTrigger with phrases like \"brightdata rate limit\", \"brightdata throttling\",\n\"brightdata 429\", \"brightdata retry\", \"brightdata backoff\".\n","Read, Write, Edit","claude-code","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbrightdata-rate-limits",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":79},[69,72,75],{"tool":70,"command":71},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill brightdata-rate-limits",{"tool":73,"command":74},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill brightdata-rate-limits",{"tool":76,"command":77},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill brightdata-rate-limits","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fjeremylongshore\u002Fclaude-code-plugins-plus-skills\u002Fbrightdata-rate-limits\u002FSKILL.md",true,[],[82,96,106,115,121,128],{"skillNo":83,"name":84,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":92,"publishedAt":47},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[87],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[93,94,95],"model","model hierarchy","model hierarchy skill",{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":102,"publishedAt":47},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[101],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},[103,104,105],"MCP","zoom","zoom mcp",{"skillNo":107,"name":108,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":47},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[112,113,114,104],"sdk","zoom apps sdk","apps",{"skillNo":116,"name":117,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":47},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[117],{"skillNo":122,"name":123,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":127,"publishedAt":47},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[126],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},[123],{"skillNo":129,"name":130,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":133,"publishedAt":47},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[130]]