Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement Juicebox rate limiting. Trigger: "juicebox rate limit", "juicebox 429", "juicebox throttle".
.claude/skills/jeremylongshore-juicebox-rate-limits/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-04 | ✗→✓ | ▲ Improved | -28% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -43% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -25% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 21% | 0% |
Juicebox's AI-powered data analysis API enforces plan-tiered rate limits across dataset uploads, analysis triggers, and result retrieval. Heavy analytical workloads like running comparative analyses across multiple datasets or batch-processing survey results hit the analysis trigger limit first. The enrichment endpoints for augmenting datasets with external data sources have separate, lower caps, making it essential to prioritize enrichment calls and batch analysis runs during off-peak windows.
| Endpoint | Limit | Window | Scope | |----------|-------|--------|-------| | Dataset upload | 20 req | 1 minute | Per API key | | Analysis trigger | 30 req | 1 minute | Per API key | | Result retrieval | 120 req | 1 minute | Per API key | | Data enrichment | 15 req | 1 minute | Per API key | | Export download | 10 req | 1 minute | Per API key |
typescriptclass JuiceboxRateLimiter { private tokens: number; private lastRefill: number; private readonly max: number; private readonly refillRate: number; private queue: Array<{ resolve: () => void }> = []; constructor(maxPerMinute: number) { this.max = maxPerMinute; this.tokens = maxPerMinute; this.lastRefill = Date.now(); this.refillRate = maxPerMinute / 60_000; } async acquire(): Promise<void> { this.refill(); if (this.tokens >= 1) { this.tokens -= 1; return; } return new Promise(resolve => this.queue.push({ resolve })); } private refill() { const now = Date.now(); this.tokens = Math.min(this.max, this.tokens + (now - this.lastRefill) * this.refillRate); this.lastRefill = now; while (this.tokens >= 1 && this.queue.length) { this.tokens -= 1; this.queue.shift()!.resolve(); } } } const analysisLimiter = new JuiceboxRateLimiter(25); const enrichLimiter = new JuiceboxRateLimiter(12);
typescriptasync function juiceboxRetry<T>( limiter: JuiceboxRateLimiter, fn: () => Promise<Response>, maxRetries = 3 ): Promise<T> { for (let attempt = 0; attempt <= maxRetries; attempt++) { await limiter.acquire(); const res = await fn(); if (res.ok) return res.json(); if (res.status === 429) { const retryAfter = parseInt(res.headers.get("Retry-After") || "15", 10); const jitter = Math.random() * 3000; await new Promise(r => setTimeout(r, retryAfter * 1000 + jitter)); continue; } if (res.status >= 500 && attempt < maxRetries) { await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 2000)); continue; } throw new Error(`Juicebox API ${res.status}: ${await res.text()}`); } throw new Error("Max retries exceeded"); }
typescriptasync function batchAnalyzeDatasets(datasetIds: string[], query: string, batchSize = 5) { const results: any[] = []; for (let i = 0; i < datasetIds.length; i += batchSize) { const batch = datasetIds.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(id => juiceboxRetry(analysisLimiter, () => fetch(`${BASE}/api/v1/datasets/${id}/analyze`, { method: "POST", headers, body: JSON.stringify({ query }), }) )) ); results.push(...batchResults); if (i + batchSize < datasetIds.length) await new Promise(r => setTimeout(r, 8000)); } return results; }
| Issue | Cause | Fix | |-------|-------|-----| | 429 on analysis trigger | Exceeded 30 req/min analysis cap | Queue analyses, space 3s apart | | 429 on enrichment | Enrichment limit (15/min) is lowest | Batch enrichments separately with wider spacing | | Upload timeout | Dataset exceeds 50MB | Compress CSV, use chunked upload endpoint | | Analysis still processing | Complex query on large dataset | Poll status every 10s, timeout at 10 min | | 403 on export | Plan does not include export feature | Verify plan tier supports data export |
See juicebox-performance-tuning.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→fail | 33,926 | 23,532 | -31% | 1 | 1 | 0% | 5,617 | 5,120 | -9% | 0 | 0 | — |
case-01 | fail→fail | 21,371 | 19,788 | -7% | 1 | 1 | 0% | 2,503 | 4,459 | +78% | 0 | 0 | — |
case-03 | fail→pass | 28,106 | 35,973 | +28% | 1 | 1 | 0% | 3,973 | 7,277 | +83% | 0 | 0 | — |
case-04 | fail→pass | 16,809 | 6,756 | -60% | 1 | 1 | 0% | 2,076 | 1,487 | -28% | 0 | 0 | — |
case-05 | pass→pass | 13,648 | 3,606 | -74% | 1 | 1 | 0% | 1,755 | 1,850 | +5% | 0 | 0 | — |
case-06 | fail→pass | 40,445 | 7,752 | -81% | 1 | 1 | 0% | 2,867 | 1,624 | -43% | 0 | 0 | — |
case-07 | fail→pass | 13,167 | 2,327 | -82% | 1 | 1 | 0% | 2,079 | 1,553 | -25% | 0 | 0 | — |
case-08 | fail→pass | 13,844 | 7,640 | -45% | 1 | 1 | 0% | 1,364 | 1,647 | +21% | 0 | 0 | — |
case-09 | fail→pass | 19,656 | 3,180 | -84% | 1 | 1 | 0% | 2,751 | 1,803 | -34% | 0 | 0 | — |
case-10 | pass→pass | 15,448 | 5,941 | -62% | 1 | 1 | 0% | 2,152 | 2,416 | +12% | 0 | 0 | — |
case-11 | fail→fail | 20,878 | 23,751 | +14% | 1 | 1 | 0% | 2,982 | 4,800 | +61% | 0 | 0 | — |
case-12 | fail→pass | 26,829 | 9,566 | -64% | 1 | 1 | 0% | 2,033 | 2,124 | +4% | 0 | 0 | — |
case-13 | fail→pass | 13,413 | 9,701 | -28% | 1 | 1 | 0% | 1,886 | 2,017 | +7% | 0 | 0 | — |
case-14 | fail→pass | 17,405 | 3,886 | -78% | 1 | 1 | 0% | 1,930 | 1,806 | -6% | 0 | 0 | — |
case-15 | fail→pass | 22,329 | 8,533 | -62% | 1 | 1 | 0% | 2,677 | 2,771 | +4% | 0 | 0 | — |
case-16 | fail→pass | 32,124 | 7,683 | -76% | 1 | 1 | 0% | 3,867 | 1,682 | -57% | 0 | 0 | — |
case-17 | fail→pass | 15,485 | 3,127 | -80% | 1 | 1 | 0% | 1,537 | 1,748 | +14% | 0 | 0 | — |
case-18 | pass→pass | 13,038 | 2,447 | -81% | 1 | 1 | 0% | 2,083 | 1,591 | -24% | 0 | 0 | — |
case-19 | fail→pass | 14,966 | 5,739 | -62% | 1 | 1 | 0% | 1,420 | 2,114 | +49% | 0 | 0 | — |
case-20 | pass→fail | 14,763 | 6,308 | -57% | 1 | 1 | 0% | 1,601 | 2,393 | +49% | 0 | 0 | — |
case-21 | pass→pass | 11,428 | 11,754 | +3% | 1 | 1 | 0% | 1,681 | 2,374 | +41% | 0 | 0 | — |
case-22 | pass→pass | 13,302 | 13,594 | +2% | 1 | 1 | 0% | 2,286 | 3,785 | +66% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.