forbear
v0.1.4
Published
Read the server's rate-limit instructions — turn a 429/503 Response into the number of milliseconds to wait, with correct fallback backoff.
Maintainers
Readme
forbear
Read HTTP server rate-limit instructions from 429/503 responses and return milliseconds to wait, with correct fallback backoff.
The problem
Different APIs use incompatible rate-limit header formats. Some send Retry-After: 30 (seconds), others send X-RateLimit-Reset: 1700000030 (epoch seconds), others use HTTP dates. Naive parsers treat epoch-seconds as delta-seconds, causing absurd waits (e.g., 53 years instead of 30 seconds).
Forbear parses all major dialects correctly, handles the epoch-seconds vs milliseconds ambiguity, and provides exponential backoff when headers are missing.
Install
npm install forbear
# or
pnpm add forbear
# or
yarn add forbearUse
import { forbear } from 'forbear';
const response = await fetch(url);
const waitMs = forbear(response);
await sleep(waitMs); // wait 30000ms if server sent Retry-After: 30With retry logic:
import pRetry from 'p-retry';
import { forbear } from 'forbear';
const result = await pRetry(async () => {
const response = await fetch(apiUrl);
if (response.status === 429) {
const waitMs = forbear(response, { attempt: pRetry.attemptNumber() });
throw new pRetry.AbortError(`Rate limited. Wait ${waitMs}ms before retry.`);
}
return response.json();
}, { minTimeout: 1000 });API
forbear(input, options?)
Returns milliseconds to wait, always in [0, cap].
function forbear(
input: Response | Headers | Record<string, string>,
options?: ForbearOptions
): numberParameters: input (Response/Headers/object), options.attempt (default: 0), options.now (default: Date.now()), options.base (default: 500), options.cap (default: 60000), options.jitter (default: true), options.random (default: Math.random).
Behavior: Parses headers by priority: Retry-After → RateLimit → X-RateLimit-Reset → exponential backoff. Never returns NaN or negative values.
inspect(input, options?)
Returns detailed rate-limit information.
function inspect(
input: Response | Headers | Record<string, string>,
options?: ForbearOptions
): RateLimitInfoReturns: waitMs (milliseconds), source (which rule: "retry-after" | "ratelimit" | "x-ratelimit" | "backoff"), header (raw value, undefined for backoff).
ForbearOptions, RateLimitInfo
Types for options and inspect return value.
Default export
import forbear from 'forbear'; // same as forbear()Non-goals
Forbear only calculates wait times. It will never: perform retries, sleep, make requests, store state, or track budgets.
Example for retries:
import pRetry from 'p-retry';
import { forbear } from 'forbear';
const response = await pRetry(async () => {
const res = await fetch(url);
if (res.status === 429) {
const wait = forbear(res, { attempt: pRetry.attemptNumber() });
await new Promise(resolve => setTimeout(resolve, wait));
throw new Error('Retry');
}
return res;
}, { retries: 3 });Related Packages
Caching & Concurrency:
- @azghr/filterkit — Framework-agnostic, type-safe filtering for TypeScript
- @azghr/singlet — Deduplicate concurrent async calls
- staleness — Stale-while-revalidate caching for async functions
Text Processing:
- @azghr/shorn — Truncate strings by byte budget without breaking graphemes
- seriatim — Sequential processing utilities
HTTP & Network:
- forestall — Delay execution until a condition is met
- obviate — Render operations unnecessary through caching
System & Process:
- quiesce — Ordered, timeboxed graceful shutdown for Node
- sortition — Deterministic percentage rollouts and A/B bucketing
- stanch — Stop flows or operations based on conditions
Utilities:
- expunge — Remove or exclude items from collections
- occlude — Hide or mask data and functionality
- placemark — Geographic location and mapping utilities
- specie — Currency and financial calculations
License
MIT
