OpenAI API Hata Kodları ve Rate Limit Yönetimi
OpenAI API ile production ortamında çalışmaya başladığınızda, mutlaka karşılaşacağınız iki büyük konu var: hata kodları ve rate limit yönetimi. Geliştirme ortamında her şey güzel çalışırken, gerçek yükü görünce sisteminizin nasıl davrandığını görmek sizi şaşırtabilir. Bu yazıda, OpenAI API’nin döndürdüğü hata kodlarını nasıl yorumlayacağınızı, rate limit mekanizmasının nasıl çalıştığını ve production’da bu sorunlarla başa çıkmak için kullanabileceğiniz pratik stratejileri ele alacağız.
OpenAI API Hata Kodlarını Anlamak
OpenAI API, standart HTTP durum kodlarını kullanıyor. Ama her kodun arkasında farklı bir senaryo olabilir ve nasıl tepki vereceğiniz buna göre değişiyor.
4xx Hataları: Sizin Tarafınızda Bir Sorun Var
400 Bad Request, istek formatınızda bir sorun olduğunda gelir. Genellikle yanlış parametreler, desteklenmeyen model adı veya token limitini aşan bir prompt bu hatayı tetikler.
401 Unauthorized, API anahtarınızın geçersiz ya da eksik olduğu anlamına gelir. Bunu production’da görmek ciddi bir alarm sinyali olmalı çünkü ya anahtarınız çalındı ya da yanlış ortam değişkeni kullanıyorsunuzdur.
403 Forbidden, hesabınızın belirli bir özelliğe erişim hakkı olmadığında gelir. Örneğin bazı modeller henüz genel erişime açık değilken denediğinizde bu kodu alırsınız.
404 Not Found, var olmayan bir model ya da endpoint’e istek attığınızda döner. Model adlarını hard-code etmek yerine konfigürasyon dosyasından okumak bu tür hataları önlemenin en iyi yolu.
429 Too Many Requests, rate limit’e takıldığınızda gelir. Bu yazının büyük bölümü bu konuya ayrılacak.
500, 502, 503 hataları ise OpenAI tarafındaki geçici sorunları gösterir. Bu hataları görünce paniklemek yerine retry mekanizmanızın devreye girmesini beklemelisiniz.
Hata Yanıtlarını Doğru Okumak
OpenAI’nin hata yanıtları JSON formatında gelir ve içinde değerli bilgiler bulunur. Bunu düzgün parse etmeden sadece HTTP koduna bakarak karar vermek eksik kalır.
# Basit bir hata testi yapalim
curl -s -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer sk-yanlis-anahtar"
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Merhaba"}]
}' | python3 -m json.tool
Yukarıdaki komut size şu şekilde bir yanıt dönecek:
# Beklenen hata yaniti formati
# {
# "error": {
# "message": "Incorrect API key provided...",
# "type": "invalid_request_error",
# "param": null,
# "code": "invalid_api_key"
# }
# }
# Hata kodunu direkt cekmek icin
curl -s -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d '{"model": "gpt-999-olmayan", "messages": [{"role": "user", "content": "test"}]}'
| python3 -c "import sys,json; r=json.load(sys.stdin); print(r['error']['code'])"
Rate Limit Mekanizması Nasıl Çalışır
OpenAI’nin rate limit sistemi iki farklı boyutta çalışır ve ikisini de anlamak çok önemli.
RPM ve TPM Limitleri
RPM (Requests Per Minute), dakikada yapabileceğiniz maksimum istek sayısıdır. Tier’ınıza göre bu değer değişir. Ücretsiz katmanda 3 RPM gibi çok düşük bir değerden başlarken, ücretli planlarda modelinize ve katmanınıza göre yüzlerce hatta binlere çıkabilir.
TPM (Tokens Per Minute), dakikada işleyebileceğiniz toplam token sayısıdır. Bunu anlamak için şunu düşünün: 10 RPM limitiniz var ama her isteğiniz 5000 token içeriyorsa, dakikada 50.000 token kullanıyorsunuzdur. TPM limitiniz 40.000 ise aslında 10 isteği tamamlayamazsınız.
TPD (Tokens Per Day), günlük toplam token limitidir. Özellikle yeni açılan hesaplarda bu limit devreye girer ve sürekli 429 alıyorsanız ama RPM/TPM limitinizi aşmadığınızı düşünüyorsanız, günlük limitinizi kontrol edin.
Rate Limit Header’larını Okumak
429 almadan önce OpenAI her yanıtta size mevcut durumunuzu header’lar aracılığıyla söyler. Bu header’ları okuyup proaktif önlem almak, reaktif retry’dan çok daha akıllıca bir yaklaşım.
# Header'lari gormek icin verbose curl kullan
curl -i -s -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d '{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Merhaba, nasılsın?"}],
"max_tokens": 50
}' 2>/dev/null | head -30
# Sadece rate limit headerlarini filtrele
curl -sI -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d '{"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "test"}]}'
| grep -i "x-ratelimit"
Göreceğiniz header’lar şunlar olacak:
- x-ratelimit-limit-requests: Dakikadaki maksimum istek sayınız
- x-ratelimit-limit-tokens: Dakikadaki maksimum token sayınız
- x-ratelimit-remaining-requests: Bu dakika içinde kalan istek hakkınız
- x-ratelimit-remaining-tokens: Bu dakika içinde kalan token hakkınız
- x-ratelimit-reset-requests: İstek limitinin ne zaman sıfırlanacağı
- x-ratelimit-reset-tokens: Token limitinin ne zaman sıfırlanacağı
- retry-after: 429 aldığınızda kaç saniye beklemeniz gerektiği
Exponential Backoff ile Retry Stratejisi
429 aldığınızda hemen tekrar denemek en kötü stratejidir. Hem limiti daha da zorlar hem de gereksiz yük yaratır. Doğru yaklaşım exponential backoff, yani her başarısız denemeden sonra bekleme süresini katlamak.
#!/usr/bin/env python3
# openai_retry.py - Production hazir retry mekanizmasi
import time
import random
import requests
import os
def openai_request_with_retry(payload, max_retries=5):
"""
Exponential backoff ile OpenAI API istegi yapar.
Sadece yeniden denenebilir hatalar icin retry uygular.
"""
api_key = os.environ.get("OPENAI_API_KEY")
url = "https://api.openai.com/v1/chat/completions"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
# Yeniden denenebilir HTTP kodlari
retryable_codes = {429, 500, 502, 503, 504}
for attempt in range(max_retries):
try:
response = requests.post(url, json=payload, headers=headers, timeout=30)
# Basarili yanit
if response.status_code == 200:
return response.json()
# Yeniden denenemez hatalar - direkt firlatiyoruz
if response.status_code not in retryable_codes:
error_data = response.json().get("error", {})
raise ValueError(f"Kalici hata [{response.status_code}]: {error_data.get('message')}")
# Rate limit veya gecici hata - retry-after header'ini kontrol et
if response.status_code == 429:
retry_after = float(response.headers.get("retry-after", 0))
if retry_after > 0:
print(f"Rate limit! {retry_after:.1f} saniye bekleniyor...")
time.sleep(retry_after)
continue
# Exponential backoff hesapla: 2^attempt + rastgele jitter
wait_time = (2 ** attempt) + random.uniform(0, 1)
print(f"Deneme {attempt + 1}/{max_retries} basarisiz. {wait_time:.1f}s bekleniyor...")
time.sleep(wait_time)
except requests.exceptions.Timeout:
wait_time = (2 ** attempt) + random.uniform(0, 1)
print(f"Timeout! {wait_time:.1f}s bekleniyor...")
time.sleep(wait_time)
raise RuntimeError(f"{max_retries} denemeden sonra basari saglanamadi")
# Test
payload = {
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Python'da liste comprehension nedir?"}],
"max_tokens": 150
}
result = openai_request_with_retry(payload)
print(result["choices"][0]["message"]["content"])
Token Sayımı ve Proaktif Limit Yönetimi
Rate limit’e takılmadan önce token sayısını tahmin etmek, sisteminizin stabilitesi için kritik önem taşır. tiktoken kütüphanesi bunu yapmanızı sağlar.
#!/usr/bin/env python3
# token_manager.py - Istek gondermeden once token sayisi hesapla
import tiktoken
import os
def count_tokens_for_messages(messages, model="gpt-4o"):
"""
Bir mesaj listesinin kac token kullanacagini hesaplar.
Bu fonksiyon istek gondermeden once cagrilmali.
"""
try:
encoding = tiktoken.encoding_for_model(model)
except KeyError:
encoding = tiktoken.get_encoding("cl100k_base")
# Her mesaj icin sabit overhead var
tokens_per_message = 3
tokens_per_name = 1
total_tokens = 0
for message in messages:
total_tokens += tokens_per_message
for key, value in message.items():
total_tokens += len(encoding.encode(str(value)))
if key == "name":
total_tokens += tokens_per_name
total_tokens += 3 # Yanit icin baslangic tokeni
return total_tokens
def smart_truncate_messages(messages, max_tokens=3000, model="gpt-4o"):
"""
Mesaj listesi cok uzunsa, sistem mesajini koruyarak
eski konusma gecmisini kirpir.
"""
system_messages = [m for m in messages if m["role"] == "system"]
other_messages = [m for m in messages if m["role"] != "system"]
current_tokens = count_tokens_for_messages(messages, model)
while current_tokens > max_tokens and len(other_messages) > 1:
# En eski mesaji kaldir (sistem mesajlari korunuyor)
other_messages.pop(0)
combined = system_messages + other_messages
current_tokens = count_tokens_for_messages(combined, model)
final_messages = system_messages + other_messages
print(f"Toplam token: {current_tokens} (max: {max_tokens})")
return final_messages
# Kullanim ornegi
messages = [
{"role": "system", "content": "Sen yardimci bir asistansin."},
{"role": "user", "content": "Python hakkinda bir soru sormak istiyorum."},
{"role": "assistant", "content": "Tabii, sormaktan cekinmeyin!"},
{"role": "user", "content": "Liste comprehension nasil calisir?"}
]
token_count = count_tokens_for_messages(messages)
print(f"Bu istek {token_count} token kullanacak")
# Limitin asimini kontrol et
tpm_limit = 10000 # Ornek TPM limitiniz
if token_count > tpm_limit * 0.8:
print("UYARI: Token sayisi TPM limitinin %80'ini asiyor!")
messages = smart_truncate_messages(messages, max_tokens=2000)
Batch Processing ile Rate Limit Dostu Yuk Dagitimi
Toplu işlem yapmanız gerektiğinde, istekleri sırayla göndermek yerine kontrollü bir hız sınırlayıcı kullanmak çok daha verimli sonuç verir.
#!/bin/bash
# batch_processor.sh - Dosyadan sorulari okuyup rate-limit dostu sekilde isler
INPUT_FILE="${1:-sorular.txt}"
OUTPUT_FILE="${2:-yanıtlar.jsonl}"
REQUESTS_PER_MINUTE=20
SLEEP_BETWEEN=$(echo "scale=2; 60 / $REQUESTS_PER_MINUTE" | bc)
if [ ! -f "$INPUT_FILE" ]; then
echo "Hata: $INPUT_FILE bulunamadi"
exit 1
fi
echo "Islem basliyor. Dakikada $REQUESTS_PER_MINUTE istek, aralarinda ${SLEEP_BETWEEN}s bekleme"
echo "Cikti: $OUTPUT_FILE"
total=$(wc -l < "$INPUT_FILE")
current=0
while IFS= read -r soru; do
current=$((current + 1))
echo "[$current/$total] Islem yapiliyor..."
payload=$(python3 -c "
import json, sys
soru = sys.argv[1]
print(json.dumps({
'model': 'gpt-4o-mini',
'messages': [{'role': 'user', 'content': soru}],
'max_tokens': 200
}))
" "$soru")
yanit=$(curl -s -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d "$payload")
# Hata kontrolu
if echo "$yanit" | grep -q '"error"'; then
hata_kodu=$(echo "$yanit" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r['error'].get('code','bilinmeyen'))")
echo " Hata: $hata_kodu - Atlanıyor"
echo "{"soru": $(echo "$soru" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read().strip()))"), "yanit": null, "hata": "$hata_kodu"}" >> "$OUTPUT_FILE"
else
echo "$yanit" | python3 -c "
import json, sys
r = json.load(sys.stdin)
cikti = {
'tokens_kullanilan': r['usage']['total_tokens'],
'yanit': r['choices'][0]['message']['content']
}
print(json.dumps(cikti, ensure_ascii=False))
" >> "$OUTPUT_FILE"
echo " Tamamlandi"
fi
# Son islemde bekleme
if [ "$current" -lt "$total" ]; then
sleep "$SLEEP_BETWEEN"
fi
done < "$INPUT_FILE"
echo "Tum islemler tamamlandi. Sonuclar: $OUTPUT_FILE"
Caching ile API Maliyetini Azaltmak
Aynı sorular için defalarca API çağrısı yapmak hem maliyetli hem de rate limit açısından gereksiz yük demek. Basit bir hash tabanlı cache sistemi bunu önler.
#!/usr/bin/env python3
# cached_openai.py - Hash tabanlı yanit onbellegi
import hashlib
import json
import os
import time
import requests
CACHE_DIR = "/tmp/openai_cache"
CACHE_TTL = 3600 # 1 saat
def get_cache_key(payload):
"""
Istek payload'indan benzersiz bir cache anahtari uretir.
max_tokens gibi deterministik olmayan parametreler dahil edilir.
"""
cache_data = {
"model": payload.get("model"),
"messages": payload.get("messages"),
"temperature": payload.get("temperature", 1.0),
"max_tokens": payload.get("max_tokens")
}
payload_str = json.dumps(cache_data, sort_keys=True)
return hashlib.sha256(payload_str.encode()).hexdigest()
def get_from_cache(cache_key):
os.makedirs(CACHE_DIR, exist_ok=True)
cache_file = os.path.join(CACHE_DIR, f"{cache_key}.json")
if not os.path.exists(cache_file):
return None
# TTL kontrolu
file_age = time.time() - os.path.getmtime(cache_file)
if file_age > CACHE_TTL:
os.remove(cache_file)
return None
with open(cache_file, "r") as f:
cached = json.load(f)
print(f"[CACHE HIT] {cache_key[:8]}... ({int(file_age)}s once cache'lendi)")
return cached
def save_to_cache(cache_key, response):
os.makedirs(CACHE_DIR, exist_ok=True)
cache_file = os.path.join(CACHE_DIR, f"{cache_key}.json")
with open(cache_file, "w") as f:
json.dump(response, f, ensure_ascii=False)
def cached_openai_request(payload):
cache_key = get_cache_key(payload)
# Once cache kontrol et
cached_response = get_from_cache(cache_key)
if cached_response:
return cached_response
# Cache'de yok, API'ye sor
print(f"[API CALL] {cache_key[:8]}... icin API'ye istek gonderiliyor")
api_key = os.environ.get("OPENAI_API_KEY")
response = requests.post(
"https://api.openai.com/v1/chat/completions",
json=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
},
timeout=30
)
response.raise_for_status()
result = response.json()
# Cache'e kaydet
save_to_cache(cache_key, result)
print(f"[CACHED] Yanit cache'e kaydedildi")
return result
# Ayni sorgu iki kez - ikincisi cache'den gelmeli
payload = {
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Python nedir? Kisaca acikla."}],
"max_tokens": 100,
"temperature": 0
}
print("--- Birinci istek ---")
r1 = cached_openai_request(payload)
print(r1["choices"][0]["message"]["content"][:100])
print("n--- Ikinci istek (cache'den gelmeli) ---")
r2 = cached_openai_request(payload)
print(r2["choices"][0]["message"]["content"][:100])
Monitoring ve Alert Sistemi Kurmak
Rate limit sorunlarını gerçek zamanlı takip etmek, reactive değil proactive olmayı sağlar. Basit bir monitoring scripti production ortamında hayat kurtarır.
#!/bin/bash
# openai_monitor.sh - Rate limit durumunu surekli izle ve alert ver
LOG_FILE="/var/log/openai_rate_monitor.log"
ALERT_THRESHOLD=80 # Kalan hakkin yuzde kaci kaldiysa alert ver
CHECK_INTERVAL=30 # Saniye cinsinden kontrol araligi
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_rate_status() {
# Kucuk bir test istegi gonderip header'lari oku
response_headers=$(curl -sI -X POST https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}],"max_tokens":1}'
2>/dev/null)
http_code=$(echo "$response_headers" | grep "^HTTP" | awk '{print $2}')
if [ "$http_code" = "429" ]; then
retry_after=$(echo "$response_headers" | grep -i "retry-after" | awk '{print $2}' | tr -d 'r')
log "KRITIK: Rate limit asildi! $retry_after saniye bekleniyor"
# Buraya Slack/email/PagerDuty bildirimi eklenebilir
return 1
fi
remaining_req=$(echo "$response_headers" | grep -i "x-ratelimit-remaining-requests" | awk '{print $2}' | tr -d 'r')
limit_req=$(echo "$response_headers" | grep -i "x-ratelimit-limit-requests" | awk '{print $2}' | tr -d 'r')
remaining_tokens=$(echo "$response_headers" | grep -i "x-ratelimit-remaining-tokens" | awk '{print $2}' | tr -d 'r')
if [ -n "$limit_req" ] && [ -n "$remaining_req" ] && [ "$limit_req" -gt 0 ]; then
used_percent=$(( (limit_req - remaining_req) * 100 / limit_req ))
log "Istek kullanimi: %$used_percent (Kalan: $remaining_req/$limit_req) | Kalan token: $remaining_tokens"
if [ "$used_percent" -ge "$ALERT_THRESHOLD" ]; then
log "UYARI: Istek kullanimi esik degeri asti! (%$used_percent >= %$ALERT_THRESHOLD)"
fi
fi
}
log "OpenAI rate limit izleme baslatildi (Esik: %$ALERT_THRESHOLD, Aralik: ${CHECK_INTERVAL}s)"
while true; do
check_rate_status
sleep "$CHECK_INTERVAL"
done
Gerçek Dünya Senaryosu: Müşteri Destek Botu
Bir e-ticaret şirketinde müşteri destek botu kuruduğunuzu düşünün. Gün içinde trafik dalgalanıyor, sabah saatlerinde çok az istek gelirken öğle ve akşam saatlerinde pik yapıyor. Bu senaryoda yapmanız gerekenler:
- Model seçimi: Basit sorular için
gpt-4o-minikullanın, karmaşık şikayetler içingpt-4o‘ya yönlendirin. Bu sayede hem maliyet hem de token kullanımı optimize olur. - Önceliklendirme: VIP müşterilerin isteklerini önce işleyin, kuyruğu buna göre düzenleyin.
- Circuit breaker: Ardı ardına 5 429 hatası aldıysanız sistemi 60 saniye durdurur ve ardından tekrar denersiniz. Sürekli deneyen bir sistem, rate limit cezasını daha da uzatır.
- Fallback mesajı: Rate limit aşıldığında kullanıcıya “Şu anda yoğun trafik var, en kısa sürede dönüş yapacağız” mesajı göstermek ve isteği kuyruğa almak, sistemi kullanışlı tutar.
- Log analizi: Hangi saatlerde pik oluştuğunu analiz ederek TPM limitinizi bu saatlere göre planlayın.
Sonuç
OpenAI API ile çalışırken hata yönetimi ve rate limit kontrolü, bir afterthought değil, sistemin temel mimarisinin parçası olmalı. 429 hatası kaçınılmaz, ama sisteminizin buna nasıl tepki verdiği tamamen sizin elinizde. Exponential backoff ile akıllı retry, token sayımı ile proaktif limit yönetimi, caching ile gereksiz istek azaltma ve monitoring ile gerçek zamanlı görünürlük; bu dört bileşeni bir araya getirdiğinizde production’da kararlı ve güvenilir bir sistem kurmuş olursunuz.
Rate limit’i bir engel olarak değil, sisteminizi daha iyi tasarlamak için bir rehber olarak görmek doğru yaklaşım. Limitlere sürekli çarpıyorsanız bu, ya mimari bir sorun olduğunun ya da plan yükseltme zamanının geldiğinin işareti. Her iki durumda da yukarıdaki araçlar size doğru kararı vermek için gereken verileri sağlayacak.
