Token bucket rate limiting

토큰 버킷 속도 제한

Requests spend replenishing tokens, controlling average API rate while permitting short bursts.

···
html
<div class="demo"><div class="head"><b>TOKEN BUCKET</b><span id="status">allowed</span></div><div class="scene"><div class="request">REQUEST<br><strong>→</strong></div><div class="bucket"><div class="tokens" id="tokens"></div><small id="count">4 / 4 tokens</small></div><div class="response" id="response">200<br>OK</div></div><div class="foot"><span>refill: 1 token / tick</span><span>tap to send</span></div></div>
css
.demo{width:min(94vw,690px);height:min(86vh,310px);padding:clamp(10px,2.4vmin,20px);border:1px solid var(--line);border-radius:14px;background:var(--surface);display:flex;flex-direction:column;gap:clamp(7px,2vmin,14px);font:600 clamp(9px,2.6vmin,15px)/1.3 ui-monospace,monospace}.head,.foot{display:flex;justify-content:space-between;gap:8px}.head b{color:var(--accent)}.foot{color:var(--muted);font-size:.82em}.scene{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;gap:clamp(7px,2.5vmin,24px)}.request,.response{border:1px solid var(--line);border-radius:8px;padding:.7em;min-width:20%;text-align:center;background:var(--bg)}.request strong{font-size:1.6em;color:var(--accent)}.response{color:var(--accent);transition:color .2s,border-color .2s}.response.denied{color:var(--accent-3);border-color:var(--accent-3)}.bucket{width:38%;height:67%;min-height:72px;border:2px solid var(--accent);border-top:0;border-radius:0 0 10px 10px;padding:8px;display:flex;flex-direction:column;justify-content:end;align-items:center;gap:8px}.tokens{width:100%;display:flex;align-items:end;justify-content:center;gap:5%;height:65%}.token{background:var(--accent);width:20%;height:65%;border-radius:4px 4px 0 0;transition:opacity .2s,transform .2s}.token.empty{opacity:.14;transform:scaleY(.2);transform-origin:bottom}.bucket small{color:var(--muted);white-space:nowrap}
js
let tokens=4,tick=0;const holder=document.getElementById('tokens');holder.innerHTML='<i class="token"></i>'.repeat(4);function draw(){[...holder.children].forEach((el,i)=>el.classList.toggle('empty',i>=tokens));document.getElementById('count').textContent=tokens+' / 4 tokens'}function request(){const ok=tokens>0;if(ok)tokens--;document.getElementById('status').textContent=ok?'allowed':'rate limited';const response=document.getElementById('response');response.innerHTML=ok?'200<br>OK':'429<br>WAIT';response.classList.toggle('denied',!ok);draw()}draw();request();const timer=setInterval(()=>{tick++;if(tick%2===0)tokens=Math.min(4,tokens+1);request()},750);document.querySelector('.demo').addEventListener('pointerdown',()=>{clearInterval(timer);request()})

A token bucket refills at a fixed rate up to its capacity. Each request spends a token; with none left, the request waits or is rejected. Stored tokens permit a short burst above the refill rate.

The demo spends tokens as requests arrive and refills them over time. A production API also needs a caller key, atomic updates in shared storage, and useful retry guidance after rejection.

When to use

Use it to curb API abuse while still accepting brief bursts from legitimate callers.

Open as page ↗