Webhook ve API Farkı: Hangi Durumda Ne Kullanılır?

Sistem entegrasyonları üzerine çalışırken en sık karşılaştığım sorulardan biri şu: “Bunu webhook mi yapsak, API mi?” Kulağa basit geliyor ama yanlış karar vermek, gereksiz polling yükü, gecikme sorunları veya aşırı karmaşık mimari anlamına gelebilir. Bu yazıda her iki yaklaşımı da gerçek dünya senaryolarıyla masaya yatırıp, hangi durumda hangisini seçmeniz gerektiğini net bir şekilde ortaya koyacağım.

Temel Fark: Kim Kimi Çağırıyor?

En özet haliyle söyleyecek olursam:

  • API (Polling): Siz sunucuya gidip “bir şey var mı?” diye sorarsınız
  • Webhook: Sunucu bir şey olduğunda size gelip “şu oldu” der

Bu ayrım kulağa basit ama pratikte mimarisel olarak çok büyük farklar doğuruyor. API tabanlı entegrasyonda siz aktifsiniz, webhook tabanlı entegrasyonda ise karşı taraf aktif.

Bir örnek üzerinden düşünelim: GitHub’dan yeni bir commit geldiğinde CI/CD pipeline’ınızı tetiklemek istiyorsunuz. İki yaklaşım:

API Polling yaklaşımı: Her 30 saniyede bir GitHub API’sine istek atarsınız, yeni commit var mı diye bakarsınız. Commit yoksa boşuna istek atmış olursunuz. Commit varsa, en fazla 30 saniyelik gecikmeyle öğrenirsiniz.

Webhook yaklaşımı: GitHub’a “yeni commit olduğunda şu URL’e POST at” dersiniz. Commit olduğu anda, milisaniyeler içinde haberdar olursunuz. Gereksiz hiçbir istek gitmez.

REST API Nasıl Çalışır?

REST API, istemcinin sunucuya istek gönderdiği klasik istek-cevap modelidir. HTTP metodları (GET, POST, PUT, DELETE) üzerinden kaynakları yönetirsiniz. Sunucu pasiftir, siz sormazsan konuşmaz.

Basit bir API çağrısı:

# Kullanıcı listesini çek
curl -X GET 
  -H "Authorization: Bearer TOKEN" 
  -H "Content-Type: application/json" 
  https://api.example.com/users

# Yeni kullanıcı oluştur
curl -X POST 
  -H "Authorization: Bearer TOKEN" 
  -H "Content-Type: application/json" 
  -d '{"name": "Ahmet Yilmaz", "email": "[email protected]"}' 
  https://api.example.com/users

Polling mantığıyla çalışan bir bash script yazalım. Diyelim ki bir ödeme işleminin tamamlanmasını bekliyorsunuz:

#!/bin/bash

PAYMENT_ID="PAY-12345"
API_URL="https://api.payment-gateway.com/payments/${PAYMENT_ID}"
TOKEN="your_api_token"
MAX_ATTEMPTS=20
SLEEP_INTERVAL=15

check_payment_status() {
    curl -s -X GET 
        -H "Authorization: Bearer ${TOKEN}" 
        -H "Content-Type: application/json" 
        "${API_URL}" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])"
}

attempt=0
while [ $attempt -lt $MAX_ATTEMPTS ]; do
    attempt=$((attempt + 1))
    status=$(check_payment_status)
    
    echo "[$(date '+%H:%M:%S')] Deneme ${attempt}/${MAX_ATTEMPTS} - Durum: ${status}"
    
    if [ "$status" = "completed" ]; then
        echo "Odeme tamamlandi!"
        exit 0
    elif [ "$status" = "failed" ]; then
        echo "Odeme basarisiz oldu!"
        exit 1
    fi
    
    sleep $SLEEP_INTERVAL
done

echo "Maksimum deneme sayisina ulasildi, odeme durumu belirsiz"
exit 2

Bu script her 15 saniyede bir API’ye istek atıyor. Ödeme 3 dakikada tamamlanırsa, 12 boşuna istek atmış olursunuz. Gerçek bir üretim sisteminde yüzlerce ödeme takip ediliyorsa bu rakamlar ciddi bir yük oluşturur.

Webhook Nasıl Çalışır?

Webhook’ta akış tersine döner. Karşı sistem, bir olay gerçekleştiğinde sizin belirlediğiniz URL’e HTTP POST isteği gönderir. Siz bu isteği dinleyen bir endpoint açarsınız, gelen veriyi işlersiniz.

Basit bir Python webhook receiver:

# Flask ile basit webhook receiver
cat > webhook_receiver.py << 'EOF'
from flask import Flask, request, jsonify
import hashlib
import hmac
import json
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

WEBHOOK_SECRET = "gizli_anahtar_buraya"

def verify_signature(payload, signature, secret):
    """Webhook imzasini dogrula"""
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

@app.route('/webhook/payment', methods=['POST'])
def payment_webhook():
    payload = request.data
    signature = request.headers.get('X-Signature-256', '')
    
    if not verify_signature(payload, signature, WEBHOOK_SECRET):
        logging.warning("Gecersiz webhook imzasi!")
        return jsonify({"error": "Unauthorized"}), 401
    
    data = request.get_json()
    event_type = data.get('event')
    
    logging.info(f"Olay alindi: {event_type}")
    
    if event_type == 'payment.completed':
        payment_id = data['payload']['payment_id']
        amount = data['payload']['amount']
        logging.info(f"Odeme tamamlandi: {payment_id}, Tutar: {amount}")
        # Siparis onay emaili gonder, stok guncelle vs.
        process_successful_payment(payment_id, amount)
        
    elif event_type == 'payment.failed':
        payment_id = data['payload']['payment_id']
        logging.warning(f"Odeme basarisiz: {payment_id}")
        process_failed_payment(payment_id)
    
    return jsonify({"status": "received"}), 200

def process_successful_payment(payment_id, amount):
    # Gercek is mantigi buraya
    logging.info(f"Siparis isleniyor: {payment_id}")

def process_failed_payment(payment_id):
    logging.info(f"Basarisiz odeme bildirimi gonderiliyor: {payment_id}")

if __name__ == '__main__':
    app.run(port=8080)
EOF

python3 webhook_receiver.py

Güvenlik: Webhook’larda İmza Doğrulaması

Webhook’ların en kritik noktası güvenliktir. Endpoint’iniz dışarıya açık olduğu için, sahte isteklere karşı korunmanız gerekir. Neredeyse tüm profesyonel servisler (GitHub, Stripe, Shopify) HMAC imzası kullanır.

# Webhook imzasini bash ile dogrulama
#!/bin/bash

WEBHOOK_SECRET="gizli_anahtar_buraya"
PAYLOAD='{"event":"payment.completed","payload":{"payment_id":"PAY-12345","amount":150.00}}'
RECEIVED_SIGNATURE="sha256=abc123..."

# HMAC-SHA256 hesapla
CALCULATED_SIGNATURE="sha256=$(echo -n "${PAYLOAD}" | 
    openssl dgst -sha256 -hmac "${WEBHOOK_SECRET}" | 
    awk '{print $2}')"

echo "Alinan imza: ${RECEIVED_SIGNATURE}"
echo "Hesaplanan imza: ${CALCULATED_SIGNATURE}"

if [ "${RECEIVED_SIGNATURE}" = "${CALCULATED_SIGNATURE}" ]; then
    echo "Imza gecerli, islem devam ediyor"
else
    echo "UYARI: Gecersiz imza, istek reddedildi"
    exit 1
fi

Bir diğer önemli güvenlik önlemi timestamp kontrolüdür. Replay saldırılarına karşı, webhook isteğinin çok eski olmadığından emin olmalısınız:

#!/bin/bash

# Timestamp kontrolu ile replay attack onleme
TIMESTAMP_HEADER="1699876543"  # Webhook'tan gelen timestamp
CURRENT_TIME=$(date +%s)
MAX_AGE=300  # 5 dakika

TIME_DIFF=$((CURRENT_TIME - TIMESTAMP_HEADER))

if [ $TIME_DIFF -gt $MAX_AGE ]; then
    echo "HATA: Webhook cok eski (${TIME_DIFF} saniye). Replay attack olabilir!"
    exit 1
fi

echo "Timestamp gecerli, fark: ${TIME_DIFF} saniye"

Hangisi Ne Zaman Kullanılır?

API (Polling) Tercih Edin

Kullanıcı aksiyonuna bağlı işlemlerde: Kullanıcı bir butona tıkladığında veri çekmek, form submit etmek, dosya yüklemek gibi senaryolarda API’nin yerini webhook tutamaz. Kullanıcı “gönder” dedi, siz hemen API’yi çağırırsınız.

Veri sorgulamak istediğinizde: “Bu kullanıcının bilgilerini getir”, “şu tarih aralığındaki siparişleri listele” gibi on-demand sorgular için webhook’un mantığı yoktur. Webhook olayları bildirir, veri sunmaz.

İki yönlü iletişim gerektiğinde: CRUD işlemleri için API şarttır. Webhook sadece bildirim mekanizmasıdır, veri manipülasyonu yapamazsınız.

Karşı taraf webhook desteklemiyorsa: Eski sistemler veya bazı üçüncü taraf servisler webhook sağlamayabilir. Bu durumda polling kaçınılmazdır ama exponential backoff kullanarak sistemi yormamak gerekir:

#!/bin/bash

# Exponential backoff ile akilli polling
JOB_ID="JOB-9876"
API_URL="https://api.example.com/jobs/${JOB_ID}"
TOKEN="api_token_here"

INITIAL_WAIT=2
MAX_WAIT=120
wait_time=$INITIAL_WAIT
attempt=0

while true; do
    attempt=$((attempt + 1))
    
    response=$(curl -s -X GET 
        -H "Authorization: Bearer ${TOKEN}" 
        "${API_URL}")
    
    status=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status', 'unknown'))")
    
    echo "[Deneme ${attempt}] Durum: ${status}, Sonraki bekleme: ${wait_time}s"
    
    case "$status" in
        "completed")
            echo "Is tamamlandi!"
            exit 0
            ;;
        "failed")
            echo "Is basarisiz!"
            exit 1
            ;;
        "running"|"pending")
            sleep $wait_time
            # Bekleme suresini iki katina cikar ama max degeri gecme
            wait_time=$((wait_time * 2))
            if [ $wait_time -gt $MAX_WAIT ]; then
                wait_time=$MAX_WAIT
            fi
            ;;
        *)
            echo "Bilinmeyen durum: $status"
            exit 2
            ;;
    esac
done

Webhook Tercih Edin

Gerçek zamanlı bildirimler için: Ödeme onayı, sipariş durumu değişikliği, GitHub push bildirimleri. Bunların hepsinde “oldu mu olmadı mı” diye sormak yerine, “oldu” haberi almanız yeterli ve çok daha verimli.

Olay güdümlü mimaride: Microservice mimarilerinde servisler arası iletişimde webhook veya mesaj kuyrukları (RabbitMQ, Kafka) tercih edilir. Bir servis bir şey yaptığında diğerini tetiklemesi için polling mantıksızdır.

Yüksek hacimli durum takibinde: Binlerce siparişi takip etmek için binlerce API isteği atmak yerine, değişiklik olduğunda tek bir webhook almak çok daha verimlidir.

CI/CD pipeline tetiklemede: GitHub Actions, GitLab CI, Jenkins gibi sistemler webhook üzerinde çalışır. Commit geldiğinde pipeline tetiklenir, sürekli “yeni commit var mı” diye sormak anlamsız olurdu.

Gerçek Dünya Senaryosu: E-ticaret Entegrasyonu

Bir e-ticaret sistemini ele alalım. Hem API hem webhook kullanmanız gerekecek:

API kullandığınız durumlar:

  • Müşteri ürün sayfasını açtığında stok bilgisi çekmek
  • Admin panelinden sipariş güncellemek
  • Ürün fiyatlarını güncellemek
  • Müşteri arama yapmak

Webhook kullandığınız durumlar:

  • Ödeme tamamlandığında kargo sistemini tetiklemek
  • Stok sıfırlandığında tedarikçiye bildirim göndermek
  • Yeni yorum geldiğinde moderasyon sistemini uyandırmak
  • Abonelik yenilendiğinde CRM’i güncellemek

Nginx ile basit bir webhook proxy kurulumu:

# Nginx webhook endpoint konfigurasyonu
cat > /etc/nginx/sites-available/webhook-proxy << 'EOF'
server {
    listen 443 ssl;
    server_name webhooks.example.com;

    ssl_certificate /etc/letsencrypt/live/webhooks.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webhooks.example.com/privkey.pem;

    # Webhook boyut siniri (5MB)
    client_max_body_size 5M;

    # IP whitelist - sadece Stripe IP'lerinden kabul et
    location /webhook/payment {
        # Stripe IP araligları
        allow 3.18.12.63;
        allow 3.130.192.231;
        allow 13.235.14.237;
        deny all;

        proxy_pass http://localhost:8080/webhook/payment;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        
        # Timeout ayarlari
        proxy_read_timeout 30s;
        proxy_connect_timeout 10s;
    }

    # GitHub webhook'ları herkesten gelebilir ama imza kontrolu var
    location /webhook/github {
        proxy_pass http://localhost:8080/webhook/github;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Hub-Signature-256 $http_x_hub_signature_256;
    }
}
EOF

nginx -t && systemctl reload nginx

Webhook Güvenilirlik Sorunları ve Çözümleri

Webhook’ların bir dezavantajı var: karşı taraf sisteminize ulaşamazsa, bildirimi kaybedebilirsiniz. Profesyonel servisler genellikle retry mekanizması sağlar ama yine de idempotency (tekrarlarda aynı sonucu üretme) sağlamanız gerekir:

#!/bin/bash

# Idempotency key kontrolu ile guvenli webhook isleme
WEBHOOK_PAYLOAD='{"event":"payment.completed","idempotency_key":"abc-123-xyz","payload":{"payment_id":"PAY-555"}}'
IDEMPOTENCY_KEY=$(echo "$WEBHOOK_PAYLOAD" | python3 -c "import sys,json; print(json.load(sys.stdin)['idempotency_key'])")

REDIS_KEY="processed_webhook:${IDEMPOTENCY_KEY}"

# Redis'te bu key daha once islendi mi?
already_processed=$(redis-cli GET "$REDIS_KEY")

if [ -n "$already_processed" ]; then
    echo "Bu webhook zaten islenmis: ${IDEMPOTENCY_KEY}, atlaniyor"
    exit 0
fi

echo "Yeni webhook isleniyor: ${IDEMPOTENCY_KEY}"

# Is mantigi burada...
# process_webhook "$WEBHOOK_PAYLOAD"

# Islendi olarak isaretke, 24 saat sakla
redis-cli SET "$REDIS_KEY" "1" EX 86400
echo "Webhook basariyla islendi ve kaydedildi"

Ayrıca webhook işleme süresi önemlidir. Eğer işleminiz uzun sürüyorsa, hemen 200 OK döndürüp arka planda işlem yapmalısınız. Aksi halde karşı taraf timeout alır ve retry başlatır:

# Webhook alindiginda hemen onayla, arkaplanda isle
cat > async_webhook.sh << 'EOF'
#!/bin/bash

PAYLOAD="$1"
JOB_QUEUE_DIR="/var/lib/webhook-queue"

mkdir -p "$JOB_QUEUE_DIR"

# Islemi kuyruğa ekle
JOB_ID=$(uuidgen)
echo "$PAYLOAD" > "${JOB_QUEUE_DIR}/${JOB_ID}.job"

echo "Kuyruga eklendi: ${JOB_ID}"
# HTTP 200 hemen donulecek, worker arkaplanda isleyecek
EOF

# Worker process (cron veya systemd service olarak calistir)
cat > webhook_worker.sh << 'EOF'
#!/bin/bash

JOB_QUEUE_DIR="/var/lib/webhook-queue"
PROCESSED_DIR="/var/lib/webhook-queue/processed"

mkdir -p "$PROCESSED_DIR"

for job_file in "${JOB_QUEUE_DIR}"/*.job; do
    [ -f "$job_file" ] || continue
    
    job_id=$(basename "$job_file" .job)
    payload=$(cat "$job_file")
    
    echo "Islem basliyor: ${job_id}"
    
    # Uzun suren is burada yapilir
    sleep 5  # Simule edilen uzun islem
    
    mv "$job_file" "${PROCESSED_DIR}/${job_id}.done"
    echo "Tamamlandi: ${job_id}"
done
EOF

chmod +x async_webhook.sh webhook_worker.sh

Hibrit Yaklaşım: Webhook + API Birlikte

Gerçek hayatta en sağlıklı yaklaşım genellikle ikisini birlikte kullanmaktır. Klasik pattern şu şekilde işler:

  1. Webhook ile “bir şey oldu” haberini alırsınız
  2. Webhook payload’ında sadece olay ID’si ve türü bulunur
  3. Detayları API üzerinden çekersiniz

Bu pattern özellikle güvenlik açısından önemlidir. Webhook payload’ına tamamen güvenmek yerine, kritik veriyi API’den doğrulayarak çekersiniz. Stripe tam olarak bu modeli kullanır.

Örnek: GitHub webhook’u geldi, commit hash’i aldınız, sonra API ile commit detaylarını çektiniz:

#!/bin/bash

# Webhook geldi: commit SHA alinmis durumda
COMMIT_SHA="abc123def456"
REPO="orgname/reponame"
GITHUB_TOKEN="ghp_your_token"

# API ile commit detaylarini dogrulayarak cek
commit_details=$(curl -s 
    -H "Authorization: token ${GITHUB_TOKEN}" 
    -H "Accept: application/vnd.github.v3+json" 
    "https://api.github.com/repos/${REPO}/commits/${COMMIT_SHA}")

commit_message=$(echo "$commit_details" | 
    python3 -c "import sys,json; print(json.load(sys.stdin)['commit']['message'])")

commit_author=$(echo "$commit_details" | 
    python3 -c "import sys,json; print(json.load(sys.stdin)['commit']['author']['name'])")

echo "Commit: ${COMMIT_SHA}"
echo "Yazar: ${commit_author}"
echo "Mesaj: ${commit_message}"

# Artik pipeline'i tetikleyebilirsiniz
echo "CI/CD pipeline tetikleniyor..."

İzleme ve Hata Ayıklama

Webhook’ları production’da izlemek için log yapısı kurmak kritik önemdedir:

#!/bin/bash

# Webhook log analizi
WEBHOOK_LOG="/var/log/webhook/access.log"

echo "=== Son 24 Saatteki Webhook Ozeti ==="

# Basarili webhook sayisi
success_count=$(grep "200" "$WEBHOOK_LOG" | grep "$(date +%Y-%m-%d)" | wc -l)
echo "Basarili: ${success_count}"

# Basarisiz webhook sayisi
fail_count=$(grep -E "4[0-9]{2}|5[0-9]{2}" "$WEBHOOK_LOG" | grep "$(date +%Y-%m-%d)" | wc -l)
echo "Basarisiz: ${fail_count}"

# En cok gelen olay tipleri
echo ""
echo "=== Olay Dagilimi ==="
grep "$(date +%Y-%m-%d)" "$WEBHOOK_LOG" | 
    grep -oP '"event":"K[^"]+' | 
    sort | uniq -c | sort -rn | head -10

# Ortalama yanit suresi
echo ""
echo "=== Performans ==="
avg_time=$(grep "$(date +%Y-%m-%d)" "$WEBHOOK_LOG" | 
    grep -oP 'rt=K[0-9.]+' | 
    awk '{ sum += $1; count++ } END { if (count > 0) print sum/count; else print 0 }')
echo "Ortalama islem suresi: ${avg_time}ms"

Sonuç

İkisi arasında seçim yaparken kendinize şu soruları sorun: “Ben mi veri isteyeceğim, yoksa bir şey olduğunda mı haberdar olmak istiyorum?” Cevabınız “ben istemek istiyorum” ise API, “haberdar olmak istiyorum” ise webhook yönüne gidin.

Pratikte akılda tutulması gereken birkaç kural:

  • Gerçek zamanlılık önemliyse webhook, gecikme tolere edilebiliyorsa polling
  • Düşük istek hacmi hedefliyorsanız webhook çok daha verimli
  • Kullanıcı tetiklemeli işlemler için her zaman API
  • Webhook kullanıyorsanız mutlaka imza doğrulaması, idempotency kontrolü ve async işleme uygulayın
  • Kritik sistemlerde her iki yaklaşımı birlikte kullanın: webhook ile hızlı bildirim, API ile doğrulama

Son olarak şunu söyleyeyim: webhook daha verimli ve modern olsa da, karşı tarafın erişemediği durumlarda yedek polling mekanizması da bulundurmak iyi bir pratiktir. En sağlam sistemler, her iki dünyanın avantajlarını akıllıca kullananlar.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir