api-rate-limit-handler – skill Lập trình cho agent AI
Triển khai cơ chế giới hạn tần suất API (throttling) có giới hạn, nhận biết idempotency, backoff và retry cho lỗi 429 và 5xx tạm thời.
Cài skill api-rate-limit-handler bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/api-rate-limit-handler-be106.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /api-rate-limit-handler – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/api-rate-limit-handler-be106.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $api-rate-limit-handler – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/api-rate-limit-handler-be106.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /api-rate-limit-handler – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/api-rate-limit-handler-be106.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /api-rate-limit-handler – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
API Rate Limit Handler
Overview
A skill for implementing production-grade rate limiting, exponential backoff, and retry strategies when integrating with external APIs. Prevents cascading failures, respects upstream quotas, and keeps your application resilient under load.
When to Use This Skill
- Use when calling external APIs that enforce rate limits (OpenAI, Stripe, GitHub, etc.)
- Use when you receive 429 Too Many Requests or 5xx errors and need graceful recovery
- Use when building a client that must respect
Retry-Afterheaders - Use when designing a system that fans out to multiple API providers
- Use when the user says "handle rate limits", "add retry logic", "backoff strategy", or "don't get throttled"
How It Works
Step 1: Classify the response
Determine whether a failed request is retryable or terminal.
| Status | Classification | Action |
|--------|---------------|--------|
| 200-299 | Success | Return response |
| 400, 401, 403, 404 | Terminal client error | Do not retry — fix the request |
| 408, 429 | Retryable (rate limit / timeout) | Retry with backoff |
| 500, 502, 503, 504 | Retryable (server error) | Retry with backoff |
Step 2: Parse rate limit headers
Always check upstream hints before computing your own delay.
function getRetryDelay(
response: Response,
attempt: number,
maxDelayMs = 60_000
): number {
// Prefer upstream hints
const retryAfter = response.headers.get("Retry-After");
if (retryAfter) {
const seconds = Number(retryAfter);
if (Number.isFinite(seconds) && seconds >= 0) {
return seconds * 1000;
}
// HTTP-date format
const date = new Date(retryAfter).getTime();
if (Number.isFinite(date)) {
return Math.max(0, date - Date.now());
}
}
// GitHub documents x-ratelimit-reset as Unix epoch seconds.
const resetHeader = response.headers.get("x-ratelimit-reset");
const githubReset = resetHeader === null || resetHeader.trim() === "" ? NaN : Number(resetHeader);
if (Number.isFinite(githubReset)) {
return Math.max(0, githubReset * 1000 - Date.now());
}
// Fallback: capped exponential backoff with full jitter.
const cap = Math.min(1000 * 2 ** attempt, maxDelayMs);
return Math.floor(Math.random() * cap);
}Upstream hints are minimum waits; never truncate them to retry early. If a hint exceeds the remaining operation budget, stop without scheduling another request. The cap applies only to fallback backoff.
Provider-specific reset headers do not share one unit or format. For example,
some APIs return durations while GitHub returns epoch seconds. Parse an
additional header only after checking that provider's current documentation.
Step 3: Implement the retry loop
async function fetchWithRetry(
url: string,
options: RequestInit,
maxRetries = 3,
maxElapsedMs = 120_000,
retryNonIdempotent = false
): Promise<Response> {
const startedAt = Date.now();
const method = (options.method ?? "GET").toUpperCase();
const replaySafe = ["GET", "HEAD", "OPTIONS", "PUT", "DELETE"].includes(method)
|| retryNonIdempotent;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.ok) return response;
// Terminal errors — do not retry
if (![408, 429, 500, 502, 503, 504].includes(response.status)) {
throw new Error(`Terminal error ${response.status}: ${response.statusText}`);
}
if (!replaySafe) {
throw new Error(
`${method} was not retried because replay safety was not explicitly established`
);
}
// Retryable — but exhausted attempts
if (attempt === maxRetries) {
throw new Error(`Failed after ${maxRetries} retries: ${response.status}`);
}
const remaining = maxElapsedMs - (Date.now() - startedAt);
const delay = getRetryDelay(response, attempt);
if (remaining <= 0 || delay > remaining) {
throw new Error(`Retry cannot be scheduled within ${maxElapsedMs}ms`);
}
// Release the connection before waiting when the body is not needed.
await response.body?.cancel();
console.warn(
`Request failed (${response.status}), retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`
);
await new Promise(resolve => setTimeout(resolve, delay));
}
throw new Error("Unreachable");
}Step 4: Add a client-side rate limiter (proactive)
Prevent hitting upstream limits in the first place with a token bucket or sliding window.
class TokenBucket {
private tokens: number;
private lastRefill: number;
private queue: Promise<void> = Promise.resolve();
constructor(
private maxTokens: number,
private refillRate: number // tokens per second
) {
this.tokens = maxTokens;
this.lastRefill = Date.now();
}
async acquire(): Promise<void> {
const ticket = this.queue.then(() => this.acquireOnce());
this.queue = ticket.catch(() => undefined);
return ticket;
}
private async acquireOnce(): Promise<void> {
this.refill();
if (this.tokens < 1) {
const waitMs = ((1 - this.tokens) / this.refillRate) * 1000;
await new Promise(resolve => setTimeout(resolve, waitMs));
this.refill();
}
this.tokens -= 1;
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}
// Usage: limit to 60 requests/minute
const limiter = new TokenBucket(60, 1);
async function rateLimitedFetch(url: string, options: RequestInit) {
await limiter.acquire();
return fetchWithRetry(url, options);
}Examples
Example 1: Idempotent API read with retry
const response = await fetchWithRetry(
"https://api.github.com/repos/OWNER/REPO",
{
method: "GET",
headers: {
"Accept": "application/vnd.github+json",
"Authorization": `Bearer ${githubToken}`,
},
},
3
);For a POST or another operation with side effects, leave
retryNonIdempotent false unless the provider documents an idempotency
mechanism and the same stable idempotency key is reused for every attempt.
Example 2: Python implementation
import time
import random
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
import httpx
def fetch_with_retry(url: str, max_retries: int = 3, max_elapsed_s: float = 120, **kwargs) -> httpx.Response:
started = time.monotonic()
kwargs.setdefault("timeout", 10.0)
for attempt in range(max_retries + 1):
response = httpx.request("GET", url, **kwargs)
if response.is_success:
return response
if response.status_code not in (408, 429, 500, 502, 503, 504) or attempt == max_retries:
response.raise_for_status()
retry_after = response.headers.get("retry-after")
delay = min(2 ** attempt + random.uniform(0, 1), 60)
if retry_after:
try:
seconds = float(retry_after)
if seconds >= 0 and seconds < float("inf"):
delay = seconds
except ValueError:
try:
retry_at = parsedate_to_datetime(retry_after)
delay = max(0, (retry_at - datetime.now(timezone.utc)).total_seconds())
except (ValueError, TypeError, OverflowError):
pass
remaining = max_elapsed_s - (time.monotonic() - started)
if remaining <= 0 or delay > remaining:
response.close()
raise TimeoutError("Retry cannot fit the remaining operation budget")
response.close()
time.sleep(delay)
raise RuntimeError("Unreachable")Best Practices
- ✅ Always respect
Retry-Afterheaders — they come from the provider who knows their limits - ✅ Add jitter to backoff to prevent thundering herd when multiple clients retry simultaneously
- ✅ Log every retry with status code, delay, and attempt number for debugging
- ✅ Set a maximum total timeout to avoid hanging indefinitely
- ✅ Use a client-side rate limiter proactively rather than only reacting to 429s
- ✅ Retry state-changing requests only with a provider-documented idempotency mechanism and a stable key
- ❌ Don't retry 4xx client errors (except 408 and 429) — fix the request instead
- ❌ Don't use fixed delays — exponential backoff distributes load more evenly
- ❌ Don't retry without a cap — unbounded retries can amplify outages
- ❌ Don't ignore per-endpoint limits — some APIs have different quotas per route
Limitations
- This skill does not replace environment-specific validation, testing, or expert review.
- Token bucket is approximate for distributed systems — use Redis-backed rate limiting for multi-instance deployments (for example the
upstash-ratelimitskill, or any shared-store limiter). - Some APIs
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- api-sdk-generator – Tạo mã SDK client, thư viện wrapper cho API, model request/response và cách dùng theo…
- api-security-best-practices – Triển khai các mẫu thiết kế API an toàn gồm xác thực, phân quyền, kiểm tra đầu vào, giới…
- api-security-testing – Quy trình kiểm thử bảo mật cho REST và GraphQL API, bao gồm xác thực, phân quyền, rate…
- dotnet-backend-patterns – Nắm vững các mẫu phát triển backend C#/.NET để xây dựng API, MCP server và ứng dụng…
- e2e-testing-patterns – Nắm vững kiểm thử end-to-end với Playwright và Cypress để xây dựng bộ test đáng tin cậy…
- cco-review – /c-level-agents:cco-review [plan] — Chief Customer Officer ám ảnh về việc giữ chân khách…
- playwright – Dùng khi tác vụ cần tự động hóa trình duyệt thật từ terminal (điều hướng, điền form…
- hunt-cors – Săn lỗi cấu hình CORS: phản chiếu origin kèm credentials, tin tưởng null origin, bỏ qua…