| name | openevidence-rate-limits |
| description | Implement OpenEvidence rate limiting, backoff, and request optimization.
Use when handling rate limit errors, implementing retry logic,
or optimizing API request throughput for clinical queries.
Trigger with phrases like "openevidence rate limit", "openevidence throttling",
"openevidence 429", "openevidence retry", "openevidence backoff".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
OpenEvidence Rate Limits
Overview
Handle OpenEvidence rate limits gracefully with exponential backoff, request queuing, and clinical priority management.
Prerequisites
- OpenEvidence SDK installed
- Understanding of async/await patterns
- Access to rate limit headers
Rate Limit Tiers
| Tier | Clinical Queries/min | DeepConsult/hour | Burst | Use Case |
|---|
| Standard | 60 | 5 | 10 | Small practices |
| Professional | 300 | 20 | 50 | Clinics, small hospitals |
| Enterprise | 1,000 | 100 | 200 | Health systems |
| Unlimited | Custom | Custom | Custom | Large integrations |
Rate Limit Headers
| Header | Description | Example |
|---|
X-RateLimit-Limit | Max requests per window | 60 |
X-RateLimit-Remaining | Requests remaining | 45 |
X-RateLimit-Reset | Unix timestamp for reset | 1706295600 |
Retry-After | Seconds to wait (on 429) | 30 |
X-DeepConsult-Remaining | DeepConsult quota | 5 |
Instructions
Step 1: Implement Exponential Backoff with Jitter
interface RetryConfig {
maxRetries: number;
baseDelayMs: number;
maxDelayMs: number;
jitterMs: number;
}
const DEFAULT_CONFIG: RetryConfig = {
maxRetries: 5,
baseDelayMs: 1000,
maxDelayMs: 60000,
jitterMs: 500,
};
export async function withExponentialBackoff<T>(
operation: () => Promise<T>,
config: Partial<RetryConfig> = {}
): Promise<T> {
const cfg = { ...DEFAULT_CONFIG, ...config };
for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
if (attempt === cfg.maxRetries) throw error;
const status = error.status || error.response?.;
(status !== && (status < || status >= )) error;
: ;
retryAfter = error.?.?.[];
(retryAfter) {
delay = (retryAfter) * ;
} {
exponentialDelay = cfg. * .(, attempt);
jitter = .() * cfg.;
delay = .(exponentialDelay + jitter, cfg.);
}
.();
( (r, delay));
}
}
();
}
Step 2: Rate Limit Monitor
export class RateLimitMonitor {
private remaining: number = 60;
private limit: number = 60;
private resetAt: Date = new Date();
private deepConsultRemaining: number = 5;
updateFromHeaders(headers: Headers | Record<string, string>): void {
const get = (key: string) =>
headers instanceof Headers ? headers.get(key) : headers[key.toLowerCase()];
const remaining = get('x-ratelimit-remaining');
if (remaining) this.remaining = parseInt(remaining);
const limit = get('x-ratelimit-limit');
if (limit) this.limit = parseInt(limit);
const resetTimestamp = ();
(resetTimestamp) {
. = ((resetTimestamp) * );
}
deepConsult = ();
(deepConsult) . = (deepConsult);
}
(): {
. < && () < .;
}
(): {
.(, ..() - .());
}
(): {
. > ;
}
(): {
{
: .,
: .,
: ..(),
: ((. - .) / .) * ,
: .,
};
}
}
{
: ;
: ;
: ;
: ;
: ;
}
Step 3: Priority-Based Request Queue
import PQueue from 'p-queue';
type ClinicalPriority = 'stat' | 'urgent' | 'routine' | 'research';
interface QueuedRequest<T> {
operation: () => Promise<T>;
priority: ClinicalPriority;
resolve: (value: T) => void;
reject: (error: any) => void;
}
export class ClinicalRequestQueue {
private queues: Record<ClinicalPriority, PQueue>;
private monitor: RateLimitMonitor;
constructor(monitor: RateLimitMonitor) {
this.monitor = monitor;
this.queues = {
stat: new PQueue({ concurrency: , : , : }),
: ({ : , : , : }),
: ({ : , : , : }),
: ({ : , : , : }),
};
}
enqueue<T>(
: <T>,
: =
): <T> {
queue = .[priority];
queue.( () => {
(..()) {
waitTime = ..();
.();
( (r, waitTime));
}
(operation);
});
}
(): <, { : ; : }> {
{
: { : ..., : ... },
: { : ..., : ... },
: { : ..., : ... },
: { : ..., : ... },
};
}
(?: ): <> {
(priority) {
.[priority].();
} {
.(.).( q.());
}
}
(?: ): <> {
(priority) {
.[priority].();
} {
.(.).( q.());
}
}
}
Step 4: Adaptive Rate Limiting
export class AdaptiveRateLimiter {
private windowMs = 60000;
private requestTimes: number[] = [];
private targetUsagePercent = 80;
private currentLimit = 60;
recordRequest(): void {
const now = Date.now();
this.requestTimes.push(now);
this.requestTimes = this.requestTimes.filter(
t => t > now - this.windowMs
);
}
updateLimit(limit: number): void {
this.currentLimit = limit;
}
shouldDelay(): { delay: boolean; waitMs: number } {
const requestsInWindow = this.requestTimes.length;
const targetMax = Math.(. * (. / ));
(requestsInWindow >= targetMax) {
oldestRequest = .(....);
waitMs = .(, oldestRequest + . - .());
{ : , waitMs };
}
{ : , : };
}
(): {
: ;
: ;
: ;
} {
requestsInWindow = ..;
{
requestsInWindow,
: (requestsInWindow / .) * ,
: . - requestsInWindow,
};
}
}
Output
- Reliable API calls with automatic retry
- Priority-based request queuing
- Rate limit monitoring and alerting
- Adaptive throttling based on usage
Error Handling
| Header | Description | Action |
|---|
X-RateLimit-Limit | Max requests | Adjust queue concurrency |
X-RateLimit-Remaining | Remaining requests | Throttle if low |
X-RateLimit-Reset | Reset timestamp | Wait until reset |
Retry-After | Seconds to wait | Honor exactly |
Examples
Complete Rate-Limited Client
import { OpenEvidenceClient } from '@openevidence/sdk';
import { RateLimitMonitor } from '../openevidence/rate-monitor';
import { ClinicalRequestQueue } from '../openevidence/request-queue';
const monitor = new RateLimitMonitor();
const queue = new ClinicalRequestQueue(monitor);
const rawClient = new OpenEvidenceClient({
apiKey: process.env.OPENEVIDENCE_API_KEY,
orgId: process.env.OPENEVIDENCE_ORG_ID,
});
export async function clinicalQuery(
question: string,
priority: 'stat' | 'urgent' | 'routine' = 'routine'
) {
return queue.enqueue(
async () => {
const response = await rawClient.query({
question,
context: { specialty: 'internal-medicine', urgency: priority },
});
monitor.(response.);
response.;
},
priority
);
}
(, );
(, );
Monitor Dashboard
app.get('/health/openevidence', (req, res) => {
const status = monitor.getStatus();
const queueStatus = queue.getQueueStatus();
res.json({
rateLimits: status,
queues: queueStatus,
healthy: status.remaining > 5,
});
});
Resources
Next Steps
For security configuration, see openevidence-security-basics.