Claude-code-plugins-plus-skills openevidence-rate-limits

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/openevidence-pack/skills/openevidence-rate-limits" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-openevidence-rate-limits && rm -rf "$T"
manifest: plugins/saas-packs/openevidence-pack/skills/openevidence-rate-limits/SKILL.md
source content

OpenEvidence Rate Limits

Overview

OpenEvidence's clinical decision support API enforces strict rate limits to ensure reliable evidence retrieval for healthcare applications. Clinical query endpoints are throttled per API key, with lower limits on evidence synthesis calls that involve AI-powered literature analysis. In clinical settings, rate limiting directly impacts patient care workflows, so implementations must prioritize graceful degradation over retry storms. Batch research queries during off-peak hours and cache evidence summaries aggressively since medical literature changes infrequently.

Rate Limit Reference

EndpointLimitWindowScope
Clinical query30 req1 minutePer API key
Evidence synthesis10 req1 minutePer API key
Literature search60 req1 minutePer API key
Citation retrieval120 req1 minutePer API key
Bulk evidence export5 req1 hourPer API key

Rate Limiter Implementation

class OpenEvidenceRateLimiter {
  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 queryLimiter = new OpenEvidenceRateLimiter(25);
const synthesisLimiter = new OpenEvidenceRateLimiter(8);

Retry Strategy

async function openEvidenceRetry<T>(
  limiter: OpenEvidenceRateLimiter, 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") || "30", 10);
      const jitter = Math.random() * 2000;
      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) * 3000));
      continue;
    }
    throw new Error(`OpenEvidence API ${res.status}: ${await res.text()}`);
  }
  throw new Error("Max retries exceeded");
}

Batch Processing

async function batchClinicalQueries(queries: string[], batchSize = 5) {
  const results: any[] = [];
  for (let i = 0; i < queries.length; i += batchSize) {
    const batch = queries.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(q => openEvidenceRetry(queryLimiter, () =>
        fetch(`${OE_BASE}/api/v1/clinical/query`, {
          method: "POST", headers,
          body: JSON.stringify({ question: q, includeEvidence: true }),
        })
      ))
    );
    results.push(...batchResults);
    if (i + batchSize < queries.length) await new Promise(r => setTimeout(r, 12_000));
  }
  return results;
}

Error Handling

IssueCauseFix
429 on clinical queryExceeded 30 req/min query capQueue queries, return cached if available
429 on synthesisSynthesis limit (10/min) is strictPre-cache common drug interaction queries
Synthesis timeoutComplex multi-study analysisSet 120s timeout, poll async endpoint
401 key expiredAPI key rotation missedAutomate key rotation with 7-day buffer
Stale evidenceCached result older than 30 daysSet TTL on cache, re-query on expiry

Resources

Next Steps

See

openevidence-performance-tuning
.