Advanced (Diperbarui: 2/6/2026)

Rate Limiting API dengan Claude Code: 429, Redis, dan Cloudflare

Implementasi rate limiting API dengan Claude Code: 429, Redis, Cloudflare, anti-abuse, dan kesalahan umum.

Rate Limiting API dengan Claude Code: 429, Redis, dan Cloudflare

Rate limiting API berarti menentukan berapa banyak request yang boleh dikirim client yang sama dalam waktu pendek, lalu meminta client menunggu jika melewati batas itu. Ini bukan mematikan layanan untuk semua orang. Ini cara menjaga agar satu user, bot, script, atau integrasi tidak menghabiskan kapasitas, kuota, atau biaya.

Claude Code bisa membuat endpoint, auth check, dan test dengan cepat. Masalahnya, API yang berjalan di local belum tentu aman untuk production. Login attempt, search API, AI generation, SMS OTP, email sending, dan webhook retry semuanya punya biaya nyata. Saat Masa menguji form kontak kecil, duplicate submit awalnya terlihat seperti masalah UX kecil. Tetapi saat QA, kuota email provider cepat berkurang. Akar masalahnya bukan hanya form; belum ada aturan “aksi ini boleh terjadi berapa kali”.

Panduan ini mengubah rate limiting menjadi workflow praktis untuk Claude Code: desain sebelum coding, demo Node.js tanpa dependency, implementasi Express + Redis, client yang menghormati Retry-After, peran Cloudflare di edge, sudut pandang anti-abuse, failure case, dan CTA konsultasi. Untuk konteks terkait, baca pengembangan API production dengan Claude Code, praktik keamanan Claude Code, dan panduan Cloudflare Workers.

Gunakan referensi resmi sebagai dasar: Cloudflare Rate limiting rules, OWASP API Security 2023 API4: Unrestricted Resource Consumption dan API6: Unrestricted Access to Sensitive Business Flows, serta MDN tentang 429 Too Many Requests.

Tentukan dulu apa yang dilindungi

Kesalahan awal adalah langsung memilih angka seperti “60 request per menit”. Lebih baik mulai dari risiko. Rate limiting melindungi server, database, biaya API eksternal, stok, password reset flow, kuota email, kredit AI, kualitas lead, dan aturan bisnis.

flowchart LR
  A["Request"] --> B["Identify client"]
  B --> C["Check policy"]
  C -->|allowed| D["Run handler"]
  C -->|too many| E["Return 429 + Retry-After"]
  D --> F["Log count and cost"]

Contoh use case realistis:

Use caseLimit keyAwal yang masuk akalYang dilindungi
Login, OTP, reset passwordIP + account id5 percobaan / 10 menitBrute force, biaya SMS
Search/list APIUser id + path60 / menitBeban DB, scraping
AI/image generationUser id + planFree plan 10/hariBiaya LLM, free tier
Webhook receiverSender + event idBurst pendek diizinkanDuplicate processing

Jangan hanya bergantung pada IP. Di kantor, sekolah, atau jaringan seluler, banyak user valid bisa memakai IP yang sama. Penyerang juga bisa mengganti IP. Untuk API yang sudah authenticated, gabungkan user id, API key, organization id, plan, endpoint, dan operation type.

Beri Claude Code spesifikasi yang jelas

“Tambahkan rate limiting” terlalu umum. Tulis algoritma, strategi key, response 429, header, test, log, dan storage local/production. Prompt berikut bisa langsung dipakai:

Add rate limiting to the existing API.

Requirements:
- Scope: POST /api/contact and POST /api/login
- If authenticated, key by userId; otherwise key by IP
- 429 JSON body: { "error": "rate_limited", "retryAfter": seconds }
- Return Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining
- Tests must cover allowed requests, limit reached, and recovery after time passes
- Use Redis in production and an in-memory store locally
- Make limits configurable through environment variables

After implementation, report the verification commands and any unverified risks.

Dengan spesifikasi seperti ini, Claude Code punya acceptance criteria. Ia tidak hanya menambah middleware, tetapi juga menjaga kontrak untuk frontend, SDK, dan batch job. Gunakan juga panduan API testing agar response 429 ikut diuji.

Contoh minimal yang bisa dijalankan: Node.js 429 server

Simpan sebagai rate-limit-demo.mjs dan jalankan dengan Node.js 20 atau lebih baru. Contoh ini memakai token bucket: bucket diisi token dengan kecepatan tetap dan tiap request menghabiskan satu token. Burst pendek masih bisa lewat, tetapi rata-rata tetap dikontrol.

import http from "node:http";

class TokenBucket {
  constructor({ capacity, refillPerSecond }) {
    this.capacity = capacity;
    this.refillPerSecond = refillPerSecond;
    this.tokens = capacity;
    this.updatedAt = Date.now();
  }

  take(now = Date.now()) {
    const elapsed = (now - this.updatedAt) / 1000;
    this.tokens = Math.min(
      this.capacity,
      this.tokens + elapsed * this.refillPerSecond,
    );
    this.updatedAt = now;

    if (this.tokens >= 1) {
      this.tokens -= 1;
      return { allowed: true, remaining: Math.floor(this.tokens), retryAfter: 0 };
    }

    const missing = 1 - this.tokens;
    const retryAfter = Math.ceil(missing / this.refillPerSecond);
    return { allowed: false, remaining: 0, retryAfter };
  }
}

const buckets = new Map();

function clientKey(req) {
  return req.headers["x-api-key"] ?? req.socket.remoteAddress ?? "anonymous";
}

function checkLimit(req) {
  const key = clientKey(req);
  if (!buckets.has(key)) {
    buckets.set(key, new TokenBucket({ capacity: 5, refillPerSecond: 1 }));
  }
  return buckets.get(key).take();
}

const server = http.createServer((req, res) => {
  if (req.url !== "/api/demo") {
    res.writeHead(404, { "content-type": "application/json" });
    res.end(JSON.stringify({ error: "not_found" }));
    return;
  }

  const result = checkLimit(req);
  res.setHeader("X-RateLimit-Limit", "5");
  res.setHeader("X-RateLimit-Remaining", String(result.remaining));

  if (!result.allowed) {
    res.writeHead(429, {
      "content-type": "application/json",
      "Retry-After": String(result.retryAfter),
    });
    res.end(JSON.stringify({
      error: "rate_limited",
      retryAfter: result.retryAfter,
    }));
    return;
  }

  res.writeHead(200, { "content-type": "application/json" });
  res.end(JSON.stringify({ ok: true, remaining: result.remaining }));
});

server.listen(3000, () => {
  console.log("Listening on http://localhost:3000/api/demo");
});
node rate-limit-demo.mjs

Di terminal lain:

for i in 1 2 3 4 5 6 7; do
  curl -i http://localhost:3000/api/demo
done

Di Windows PowerShell:

1..7 | ForEach-Object {
  curl.exe -i http://localhost:3000/api/demo
}

Request keenam atau ketujuh harus menghasilkan 429 Too Many Requests. MDN menjelaskan bahwa 429 bisa membawa Retry-After, sehingga client tahu kapan boleh mencoba lagi.

Redis untuk beberapa instance

Versi memory bagus untuk belajar, tetapi rusak saat ada beberapa API instance. Server A bisa menganggap sisa request 0, sementara server B masih melihat 5. Redis membuat counter bersama.

Contoh Express ini memakai Redis sorted set untuk sliding window. Sliding window menghitung “60 detik terakhir dari sekarang”, bukan reset kaku di awal menit, sehingga lebih halus.

npm init -y
npm i express ioredis
docker run --rm --name redis-rate-limit -p 6379:6379 redis:7-alpine
import express from "express";
import Redis from "ioredis";

const app = express();
const redis = new Redis(process.env.REDIS_URL ?? "redis://127.0.0.1:6379");

const limitScript = `
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window_ms = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local member = ARGV[4]

redis.call("ZREMRANGEBYSCORE", key, 0, now - window_ms)

local count = redis.call("ZCARD", key)
if count >= limit then
  local oldest = redis.call("ZRANGE", key, 0, 0, "WITHSCORES")[2]
  local retry_ms = math.max(1, oldest + window_ms - now)
  return {0, 0, retry_ms}
end

redis.call("ZADD", key, now, member)
redis.call("PEXPIRE", key, window_ms)
return {1, limit - count - 1, 0}
`;

async function rateLimit(req, res, next) {
  const user = req.get("authorization")?.replace(/^Bearer\s+/i, "");
  const identity = user || req.ip || "anonymous";
  const key = `rl:${identity}:${req.path}`;
  const limit = Number(process.env.RATE_LIMIT_REQUESTS ?? 10);
  const windowMs = Number(process.env.RATE_LIMIT_WINDOW_MS ?? 60000);
  const now = Date.now();
  const member = `${now}:${Math.random()}`;

  const [allowed, remaining, retryMs] = await redis.eval(
    limitScript,
    1,
    key,
    limit,
    windowMs,
    now,
    member,
  );

  res.setHeader("X-RateLimit-Limit", String(limit));
  res.setHeader("X-RateLimit-Remaining", String(remaining));

  if (allowed === 1) return next();

  const retryAfter = Math.ceil(Number(retryMs) / 1000);
  res.setHeader("Retry-After", String(retryAfter));
  res.status(429).json({ error: "rate_limited", retryAfter });
}

app.use(rateLimit);

app.get("/api/search", (req, res) => {
  res.json({ data: ["claude-code", "rate-limit"], at: new Date().toISOString() });
});

app.listen(3000, () => {
  console.log("API ready on http://localhost:3000/api/search");
});
node redis-rate-limit-server.mjs
for i in $(seq 1 12); do
  curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/api/search
done

Saat meminta Claude Code membuat versi production, tulis juga mode saat Redis gagal. Form marketing mungkin boleh fail open sebentar. Login, payment, dan AI credit endpoint mungkin harus fail closed. Ini keputusan risiko bisnis.

Client harus menghormati Retry-After

Server-side limiter hanya separuh desain. SDK, batch job, dan webhook sender harus membaca Retry-After lalu menunggu.

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function fetchWithRateLimit(url, options = {}, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
    const res = await fetch(url, options);
    if (res.status !== 429) return res;

    const retryAfter = Number(res.headers.get("retry-after") ?? "1");
    const waitMs = Math.max(1, retryAfter) * 1000;
    console.log(`429 received. Waiting ${waitMs}ms before retry.`);
    await sleep(waitMs);
  }

  throw new Error("Rate limit retry budget exhausted");
}

for (let i = 0; i < 8; i += 1) {
  const res = await fetchWithRateLimit("http://localhost:3000/api/demo");
  console.log(i + 1, res.status, await res.text());
}

Untuk client API eksternal, minta Claude Code: jika 429, gunakan exponential backoff, prioritaskan Retry-After jika ada, batasi retry, dan log failure terakhir. Retry tanpa batas bisa menjadi incident.

Cloudflare di edge, aturan user di aplikasi

Cloudflare Rate Limiting Rules bagus untuk menolak spike jelas sebelum mencapai origin. Dokumentasi resmi menjelaskan expression, period, threshold, mitigation timeout, dan action. Ini cocok untuk login, public search, admin route, AI generation entry, dan pola bot.

Namun Cloudflare tidak menggantikan aturan produk. Kuota free vs paid, penggunaan organisasi, AI credit per user, refund abuse, dan invitation abuse membutuhkan data aplikasi. Biasanya dipakai berlapis:

LayerPeranContoh
Cloudflare/WAFBlokir burst dan bot lebih awalLimit /api/login per IP
ApplicationLimit user, org, plan, operationFree user 10 generation/hari
Queue/workerRatakan pekerjaan mahalEmail, image, PDF
Billing/monitoringDeteksi biaya abnormalAlert SMS dan LLM

OWASP API4 melihat konsumsi CPU, memory, ukuran file, dan layanan pihak ketiga tanpa batas sebagai risiko security. OWASP API6 membahas abuse otomatis pada alur bisnis sensitif seperti purchase, reservation, posting, dan referral. Jadi rate limiting bukan hanya anti-DDoS, tetapi juga revenue protection.

Kesalahan umum

Kesalahan pertama adalah satu limit global untuk semua API. Read profile dan reset password punya risiko berbeda. Pisahkan berdasarkan endpoint dan operation.

Kesalahan kedua adalah response 429 tidak konsisten. Jika satu route mengembalikan HTML, route lain text, dan route lain JSON, client menjadi rapuh. Standarkan JSON body, Retry-After, dan header limit.

Kesalahan ketiga adalah hanya menghitung request sukses. Failed login, payload invalid, dan reset password untuk email tidak dikenal tetap punya biaya dan sinyal abuse. Sering kali failure perlu limit lebih ketat.

Kesalahan keempat adalah menyimpan data personal di key. Jangan simpan email atau nomor telepon mentah di Redis key atau log. Hash jika perlu dan pakai TTL pendek.

Kesalahan kelima adalah test benar-benar menunggu 60 detik. Inject now ke fungsi dan majukan waktu di test agar CI tidak lambat.

Kesalahan terakhir adalah memblokir infrastruktur valid. Search bot, uptime check, monitoring internal, payment webhook, dan partner callback bisa butuh policy terpisah. Exception harus sempit dan bisa diaudit.

Checklist review Claude Code

Setelah implementasi, minta Claude Code mengecek:

  • Semua 429 memakai shape JSON yang sama?
  • Retry-After dan remaining header ada?
  • Key untuk IP, user id, API key, dan organization benar?
  • Redis failure behavior jelas?
  • Auth failure, validation failure, dan external API failure dihitung saat perlu?
  • Test mencakup allowed, blocked, recovered?
  • Exception admin, monitoring, webhook, dan crawler terlalu luas?

Ini bukan hanya kualitas kode. Pada produk dengan AI, SMS, email, atau payment, kesalahan limiter langsung muncul di biaya.

CTA konsultasi

ClaudeCodeLab membahas API implementation, security review, rate limiting, billing safeguards, dan monitoring di Claude Code training and consulting. Pada proyek Next.js, Express, Cloudflare Workers, atau AWS API Gateway, pekerjaan utamanya adalah mengubah “aksi apa, untuk siapa, berapa kali” menjadi code, test, dan log.

Untuk proyek personal, mulai dari demo Node.js lalu pindah ke Redis saat instance lebih dari satu. Untuk tim, dokumentasikan prompt Claude Code, checklist review, environment variable, dan runbook agar perubahan limit tidak menjadi tebak-tebakan.

Saya menguji contoh di artikel ini. Server memory mengembalikan 429 setelah request berulang, versi Redis mengembalikan 429 dengan Retry-After setelah melewati window 10 request, dan client wait logic menghentikan retry langsung. Pelajarannya jelas: rate limit production-ready hanya jika response, retry, log, dan exception diverifikasi bersama.

#Claude Code #rate limiting #API #security #Node.js
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.