API rate limiting, bir API'nin belirli bir zaman aralığında bir istemciden kaç istek kabul edeceğini önceden belirleyen bir kontrol mekanizmasıdır; Türkçede bu kavrama genellikle istek sınırlama denir. Basitçe söylemek gerekirse, bir API "dakikada en fazla 60 istek kabul ediyorum" dediğinde, bu sınırı aşan istekler ya reddedilir ya da bir süre bekletilir. Bu yazıda bu mantığın günlük hayattaki karşılığını, neden gerekli olduğunu ve Python ile Java'da nasıl temel düzeyde uygulanabileceğini adım adım göreceğiz.
Rate Limiting Nedir? Günlük Hayattan Bir Benzetme
Rate limiting kavramını anlamanın en kolay yolu, günlük hayattan tanıdık bir sahneye bakmaktır. Bir bankada sıra numarası alıp gişede işlem yaptırmayı düşünün: gişe görevlisi aynı anda tek bir kişiye hizmet verir, geri kalan herkes sırasını bekler. Eğer aynı anda yüzlerce kişi gişeye hücum etse, sistem tıkanır ve kimse düzgün hizmet alamaz. Sıra numarası mantığı, aslında hem gişeyi korur hem de herkesin adil biçimde hizmet almasını sağlar.
API dünyasında da durum çok benzerdir. Bir sunucu, aynı anda gelen çok sayıda isteği işlemek zorunda kaldığında yavaşlar ya da tamamen çöker. Rate limiting, tıpkı gişedeki sıra mantığı gibi, "şu kadar süre içinde en fazla bu kadar istek kabul ederim" diyerek sunucuyu korur. Bir istemci (kullanıcı, uygulama ya da bot) belirlenen sınırı aştığında, API ona genellikle bir hata mesajıyla yanıt verir ve isteğin biraz sonra tekrar denenmesini ister.
Bu mekanizma yalnızca büyük ölçekli sistemlere özgü değildir; küçük bir öğrenci projesinde bile karşınıza çıkabilir. Bir hava durumu API'sine saniyede onlarca istek gönderen bir betik yazdığınızda, o API'nin sizi geçici olarak engellemesi tamamen rate limiting mantığının bir sonucudur. Bu nedenle backend geliştirme öğrenen herkesin bu kavramı temel düzeyde kavraması, hem kendi API'lerini tasarlarken hem de başkalarının API'lerini tüketirken işine yarar.
API'lerde Rate Limiting Neden Gereklidir?
Bir API'nin rate limiting içermemesi, ilk bakışta zararsız görünse de pratikte ciddi sorunlara yol açabilir. En temel neden, kötüye kullanımın önlenmesidir. Sınırsız istek hakkı tanıyan bir giriş (login) uç noktası, kaba kuvvet (brute force) saldırılarına açık kapı bırakır; bir saldırgan aynı kullanıcı adı için binlerce şifre denemesini saniyeler içinde gönderebilir. Rate limiting, bu tür denemeleri belirli bir sayının üzerine çıktığında otomatik olarak yavaşlatır veya durdurur.
İkinci önemli neden, sunucu kaynaklarının korunmasıdır. Her API isteği; CPU zamanı, bellek ve çoğu zaman bir veritabanı bağlantısı tüketir. Sınırsız istek akışı, sunucunun veritabanı bağlantı havuzunu tüketip diğer meşru kullanıcıların isteklerine cevap veremez hale gelmesine neden olabilir. Bu durumda hizmet dışı kalma riski gerçek bir tehdit haline gelir.
Üçüncü neden ise adil kullanım ilkesidir. Bir API'yi paylaşan yüzlerce kullanıcı olduğunu düşünün; eğer bir kullanıcı ya da uygulama aşırı yoğun istek gönderirse, diğer kullanıcıların istekleri geride kalır ya da hiç yanıtlanmaz. Rate limiting, herkese belirli bir pay ayırarak bu dengesizliği önler.
- Beklenmedik trafik artışlarında sunucunun çökmesi yerine kontrollü biçimde yanıt vermesi
- Kötü niyetli botların veri kazıma (scraping) girişimlerinin yavaşlatılması
- Ücretsiz ve ücretli plan kullanıcıları arasında istek hakkının dengeli dağıtılması
Rate limiting olmayan bir servis, tek bir kötü niyetli istemci ya da hatalı yazılmış bir döngü yüzünden tüm kullanıcılar için erişilemez hale gelebilir. Bu tür temel API tasarım kararlarını anlamak, backend tarafında çalışacak her Python veya Java geliştiricisi için önemlidir; bu konudaki hazırlığınızı ölçmek isterseniz ücretsiz kodlama bilgisi testi ile mevcut seviyenizi görebilirsiniz.
Sabit Pencere (Fixed Window) Mantığı

Sabit pencere (fixed window) yaklaşımı, rate limiting'in en basit ve en sık karşılaşılan uygulamasıdır. Mantık şudur: zaman, örneğin bir dakikalık sabit dilimlere bölünür ve her dilim içinde bir istemcinin gönderebileceği istek sayısına üst sınır konur. Diyelim ki sınır dakikada 100 istek; saat 12:00:00 ile 12:01:00 arasında bu istemci en fazla 100 istek gönderebilir, 101. istek reddedilir.
Bu yaklaşımın çalışma şekli bir sayaç mantığına dayanır. Her yeni pencere başladığında (yani yeni dakika geldiğinde) sayaç sıfırlanır ve istekler yeniden sıfırdan sayılmaya başlar. Sunucu tarafında bu genellikle istemci kimliğine (kullanıcı ID'si, API anahtarı ya da IP adresi) bağlı basit bir sayaç ve bir zaman damgasıyla tutulur. Uygulaması kolay olduğu için küçük ve orta ölçekli projelerde, özellikle öğrenme amaçlı API'lerde sıkça tercih edilir.
Ancak sabit pencerenin bilinen bir zayıflığı vardır: pencere sınırlarında ani yığılma (burst) riski. Örneğin dakikalık sınır 100 ise, bir istemci 12:00:59'da 100 istek gönderip 12:01:00'da tekrar 100 istek gönderebilir; bu durumda iki saniyelik bir aralıkta toplam 200 istek sunucuya ulaşmış olur. Bu, sabit pencerenin basitliğinin getirdiği kabul edilebilir bir dengedir; ileri düzey sistemlerde bu sorunu yumuşatan farklı stratejiler bulunur, fakat temel mantığı kavramak için sabit pencere yeterli ve öğretici bir başlangıç noktasıdır.
Bu tür sayaç tabanlı mantıkları kurabilmek, temelde koşul ifadeleri, zaman fonksiyonları ve basit veri yapılarıyla çalışmayı gerektirir. Bu becerileri sağlam bir temelde adım adım geliştirmek isteyenler için Python video eğitim serisi bu tarz pratik senaryoları örneklerle işler. İlerleyen bölümde bu mantığın Python kodu üzerinden nasıl somutlaştığını birlikte inceleyeceğiz.
Token Bucket Mantığı: Sabit Pencereye Alternatif
Sabit pencere yaklaşımı basit olsa da gerçek dünyada beklenmedik bir sorun yaratır: pencere sıfırlandığı anda kullanıcı arka arkaya çok sayıda isteği aynı saniyeye sığdırabilir. Bu problemi çözmek için geliştirilen token bucket (jeton kovası) mantığı, isteği zaman dilimine göre değil, elde bulunan "jeton" miktarına göre değerlendirir. Mantık şöyle işler: sistemde hayali bir kova vardır ve bu kova belirli bir hızda otomatik olarak jetonlarla dolar. Bir istemci API'ye istek attığında, kovadan bir jeton harcanır. Kovada jeton olduğu sürece istekler kabul edilir; kova boşaldığında ise yeni istekler reddedilir ya da bekletilir.
Bu yöntemin en büyük avantajı, ani istek patlamalarına (burst) karşı esnek davranmasıdır. Örneğin bir kullanıcı uzun süre API'yi hiç kullanmadıysa, kovası zamanla dolmuş olur ve bu birikmiş jetonlarla kısa bir süre içinde normalden fazla istek atabilir. Sabit pencere mantığında böyle bir esneklik yoktur; pencere içindeki limit ne kadar erken tüketilirse tüketilsin, yeni pencereye kadar beklemek zorunludur. Token bucket ise "biriken hakkı bir anda kullanabilme" imkanı sunarak, gerçek kullanım senaryolarına daha uygun bir davranış sergiler.
Bu yaklaşımı bir su deposuna benzetmek mümkündür: depo sabit bir debiyle dolar, ancak depoda su birikmişse musluğu açtığınızda anlık olarak fazla su alabilirsiniz; depo boşaldığında ise doluma yetişmeniz gerekir. API tarafında da benzer şekilde, kova dolu olduğu sürece kullanıcı rahat hareket eder, kova tükendiğinde ise sistem yeni jetonlar birikene kadar istekleri geciktirir veya reddeder. Bu nedenle token bucket, özellikle kullanıcı davranışının düzensiz ama toplamda kontrol altında tutulması istenen API'lerde tercih edilen bir modeldir. Sabit pencerenin sertliğine karşı token bucket'ın sunduğu bu esneklik, ileride göreceğimiz karşılaştırma tablosunda daha net ortaya çıkacaktır.
HTTP 429 (Too Many Requests) Ne Anlama Gelir?
Bir API'ye izin verilen sınırın üzerinde istek gönderildiğinde sunucunun istemciye verdiği standart cevap, HTTP 429 Too Many Requests durum kodudur. Bu kod, sunucunun isteği teknik olarak anlayabildiğini ama şu anda işleme almayacağını belirtir; yani mesaj aslında "çok fazla istek attın, biraz yavaşla" anlamına gelir. Rate limiting mekanizması devreye girip bir istemcinin tanımlı sınırı aştığını tespit ettiğinde, sunucu isteği reddedip bu kodu döndürür.
API tasarımında 429 yanıtı genellikle tek başına gönderilmez; istemciye ne kadar süre beklemesi gerektiğini bildiren ek bilgilerle birlikte döner. Bu amaçla kullanılan başlık kavramsal olarak "ne zaman tekrar deneyebilirsin" bilgisini taşır ve istemcinin rastgele tekrar denemek yerine belirli bir süre sonra yeniden istek göndermesine imkan tanır. Böylece hem sunucu gereksiz yükten korunur hem de istemci ne zaman tekrar deneme yapması gerektiğini net biçimde bilir.
İstemci tarafında 429 koduyla karşılaşıldığında yapılması gereken davranış bellidir: isteği hemen ve art arda tekrar göndermek yerine bir süre beklemek ve ardından yeniden denemektir. Bu, hem kullanıcı deneyimini bozmadan hem de sunucuyu daha da yormadan ilerlemenin yoludur. Basit bir uygulamada bu bekleme süresi sabit bir değer olabilirken, daha olgun sistemlerde her denemede bekleme süresini kademeli olarak artırmak da yaygın bir pratiktir. Önemli olan, 429 kodunu bir hata değil, bir uyarı ve yönlendirme sinyali olarak ele almaktır; istemci tarafı kodu bu sinyale göre davranış geliştirirse, hem API sağlıklı çalışmaya devam eder hem de istemci gereksiz yere engellenmemiş olur.
Python ile Basit Bir Rate Limiter Örneği

Sabit pencere mantığının koda nasıl döküldüğünü görmek, konuyu kalıcı biçimde kavramanın en pratik yoludur. Aşağıdaki örnek, dış kütüphane kullanmadan yalnızca zaman damgası ve sayaç mantığıyla çalışan basit bir rate limiter fonksiyonu gösterir. Bu tür temel yapı taşlarını adım adım kurabilmek, birebir Python dersleri kapsamında olduğu gibi düzenli pratik yapmayı gerektirir.
import time
istek_sayaci = 0
pencere_baslangici = time.time()
LIMIT = 5 # pencere başına izin verilen istek sayısı
PENCERE_SURESI = 10 # saniye cinsinden pencere uzunluğu
def istek_gonder():
global istek_sayaci, pencere_baslangici
simdi = time.time()
# pencere süresi dolduysa sayaç ve başlangıcı sıfırla
if simdi - pencere_baslangici >= PENCERE_SURESI:
istek_sayaci = 0
pencere_baslangici = simdi
if istek_sayaci < LIMIT:
istek_sayaci += 1
return "200 OK - istek kabul edildi"
else:
return "429 Too Many Requests"
Kod incelendiğinde fixed window mantığının üç temel bileşeni net biçimde görülür: mevcut zamanı ölçen bir zaman damgası, o pencere içindeki istek sayısını tutan bir sayaç ve pencere süresi dolduğunda her ikisini de sıfırlayan bir kontrol. pencere_baslangici değişkeni son sıfırlamanın ne zaman yapıldığını hatırlar; her yeni istekte bu değerle şu anki zaman arasındaki fark PENCERE_SURESI değerini aşıyorsa, sistem yeni bir pencereye geçildiğini anlar ve sayaç yeniden sıfırdan başlar. Sayaç limitin altındaysa istek kabul edilir ve sayaç bir artırılır; limit doldurulmuşsa fonksiyon 429 durumunu temsil eden bir sonuç döner.
Bu örnek kasıtlı olarak sade tutulmuştur; gerçek bir API sunucusunda bu mantık genellikle her istemci için ayrı ayrı çalıştırılır ve bir istek işleme katmanına (middleware) entegre edilir. Ancak temel çalışma prensibi burada gösterilenle aynıdır: zamanı ölç, sayacı kontrol et, gerekirse sıfırla. Bu üç adımı kavramış bir öğrenci, ister Python ister başka bir dilde olsun, herhangi bir rate limiting mekanizmasının iskeletini kolaylıkla okuyup anlayabilir.
Java'da Benzer Mantık Nasıl Kurulur?
Python örneğinde kurduğumuz zaman penceresi ve sayaç mantığı, aslında hiçbir dile özgü değildir; bu, saf bir algoritma sorunudur ve aynı mantık Java'da da birebir aynı adımlarla kurulabilir. Değişen tek şey kullanılan sözdizimi ve bazı yapı taşlarıdır, çözümün omurgası değişmez.
Java'da bir isteğin "şu an" ne zaman geldiğini öğrenmek için genellikle System.currentTimeMillis() metodu kullanılır; bu, Python'daki time.time() fonksiyonunun gördüğü işi görür ve milisaniye cinsinden bir zaman damgası döndürür. Sabit pencere mantığını kurmak için ihtiyaç duyulan diğer parça ise basit bir sayaç değişkenidir: pencere başlangıç zamanını tutan bir alan ve o pencere içinde kaç istek geldiğini sayan bir tam sayı değişkeni yeterlidir. Yeni bir istek geldiğinde mevcut zaman damgası ile pencere başlangıcı karşılaştırılır; pencere süresi dolmuşsa sayaç sıfırlanıp pencere yeniden başlatılır, dolmamışsa sayaç bir artırılır ve limit aşılmışsa istek reddedilir.
Burada Python'dan farklı olarak dikkat edilmesi gereken bir nokta vardır: Java'da bu sayaç ve zaman değişkenlerine aynı anda birden fazla isteğin (yani birden fazla iş parçacığının) erişebileceği durumlar oluşabilir. Bir web sunucusu tipik olarak gelen her isteği ayrı bir iş parçacığında işleyebildiği için, paylaşılan sayaç değişkenine güvenli erişim sağlanmalıdır. Bu noktada synchronized anahtar kelimesi veya eşzamanlılığa uygun sayaç yapıları devreye girer; amaç, iki isteğin aynı anda sayacı okuyup güncelleyerek limitin yanlışlıkla aşılmasını önlemektir. Bu konu ileri düzey bir eşzamanlılık meselesi gibi görünse de temelde çözülen problem Python'dakiyle aynıdır: "bu pencerede kaç istek geldi ve limit doldu mu?"
Rate limiting gibi konular, bir algoritmanın nasıl farklı dillerde aynı mantıkla yeniden ifade edilebildiğini görmek için oldukça öğreticidir. Python'da öğrenilen bir çözümü Java'ya taşımak, iki dilin sözdizimi farklarını kavramanın yanı sıra algoritmik düşünmeyi pekiştirir; bu yüzden Python temelini oturtan bir öğrencinin Java'ya geçişi genellikle beklenenden daha akıcı olur. Berk Akademi'nin 1-1 özel Java dersleri kapsamında da öğrencilere tam olarak bu yaklaşım kazandırılır: önce mantığı kavrat, sonra dile özgü ayrıntılara geç.
Sabit Pencere ile Token Bucket Karşılaştırması
İki yöntem de aynı soruna farklı açılardan yaklaşır ve hangisinin seçileceği, projenin ihtiyaçlarına göre değişir. Aşağıdaki tablo, iki yaklaşımı temel kriterler üzerinden özetler:
| Kriter | Sabit Pencere (Fixed Window) | Token Bucket |
|---|---|---|
| Uygulama kolaylığı | Çok basit; tek bir sayaç ve zaman damgasıyla kurulabilir | Biraz daha fazla mantık gerektirir; jeton sayısı ve doldurma hızı takip edilir |
| Burst (ani patlama) toleransı | Düşük; pencere sınırında ani yoğunluklara karşı hassastır | Daha yüksek; biriken jetonlar sayesinde kısa süreli ani yüklenmelere izin verebilir |
| Kaynak tüketimi | Çok düşük; yalnızca bir sayaç ve bir zaman değeri tutulur | Sabit pencereye göre biraz daha fazla; jeton durumu sürekli güncellenir |
| Tipik kullanım senaryosu | Basit iç API'ler, öğrenme amaçlı projeler, düşük trafikli servisler | Değişken trafik desenine sahip servisler, kullanıcı deneyimini önceliklendiren API'ler |
Sabit pencerenin en büyük avantajı anlaşılırlığıdır; bir öğrencinin ilk rate limiting deneyimi için idealdir çünkü mantığı birkaç satırda özetlenebilir. Token bucket ise gerçek dünyada kullanıcıların davranışlarının hiç de "düzenli aralıklarla" gelmediği gerçeğini hesaba katar ve bu yüzden daha profesyonel API tasarımlarında tercih edilir. Ancak ikisi de aynı temel probleme çözüm arar: sistemi aşırı yükten korumak. Bir öğrenci için doğru yaklaşım, önce sabit pencereyi tam olarak kavramak, ardından token bucket'ın hangi eksikliği giderdiğini anlamaktır.
Rate Limiting Gerçek Hayatta Nerelerde Karşımıza Çıkar?
Rate limiting, soyut bir algoritma konusu olmaktan çok, gündelik yazılım geliştirme pratiğinin içine gömülü bir gerçekliktir. Üçüncü parti bir API'yi kullanan hemen her geliştirici, er ya da geç bir limitle karşılaşır: hava durumu verisi çeken bir servis, döviz kuru sağlayan bir API veya ödeme işlemlerini yöneten bir sağlayıcı, belirli bir zaman diliminde kaç istek kabul edeceğini kendi belirler. Bu limitler genellikle API sağlayıcısının belgelerinde tanımlıdır ve bir geliştiricinin bu kuralları anlamadan sistem tasarlaması, üretimde beklenmedik HTTP 429 hatalarıyla karşılaşmasına yol açar.
Kendi geliştirdiğimiz servislerde de rate limiting genellikle belirli uç noktalarda özellikle önemlidir. Örneğin bir giriş (login) formu, kısa sürede art arda çok sayıda deneme aldığında bu genellikle ya bir bot etkinliğine ya da bir kullanıcının şifresini defalarca yanlış denemesine işaret eder; her iki durumda da isteklerin sınırlandırılması sistemi korur. Aynı şekilde bir iletişim formu veya kayıt uç noktası da kısa sürede aşırı sayıda gönderim aldığında, bu genellikle istenmeyen otomatik trafik anlamına gelir ve rate limiting burada basit ama etkili bir savunma katmanı oluşturur.
Bu yazıda öğrenilen mantık, karmaşık bir altyapı gerektirmeden küçük bir kişisel projede rahatlıkla denenebilir. Bir öğrenci, kendi yazdığı basit bir API'ye veya hatta bir konsol uygulamasına, bu yazıdaki sabit pencere örneğini uyarlayarak "bu fonksiyon dakikada en fazla üç kez çalışabilir" gibi kurallar ekleyebilir. Bu tür küçük denemeler, teorik bilgiyi kalıcı hale getirmenin en etkili yoludur. Bu noktada rehberlik ve geri bildirim almak isteyenler için 1-1 özel ders seçeneği, öğrencinin kendi projesi üzerinden birebir ilerlemesini sağlayan pratik bir yol sunar.
Sık Sorulan Sorular
Rate limiting ile throttling arasındaki fark nedir?
Rate limiting genellikle bir isteğin tamamen reddedilmesi anlamına gelirken, throttling isteğin kabul edilip yavaşlatılması veya geciktirilmesi anlamına gelir. Pratikte iki terim çoğu zaman iç içe kullanılır, ancak kavramsal fark isteğin "reddedilmesi" ile "yavaşlatılması" arasındadır.
Her API'de rate limiting kullanmak zorunlu mudur?
Hayır, zorunlu değildir; ancak dışa açık, herkesin erişebildiği veya kaynak tüketimi yüksek işlemler yapan bir API için rate limiting eklemek neredeyse her zaman iyi bir pratiktir. Düşük trafikli, yalnızca güvenilir istemcilerin eriştiği iç sistemlerde bu ihtiyaç daha az öncelikli olabilir.
HTTP 429 hatası aldığımda istemci olarak ne yapmalıyım?
İsteği hemen tekrar göndermek yerine bir süre beklemek gerekir; birçok API bu bekleme süresini yanıt başlıklarında belirtir. Beklemeden sürekli tekrar deneme yapmak hem isteğin yine reddedilmesine hem de sunucu üzerinde gereksiz yüke yol açar.
Token bucket neden sabit pencereden daha esnek kabul edilir?
Çünkü kullanıcı davranışları gerçek hayatta düzenli aralıklarla gelmez; token bucket, biriken jetonlar sayesinde kısa süreli ani yüklenmelere (burst) izin verebildiği için sabit pencerenin katı sınır çizgisine göre daha gerçekçi bir kullanıcı deneyimi sunar.
Python'da rate limiting yapmak için mutlaka hazır bir kütüphane mi kullanılmalı?
Hayır, öğrenme aşamasında mantığı anlamak için hazır bir kütüphaneye ihtiyaç yoktur; bu yazıdaki gibi birkaç değişken ve koşul ifadesiyle temel bir rate limiter yazılabilir. Gerçek üretim ortamlarında hazır çözümler tercih edilebilir, ancak temel mantığı bilmeden bu araçları doğru kullanmak da zorlaşır.
Java'da rate limiting mantığını kurmak için hangi temel yapılar kullanılır?
Zaman damgası almak için System.currentTimeMillis(), geçen isteği saymak için basit bir sayaç değişkeni ve birden fazla isteğin aynı anda işlendiği durumlarda güvenli erişim sağlamak için synchronized gibi eşzamanlılık araçları temel yapı taşlarıdır.
Rate limiting, küçük görünen ama API tasarımının güvenilirliğini doğrudan etkileyen temel bir konudur; bu mantığı bir kez kavrayan bir geliştirici, hem Python hem Java projelerinde aynı çözümü rahatlıkla uygulayabilir. Bu tür temel backend kavramlarını sıfırdan ve uygulamalı biçimde öğrenmek isteyenler 1-1 özel Python dersleri ile bu yolculuğa başlayabilir.