API Yanıt Kodları: HTTP Status Kodlarını Anlama ve Doğru Kullanma
Bir API entegrasyonu üzerinde çalışırken en çok zaman kaybettiren şeylerden biri, dönen yanıt kodlarını tam olarak anlamamak ve hata ayıklama sürecinde yanlış yönlere koşturmaktır. “Neden 401 dönüyor, token doğru ya?” diye saatlerce uğraşmak, sonra autherization header’ını yanlış yazdığını fark etmek… Hepimiz yaşadık. Bu yazıda HTTP durum kodlarını sysadmin ve geliştirici gözüyle ele alacağız, gerçek dünya senaryolarıyla hangi kodun ne anlama geldiğini ve nasıl handle edilmesi gerektiğini konuşacağız.
HTTP Status Kodları Neden Bu Kadar Önemli?
HTTP protokolü, 1991’den bu yana istemci-sunucu iletişiminin temelini oluşturuyor. Status kodları ise bu iletişimin “konuşma dili” diyebileceğimiz yapılar. Sunucu size sadece veri göndermekle kalmıyor, aynı zamanda o verinin durumunu, isteğin başarılı olup olmadığını ve bir şeyler yanlış gittiyse bunun nerede yanlış gittiğini de söylüyor.
Bir API’yi doğru şekilde entegre etmek için bu kodları anlamak şart. Özellikle microservice mimarilerde, bir servisten gelen hatalı yanıt kodu zincirinin tamamını etkileyebilir. Monitoring sistemlerinizin anlamlı alertler üretmesi, retry mekanizmalarınızın doğru çalışması, kullanıcıya doğru hata mesajı gösterilmesi… Bunların hepsi status kodlarına bağlı.
Status kodları beş ana kategoriye ayrılır:
- 1xx: Bilgilendirme (Informational)
- 2xx: Başarı (Success)
- 3xx: Yönlendirme (Redirection)
- 4xx: İstemci Hatası (Client Error)
- 5xx: Sunucu Hatası (Server Error)
2xx: Her Şey Yolunda, Ama Hangisi?
2xx kodları “başarılı” demek ama aralarındaki farklar önemli. En çok karıştırılan kodlardan başlayalım.
200 OK, en yaygın başarı kodudur. GET, PUT, PATCH isteklerinde döner ve body içeriği döndürülür.
201 Created, POST isteği sonucunda yeni bir kaynak oluşturulduğunda kullanılır. Yanıtta genellikle Location header’ı da bulunur ve yeni kaynağın URL’ini gösterir.
204 No Content, başarılı ama döndürülecek içerik yok. DELETE işlemlerinde veya body döndürmek istemediğiniz PUT işlemlerinde kullanılır.
202 Accepted, isteği aldık ama henüz işlemedik. Asenkron işlemler için idealdir. Bir video dönüştürme servisi, dosyayı kuyruğa aldığında 202 döner.
Şimdi gerçek bir senaryo düşünelim. Bir kullanıcı kayıt API’si yazıyorsunuz ve curl ile test ediyorsunuz:
# Yeni kullanıcı oluşturma - 201 bekliyoruz
curl -X POST https://api.example.com/users
-H "Content-Type: application/json"
-H "Authorization: Bearer eyJhbGc..."
-d '{"username": "ahmet", "email": "[email protected]"}'
-v 2>&1 | grep -E "^< HTTP|^{|Location"
# Beklenen çıktı:
# < HTTP/2 201
# Location: https://api.example.com/users/12345
# Silme işlemi - 204 bekliyoruz, body gelmemeli
curl -X DELETE https://api.example.com/users/12345
-H "Authorization: Bearer eyJhbGc..."
-w "nHTTP Status: %{http_code}nResponse Size: %{size_download} bytesn"
-s -o /dev/null
# Beklenen çıktı:
# HTTP Status: 204
# Response Size: 0 bytes
Özellikle 204 response’u handle ederken dikkat edin. Bazı kütüphaneler body parse etmeye çalışır ve boş response’ta hata fırlatır. Bu yüzden status kodu kontrolü her zaman önce yapılmalı.
3xx: Yönlendirmeler ve Gizli Tuzaklar
Yönlendirmeler genellikle göz ardı edilir çünkü tarayıcılar otomatik follow eder. Ama API çağrılarında bu durum farklı.
301 Moved Permanently, kaynak kalıcı olarak taşındı. İstemciler URL’i güncellemeli.
302 Found, geçici yönlendirme. Sonraki isteklerde eski URL kullanılmaya devam edilmeli.
304 Not Modified, conditional GET’lerde kullanılır. Cache geçerli, yeni veri gönderilmeyecek.
307 Temporary Redirect ve 308 Permanent Redirect ise 302 ve 301’in method-preserving versiyonlarıdır. POST isteği POST olarak yönlendirilir, GET’e dönüştürülmez.
# curl ile redirect takibi - kaç hop yapıldığını görmek için
curl -L -v https://api.example.com/old-endpoint
-H "Authorization: Bearer token123"
2>&1 | grep -E "^< HTTP|^> (GET|POST)|Location:"
# Redirect olmadan, nereye yönlendirildiğini görmek için
curl -I https://api.eski-adres.com/endpoint
--max-redirs 0 2>&1
# Script'te redirect kontrolü
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}"
--max-redirs 0 https://api.example.com/resource)
if [ "$HTTP_CODE" -eq 301 ] || [ "$HTTP_CODE" -eq 308 ]; then
echo "Kalici yonlendirme var, URL guncellenmeli!"
REDIRECT_URL=$(curl -sI --max-redirs 0 https://api.example.com/resource
| grep -i "^location:" | awk '{print $2}' | tr -d 'r')
echo "Yeni URL: $REDIRECT_URL"
fi
API entegrasyonlarında 301 aldığınızda alarm zili çalmalı. Servis sağlayıcı URL yapısını değiştirmiş olabilir ve siz eski URL’e istek atmaya devam ediyorsunuzdur. Her redirect bir extra round-trip demek, yani latency artıyor.
4xx: İstemci Hataları – Sorun Bizde
4xx hataları en çok debug edilen kodlar. Burada sorun istemci tarafında, yani ya bizim kodumuzda ya da bizim gönderdiğimiz veride.
400 Bad Request, gönderilen istek sunucu tarafından anlaşılamadı. Genellikle malformed JSON, eksik required field veya tip uyumsuzluğu.
401 Unauthorized, kimlik doğrulama başarısız veya hiç yapılmamış. Header eksik veya token geçersiz.
403 Forbidden, kimlik doğrulama başarılı ama bu kaynağa erişim yetkisi yok. 401’den farkı önemli: 401’de kim olduğunuzu bilmiyoruz, 403’te biliyoruz ama izniniz yok.
404 Not Found, kaynak bulunamadı. Ama dikkat: Bazı API’ler güvenlik amacıyla 404 döner aslında 403 olması gereken yerde (kaynağın varlığını gizlemek için).
405 Method Not Allowed, bu endpoint bu HTTP metodunu desteklemiyor. GET-only bir endpoint’e POST atmak gibi.
409 Conflict, çakışma. Aynı username ile tekrar kayıt olmak veya version conflict durumlarında.
422 Unprocessable Entity, istek syntaktik olarak doğru ama semantik olarak yanlış. Modern REST API’lerde 400 yerine giderek daha fazla kullanılıyor.
429 Too Many Requests, rate limit aşıldı. Response header’larında Retry-After veya X-RateLimit-Reset bilgisi gelir.
# Rate limit kontrolü ve retry mekanizması
check_rate_limit() {
local URL=$1
local TOKEN=$2
RESPONSE=$(curl -s -D /tmp/headers.txt
-H "Authorization: Bearer $TOKEN"
-o /tmp/body.txt
-w "%{http_code}"
"$URL")
HTTP_CODE=$RESPONSE
if [ "$HTTP_CODE" -eq 429 ]; then
RETRY_AFTER=$(grep -i "retry-after:" /tmp/headers.txt | awk '{print $2}' | tr -d 'r')
REMAINING=$(grep -i "x-ratelimit-remaining:" /tmp/headers.txt | awk '{print $2}' | tr -d 'r')
echo "Rate limit asildi!"
echo "Kalan istek hakki: $REMAINING"
echo "$RETRY_AFTER saniye sonra tekrar dene"
if [ -n "$RETRY_AFTER" ]; then
sleep "$RETRY_AFTER"
# Recursive retry
check_rate_limit "$URL" "$TOKEN"
fi
fi
cat /tmp/body.txt
}
check_rate_limit "https://api.example.com/data" "mytoken123"
# 401 vs 403 debug scripti
debug_auth() {
local ENDPOINT=$1
local TOKEN=$2
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}"
-H "Authorization: Bearer $TOKEN"
"$ENDPOINT")
case $HTTP_CODE in
200)
echo "Basarili erisim"
;;
401)
echo "Kimlik dogrulama hatasi!"
echo "Kontrol listesi:"
echo " - Token suresi dolmus mu? (JWT exp claim kontrol et)"
echo " - Authorization header dogru formatta mi? (Bearer token)"
echo " - Token dogru API icin mi uretildi?"
# Token decode (JWT icin)
echo "Token payload:"
echo "$TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool
;;
403)
echo "Yetkilendirme hatasi!"
echo "Kimliginiz dogrulandi ama bu kaynaga erisim izniniz yok"
echo "Kontrol listesi:"
echo " - Rol/permission ayarlarinizi kontrol edin"
echo " - Dogru scope ile token alinmis mi?"
echo " - IP whitelist var mi?"
;;
404)
echo "Kaynak bulunamadi - ya da gizleniyor (bazi API'ler 404 ile 403'u gizler)"
;;
esac
}
debug_auth "https://api.example.com/admin/users" "$API_TOKEN"
5xx: Sunucu Hataları – Sorun Onlarda
5xx hatalarında sorun sunucu tarafında. Ama bu “bizim yapacak bir şeyimiz yok” anlamına gelmiyor. Retry stratejisi, circuit breaker pattern, fallback mekanizmaları… Hepsi burada devreye girer.
500 Internal Server Error, sunucu tarafında beklenmeyen bir hata. En genel hata kodu.
502 Bad Gateway, proxy veya gateway arkasındaki upstream sunucu geçersiz yanıt döndü. Nginx’in arkasındaki uygulama sunucusu çökmüşse tipik olarak 502 görürsünüz.
503 Service Unavailable, servis geçici olarak kullanılamıyor. Bakım modu veya aşırı yük. Retry-After header’ı gelebilir.
504 Gateway Timeout, upstream sunucu zamanında yanıt vermedi. 502’den farkı: 502’de upstream hatalı yanıt döndü, 504’te hiç yanıt gelmedi (timeout).
507 Insufficient Storage, disk doldu, istek işlenemiyor.
# 5xx hataları için exponential backoff ile retry scripti
api_call_with_retry() {
local URL=$1
local MAX_RETRIES=5
local RETRY_COUNT=0
local WAIT_TIME=1
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
HTTP_CODE=$(curl -s -o /tmp/api_response.json
-w "%{http_code}"
-H "Content-Type: application/json"
-H "Authorization: Bearer $API_TOKEN"
"$URL")
# 2xx basarili, cikarken response'u doker
if [[ "$HTTP_CODE" =~ ^2 ]]; then
cat /tmp/api_response.json
return 0
fi
# 4xx hatalarinda retry yapma, kullanici hatasidir
if [[ "$HTTP_CODE" =~ ^4 ]]; then
echo "Istemci hatasi ($HTTP_CODE), retry yapilmiyor"
cat /tmp/api_response.json
return 1
fi
# 5xx hatalarinda retry
if [[ "$HTTP_CODE" =~ ^5 ]]; then
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "Sunucu hatasi ($HTTP_CODE). Deneme $RETRY_COUNT/$MAX_RETRIES"
echo "$WAIT_TIME saniye bekleniyor..."
sleep $WAIT_TIME
# Exponential backoff: 1, 2, 4, 8, 16 saniye
WAIT_TIME=$((WAIT_TIME * 2))
fi
done
echo "Maximum retry sayisina ulasildi. Son HTTP kodu: $HTTP_CODE"
return 1
}
api_call_with_retry "https://api.example.com/critical-data"
# 502/504 debug - proxy zincirini analiz et
check_proxy_chain() {
local URL=$1
echo "=== Proxy/Gateway Debug ==="
# Response header'larini incele
curl -sI "$URL" | grep -E "^(Server|X-Powered-By|Via|X-Cache|CF-Ray|X-Request-ID):"
# Timing bilgisi
curl -s -o /dev/null -w "
DNS Lookup: %{time_namelookup}s
TCP Connect: %{time_connect}s
TLS Handshake: %{time_appconnect}s
First Byte: %{time_starttransfer}s
Total Time: %{time_total}s
HTTP Code: %{http_code}
" "$URL"
echo ""
echo "502 ise: Upstream uygulama sunucusu kontrol edilmeli"
echo "504 ise: Upstream timeout - islem cok uzun sürüyor"
}
check_proxy_chain "https://api.example.com/slow-endpoint"
Monitoring ve Alerting Stratejisi
Status kodlarını anlamak bir şey, bunları production’da izlemek başka bir şey. Prometheus ve benzeri araçlarla metrik toplamak standart hale geldi. Ama basit bir monitoring scripti bile çok işe yarar.
#!/bin/bash
# api_health_monitor.sh - API endpoint saglik kontrolu
ENDPOINTS=(
"https://api.example.com/health"
"https://api.example.com/v2/status"
"https://api.partner.com/ping"
)
ALERT_EMAIL="[email protected]"
LOG_FILE="/var/log/api_monitor.log"
check_endpoint() {
local URL=$1
local TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# Hem HTTP kodu hem de response time al
RESULT=$(curl -s -o /dev/null
-w "%{http_code}|%{time_total}|%{size_download}"
--connect-timeout 10
--max-time 30
"$URL")
HTTP_CODE=$(echo $RESULT | cut -d'|' -f1)
RESPONSE_TIME=$(echo $RESULT | cut -d'|' -f2)
BODY_SIZE=$(echo $RESULT | cut -d'|' -f3)
LOG_ENTRY="$TIMESTAMP | $URL | HTTP:$HTTP_CODE | Time:${RESPONSE_TIME}s | Size:${BODY_SIZE}b"
echo "$LOG_ENTRY" >> "$LOG_FILE"
# Alert kosullari
if [[ "$HTTP_CODE" =~ ^5 ]]; then
echo "KRITIK: $URL sunucu hatasi donduruyor: $HTTP_CODE" |
mail -s "[API ALERT] 5xx Error: $URL" "$ALERT_EMAIL"
elif [ "$HTTP_CODE" -eq 429 ]; then
echo "UYARI: $URL rate limit asimiyor" |
mail -s "[API ALERT] Rate Limit: $URL" "$ALERT_EMAIL"
elif (( $(echo "$RESPONSE_TIME > 5.0" | bc -l) )); then
echo "UYARI: $URL yavas yanitliyor: ${RESPONSE_TIME}s" |
mail -s "[API ALERT] Slow Response: $URL" "$ALERT_EMAIL"
fi
echo "$HTTP_CODE"
}
for endpoint in "${ENDPOINTS[@]}"; do
CODE=$(check_endpoint "$endpoint")
echo "[$CODE] $endpoint"
done
Özel HTTP Kodları ve Yaygın Yanlış Kullanımlar
API tasarımında bazı kodlar çok yanlış kullanılıyor. Bunları bilmek hem kendi API’nizi doğru tasarlamanıza hem de entegre ettiğiniz API’nin davranışını anlamanıza yardımcı olur.
200 ile hata dönmek: En sık gördüğüm antipattern. Yanıt body’sinde {"status": "error", "code": 500} yazarken HTTP kodu 200 dönmek. Bu şekilde monitoring sistemleri hataları yakalayamaz, retry mekanizmaları çalışmaz.
404 vs 410: 404 “şu an bulamıyorum”, 410 Gone “bu kaynak kalıcı olarak silindi”. SEO açısından da önemli ama API’lerde de anlamlı: 410 görünce retry yapmak anlamsız.
Validation hatalarında 400 vs 422: Modern REST pratiklerine göre 400 syntaktik hata (geçersiz JSON), 422 semantik hata (JSON geçerli ama içerik kurallara aykırı) için kullanılmalı.
# API response analiz - yanlış kullanim tespiti
analyze_api_response() {
local URL=$1
local PAYLOAD=$2
RESPONSE=$(curl -s -D /tmp/resp_headers.txt
-X POST "$URL"
-H "Content-Type: application/json"
-d "$PAYLOAD"
-w "n###HTTP_CODE###%{http_code}"
-o /tmp/resp_body.txt)
HTTP_CODE=$(tail -1 /tmp/resp_body.txt | grep -o '[0-9]*$')
BODY=$(head -n -1 /tmp/resp_body.txt)
echo "HTTP Status: $HTTP_CODE"
echo "Response Body:"
echo "$BODY" | python3 -m json.tool 2>/dev/null || echo "$BODY"
# Antipattern tespiti: 200 ile hata donuyor mu?
if [ "$HTTP_CODE" -eq 200 ]; then
ERROR_IN_BODY=$(echo "$BODY" | python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
if 'error' in data or (isinstance(data.get('status'), str) and 'error' in data.get('status', '').lower()):
print('ANTIPATTERN: 200 ile hata mesaji donuyor!')
else:
print('OK: Basarili yanit')
except:
print('Body JSON degil')
" 2>/dev/null)
echo "$ERROR_IN_BODY"
fi
}
analyze_api_response
"https://api.example.com/users"
'{"username": "", "email": "gecersiz-email"}'
Content Negotiation ve Status Kodları
Accept header’larıyla ilgili durum kodları da sık karşılaşılan bir alan. 406 Not Acceptable, istediğiniz formatta yanıt üretemiyorum demek. 415 Unsupported Media Type ise gönderdiğiniz Content-Type desteklenmiyor.
# Content negotiation test
test_content_types() {
local URL=$1
echo "=== JSON istegi ==="
curl -s -o /dev/null -w "HTTP: %{http_code}n"
-H "Accept: application/json"
"$URL"
echo "=== XML istegi (desteklenmeyebilir) ==="
curl -s -o /dev/null -w "HTTP: %{http_code}n"
-H "Accept: application/xml"
"$URL"
echo "=== Yanlis Content-Type ile POST ==="
curl -s -o /dev/null -w "HTTP: %{http_code}n"
-X POST "$URL"
-H "Content-Type: text/plain"
-d '{"key": "value"}'
# 415 bekliyoruz son istekte
}
test_content_types "https://api.example.com/data"
Sonuç
HTTP status kodları, API iletişiminin belkemiği. Bunları doğru anlamak ve uygulamak production sistemlerinizin sağlığı için kritik.
Özetlemek gerekirse:
- 2xx kodlarını birbirinden ayırt edin, 200 her şey değil. 201 ve 204 ayrı anlamlara geliyor.
- 3xx yönlendirmelerini API çağrılarında otomatik follow etmeden önce düşünün, kalıcı yönlendirme alarm işareti olabilir.
- 401 ile 403’ü karıştırmayın, debug stratejileri farklı.
- 429’u görünce retry-after header’ını okuyun, brute force retry yapmayın.
- 5xx hatalarında exponential backoff ile retry uygulayın, 4xx hatalarında retry yapmayın.
- Kendi API’nizi tasarlarken 200 ile hata döndürme antipattern’ından kaçının.
- Monitoring sistemlerinizi status kod kategorilerine göre kurun, sadece 200/200 değil diye bakmayın.
Sysadmin olarak en çok işinize yarayacak şey, bir API entegrasyonunda sorun çıktığında sistematik bir yaklaşım izlemektir. Önce HTTP koduna bakın, sonra response header’larını inceleyin, sonra body’yi okuyun. Bu sırayla gidince çoğu sorun çok daha hızlı çözülüyor. Loglarınıza her zaman HTTP status kodunu yazdırın, gece 3’te pagerduty çalındığında bu küçük detay size çok zaman kazandırır.
