Web Development

Cara Handle 429 Rate Limit di Bulk API Request [2026]

Asep Alazhari

Bulk create flow yang nembak ratusan request bakal kena 429. Ini cara gue benerin pakai Retry-After handling, backoff jitter, dan live quota tracking.

Cara Handle 429 Rate Limit di Bulk API Request [2026]

Gue pernah bikin fitur bulk create yang bikin admin bisa nambahin puluhan record sekaligus, gak perlu klik form satu-satu. Waktu testing, semuanya lancar, lima record, sepuluh record, dua puluh record. Terus ada admin beneran yang pilih 150 record sekaligus terus klik go. Setengahnya gagal dengan status code 429, dan pesan errornya di layar cuma bilang ada yang salah. Gue gak tahu record mana aja yang berhasil, admin-nya juga gak tahu.

Sore itu jadi crash course dadakan soal rate limiting dari sisi client. Bukan bagian di mana lo yang set rate limiter di API sendiri, tapi bagian yang lebih susah, di mana lo yang manggil API orang lain yang ada rate limit-nya dalam loop, dan harus tetep jalan meski kena limit. Artikel ini isinya apa yang gue bangun buat benerin ini, dan kenapa solusinya bukan soal retry lebih keras, tapi pacing yang lebih pinter.

Key Takeaways

  • Status code 429 artinya terlalu banyak request, dan solusinya bukan asal retry, tapi baca response header yang udah dikasih sama API-nya.
  • Selalu cek header Retry-After duluan. Kalau gak ada, pakai exponential backoff dengan random jitter, biar banyak client gak retry di momen yang sama persis.
  • Batasi retry maksimal 3 sampai 5 kali per request, terus kasih error final yang jelas, jangan retry terus-terusan.
  • Tambahin adaptive pacing di antara request dalam satu batch, biar lo pelan-pelan sebelum kena limit, bukan cuma react setelah kena.
  • Kasih tau sisa quota ke user secara real time lewat progress dan results summary, jangan cuma satu error generik di akhir.

Apa Itu 429 Rate Limit Error?

Status code 429 Too Many Requests artinya server ngerti request lo, tapi nolak karena lo udah ngirim terlalu banyak request dalam satu window waktu tertentu. Ini didefinisikan di RFC 6585 sebagai bagian dari standar HTTP status code. Server-nya gak rusak, dia lagi protect diri dari overload, dan dia expect lo buat pelan-pelan terus coba lagi nanti.

Kebanyakan API yang bagus bakal ngirim header tambahan bareng response 429, yang ngasih tau lo harus ngapain. Dua yang paling penting adalah Retry-After, yang ngasih tau berapa lama lo harus nunggu sebelum coba lagi, dan header keluarga rate limit kayak X-RateLimit-Remaining, yang ngasih tau sisa request lo di window sekarang.

Kenapa Bulk Create Gampang Banget Kena 429?

Submit form satu-satu jarang kena rate limit. Bulk operation beda cerita, soalnya dia ngubah satu aksi user jadi puluhan atau ratusan API call dalam waktu singkat. Kalau bulk create loop lo nembak request secepat network bisa, artinya lo lagi load test API yang lo sendiri bergantung sama dia, dan rate limit emang dibikin buat nyetop burst kayak gitu.

Failure mode-nya juga licik. 20 sampai 30 request pertama biasanya berhasil, soalnya window rate limit-nya belum penuh. Terus request mulai gagal di tengah batch, yang paling nyebelin, soalnya sekarang lo punya campuran record yang berhasil dan gagal, tanpa cara jelas buat bedain mana yang mana.

Gimana Cara Baca Sinyal yang Bener dari Response 429?

Caranya sama kayak baca response header lainnya, cek objek response-nya sebelum lo mutusin langkah selanjutnya. Ini helper kecil buat narik dua sinyal yang paling penting.

function parseRateLimitInfo(response) {
    const retryAfterHeader = response.headers.get("retry-after");
    const remainingHeader = response.headers.get("x-ratelimit-remaining");

    return {
        status: response.status,
        retryAfterMs: retryAfterHeader ? Number(retryAfterHeader) * 1000 : null,
        remaining: remainingHeader ? Number(remainingHeader) : null,
    };
}

Kalau retryAfterMs balik jadi angka beneran, percaya itu dibanding backoff math yang lo bikin sendiri. Server-nya lagi ngasih tau persis kapan window-nya reset, dan kalau lo nebak delay lebih pendek, lo bakal kena limit lagi aja.

Bikin Retry Layer: Exponential Backoff dengan Jitter

Kalau Retry-After gak ada, pola standar 2026 adalah exponential backoff dengan jitter. Backoff artinya tiap retry nunggu lebih lama dari sebelumnya. Jitter artinya lo nambahin offset random kecil ke waktu tunggu itu, jadi kalau sepuluh client kena rate limit di detik yang sama, mereka gak semua retry di detik yang sama persis, yang bisa nyebabin gelombang 429 kedua.

async function requestWithBackoff(makeRequest, maxAttempts = 4) {
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        const response = await makeRequest();
        if (response.status !== 429) {
            return response;
        }

        const { retryAfterMs } = parseRateLimitInfo(response);
        const baseDelay = retryAfterMs ?? 500 * 2 ** attempt;
        const jitter = Math.random() * 250;
        const delay = baseDelay + jitter;

        if (attempt === maxAttempts) {
            throw new Error(`Request failed after ${maxAttempts} attempts due to rate limiting`);
        }

        await new Promise((resolve) => setTimeout(resolve, delay));
    }
}

Batasin attempt di 3 sampai 5 kali sama pentingnya kayak backoff-nya sendiri. Retry loop tanpa batas bisa bikin user liatin spinner berapa menit sementara code lo diam-diam nyerang API yang udah bilang buat berhenti.

Apa Itu Adaptive Pacing, dan Kenapa Ditambahin Sebelum Kena 429?

Adaptive pacing artinya ngasih jarak antar request dalam satu batch berdasarkan sinyal yang udah dikasih API, bukan nembak semua request beruntun terus baru react pas 429 muncul. Retry logic benerin masalah setelah kejadian. Pacing nyegah masalahnya kejadian sesering itu dari awal.

Versi simpel-nya nge-track sisa quota dari tiap response terus pelan-pelan begitu turun di bawah threshold yang lo pilih.

async function runBulkCreate(items, createOne) {
    let delayBetweenRequests = 0;
    const results = [];

    for (const item of items) {
        if (delayBetweenRequests > 0) {
            await new Promise((resolve) => setTimeout(resolve, delayBetweenRequests));
        }

        const response = await requestWithBackoff(() => createOne(item));
        const { remaining } = parseRateLimitInfo(response);

        if (remaining !== null && remaining < 10) {
            delayBetweenRequests = 300;
        }

        results.push({ item, status: response.status });
    }

    return results;
}

Ini contoh yang sengaja disederhanain, tapi ide-nya bisa dikembangin. Sebagian API ngasih balik ukuran window quota lengkap, dan lo bisa atur pacing buat nyebar rata di window itu, bukan naikin delay langsung sekaligus. Prinsip intinya tetep sama, baca sisa quota, terus pelan-pelan sebelum API-nya maksa lo berhenti.

Naive Retry vs Resilient Bulk Request

AspekPendekatan naivePendekatan resilient
Sumber delayTebakan tetap, misalnya 1 detikHeader Retry-After duluan, backoff jitter sebagai fallback
Batas retryGak ada, atau retry terusDibatasi 3 sampai 5 kali, terus failure yang jelas
Pacing requestNembak secepat mungkinPelan-pelan pas sisa quota turun
Feedback ke userSatu error generik di akhirProgress live dengan hasil per item
Failure modePartial success diam-diam, gak jelas record mana yang gagalResults summary jelas, aman buat retry cuma item yang gagal

Kasih Tau Quota ke User, Jangan Nebak

Bagian terakhir bukan soal logic backend sama sekali, ini soal UI. Begitu bulk loop lo nge-track sisa quota dan status per item, tampilin state itu, jangan disembunyiin di balik satu loading spinner doang. Tabel results dengan satu baris per item, nunjukin created, retried, atau failed, ngubah partial failure yang bikin bingung jadi sesuatu yang bisa langsung ditindaklanjutin user, termasuk retry cuma baris yang gagal, bukan seluruh batch.

Gue pernah bahas pelajaran resilience yang mirip, kali ini soal dependency yang down bukan yang di-throttle, di Fixing RabbitMQ 4.x Connection: amqplib ke CloudAMQP. Insting retry dan backoff dari fix itu kepake hampir langsung buat masalah rate limit ini.

Best Practices dan Pitfall yang Sering Kejadian

  • Selalu utamain header Retry-After dibanding backoff math sendiri kalau API-nya nyediain.
  • Tambahin random jitter ke tiap backoff delay, bukan cuma retry pertama, biar gak ada gelombang retry yang bareng dari banyak user.
  • Batasin total retry attempt per item biar batch yang kena rate limit gagal dengan predictable, bukan nge-hang.
  • Track X-RateLimit-Remaining sepanjang batch, bukan cuma per request, biar pacing react ke tren beneran.
  • Jangan pernah retry request yang udah berhasil. Track status per item biar retry cuma nyentuh item yang beneran gagal.
  • Log hasil akhir per item, bukan cuma jumlah sukses gagal di level batch, biar support dan debugging masih bisa dilakuin belakangan.

Versi cluster level dari pelajaran yang sama, tetep resilient pas dependency lo berhenti kerja sama, ada di Kubernetes Cluster Mati Setelah Node Reboot: Postmortem Lengkap. Beda layer stack, tapi disiplin dasarnya sama, cek sinyal dulu sebelum langsung asumsi yang terburuk.

Pertanyaan yang Sering Diajukan

Status code 429 itu artinya apa?

Status code 429 artinya Too Many Requests. Server ngerti request-nya tapi nolak soalnya client udah lewatin batas rate yang diizinin, sesuai definisi di RFC 6585.

Harus selalu pakai exponential backoff buat error 429?

Pakai exponential backoff dengan jitter cuma kalau server gak ngasih header Retry-After. Kalau Retry-After ada, percaya itu duluan, soalnya itu nyerminin window rate limit beneran dari server, bukan tebakan.

Berapa kali sih idealnya retry request yang kena rate limit?

Batasin retry 3 sampai 5 kali per request. Lebih dari itu, kasih error yang jelas ke user aja daripada retry diam-diam, soalnya attempt tambahan jarang nolong kalau request-nya udah gagal segitu banyak kali.

Jitter itu apa dan kenapa penting buat retry?

Jitter itu delay random kecil yang ditambahin di atas waktu tunggu backoff. Ini nyegah banyak client yang kena rate limit di momen yang sama buat retry di momen yang sama persis juga, yang kalau enggak bakal nyebabin gelombang error 429 kedua.

Gimana cara nyegah error 429 di bulk create flow, bukan cuma nanganin pas udah kejadian?

Tambahin adaptive pacing. Track sisa quota dari tiap response terus pelan-pelan loop-nya begitu turun di bawah threshold aman, bukan nembak semua request beruntun terus baru react setelah 429 muncul.

Kesimpulan

Solusi buat kegagalan bulk create 150 record gue bukan retry count yang lebih gede, tapi baca sinyal yang udah dikirim API dari awal. Retry-After ngasih tau persis berapa lama gue harus nunggu. X-RateLimit-Remaining ngasih tau kapan harus pelan-pelan sebelum kena tembok. Backoff dengan jitter nyegah retry-retry-nya nabrak satu sama lain. Tampilan results per item ngasih tau admin persis apa yang kejadian, bukan satu error yang gak jelas.

Kalau lo punya bulk operation yang manggil API rate limited apa aja, mulai dari response header sebelum lo nulis satu baris pun retry logic. API-nya biasanya udah ngasih tau lo harus ngapain.

Back to Blog

Related Posts

View All Posts »
React Query Stale Data: Kenapa Tampil Data Lama & Cara Fixnya [2026]
Web Development

React Query Stale Data: Kenapa Tampil Data Lama & Cara Fixnya [2026]

React Query menampilkan data lama saat pertama render tapi update setelah navigasi? Panduan ini membahas root cause-nya — query key yang salah, konfigurasi staleTime, dan placeholder data — lengkap dengan contoh kode fix untuk TanStack Query v4 dan v5.