API Platformu

Task'lar ve Uzun Süren İstekler

Bir dakikadan uzun süren istekler task id ile 202 döner. Sonucu sonra alın, güvenle tekrar deneyin, iki kez ödeme yapmayın.

Bazı istekler, bir HTTP bağlantısının açık kalması gereken süreden uzun sürer. Aşağıdaki endpoint'lerde her istek bir task olarak çalışır. İş 60 saniye içinde biterse sonucu eskisi gibi alırsınız. Bitmezse 202 Accepted ile bir task_id döner; sonucu hazır olduğunda GET /v1/tasks/{task_id} ile alırsınız. İş kaybolmaz, iki kez ücret ödemezsiniz.

Hangi endpoint'ler task olarak çalışır

Endpointservice
POST /v1/serpserp
POST /v1/keyword-datakeyword_data
POST /v1/domain-researchdomain_research
POST /v1/youtubeyoutube
POST /v1/trendstrends
POST /v1/on-pageon_page
POST /v1/business-databusiness_data

Backlinks, Google Images ve Technical SEO dahil diğer tüm endpoint'ler şimdilik eskisi gibi senkron yanıt verir. Diğer endpoint'ler de zamanla task'lara geçecek.

Nasıl çalışır?

  1. İsteği her zamanki gibi gönderin

    Aynı URL, aynı header'lar, aynı body. Task, iş başlamadan önce kaydedilir; ardından bağlantı en fazla wait saniye (varsayılan 60) açık tutulur.

  2. Zamanında biterse sonuç gelir

    Status code ve body her zamanki gibidir. Tek fark, task id'yi taşıyan yeni X-Task-Id header'ıdır.

  3. Hâlâ çalışıyorsa 202 gelir

    Response'taki Location header'ı task'ın adresini, Retry-After header'ı ise tekrar sormadan önce kaç saniye beklemeniz gerektiğini söyler.

  4. Sonucu alın

    Retry-After saniye sonra GET /v1/tasks/{task_id} çağırın; status 202 olmaktan çıkana kadar tekrarlayın. Son response, POST'un döndüreceği response'un aynısıdır.

Task'lardan önce yazılmış entegrasyonlar, bir dakika içinde biten her istekte çalışmaya devam eder. Yine de her istemci 202'yi ele almalıdır: herhangi bir istek wait süresini aşabilir, ayrıca bir deploy sırasında bekleyen istek 202'yi erken alabilir. Örnekler bölümündeki yardımcı fonksiyonlar bunu sizin yerinize halleder.

Task id'yi nerede bulurum?

Her task'ın 4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39 gibi bir UUID'si vardır. Bu id her yanıtla birlikte gelir; böylece bir sonuca her zaman geri dönebilirsiniz:

Ne aldınızTask id nerede
202 AcceptedBody'deki task_id, X-Task-Id header'ı, Location ve poll_url değerlerinin sonu
POST'tan doğrudan gelen bir sonuç (200) ya da hataX-Task-Id header'ı. Domain Research, YouTube, Trends, On-Page ve Business Data bunu body'de request_id olarak da verir
GET /v1/tasks/{task_id} yanıtıX-Task-Id header'ı
Hiçbir şey: süreciniz çöktü ya da yanıtı kaybettinizGET /v1/tasks son task'larınızı task_id'leriyle listeler (Task'ları listeleme)

task_id, request_id, X-Task-Id ve X-Request-Id her zaman aynı değerdir. X-Request-Id, task'lardan önce yazılmış istemciler için duruyor. Her isteğin task id'sini loglayın: bir sonucu tekrar almanın ve destek ekibimizin isteği uçtan uca izlemesinin yolu budur.

Ücretsiz önbellek yanıtlarının task id'si yoktur

Ücretsiz önbellekten dönen bir SERP yanıtı task oluşturmadan anında döner, bu yüzden X-Task-Id taşımaz. İş yapan ya da ücretlendirilen her isteğin bir task id'si vardır.

202 Response

Task wait saniye sonunda hâlâ çalışıyorsa POST 202 Accepted döner:

HeaderDeğer
Location/v1/tasks/{task_id}, sorgulanacak yol
Retry-AfterTekrar sormadan önce beklenecek saniye
X-Task-IdTask id
X-Request-IdTask id (aynı değer, eski istemciler için)
X-Task-Statuspending
HTTP/1.1 202 Accepted
Location: /v1/tasks/4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39
Retry-After: 5
X-Task-Id: 4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39
X-Request-Id: 4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39
X-Task-Status: pending
Content-Type: application/json
{
  "success": true,
  "status": "pending",
  "task_id": "4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39",
  "request_id": "4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39",
  "service": "serp",
  "poll_url": "https://data.semust.com/v1/tasks/4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39",
  "retry_after": 5,
  "created_at": "2026-11-06T13:15:26Z",
  "elapsed_seconds": 60,
  "deadline_at": "2026-11-06T13:25:26Z",
  "deadline_in_seconds": 540
}
AlanTipAçıklama
successbooleanHer zaman true: istek kabul edildi
statusstringpending
task_idstringTask'ın UUID'si
request_idstringtask_id ile aynı
servicestringserp, keyword_data, domain_research, youtube veya trends
poll_urlstringGET /v1/tasks/{task_id} adresinin tamamı
retry_afterintegerRetry-After header'ıyla aynı
created_atstringTask'ın oluşturulma zamanı, UTC, RFC 3339
elapsed_secondsintegercreated_at'ten bu yana geçen saniye
deadline_atstringBitmemiş bir task'tan vazgeçilecek zaman: o an gelince task 504 TIMEOUT ile başarısız olur ve iade edilir. UTC, RFC 3339
deadline_in_secondsintegerdeadline_at'e kalan saniye. Her sorgulamada azalır; böylece ne kadar daha sorgulayabileceğinizi bilirsiniz

success: true sonucun geldiği anlamına gelmez

202 body'sindeki "success": true, isteğin bittiğini değil kabul edildiğini söyler. Kararı HTTP status code'una göre verin. Yalnızca success alanına bakıp ardından items ya da organic okuyan bir istemci bu alanları boş bulur.

Task sorgulama

GET /v1/tasks/{task_id} çağrısını POST'ta kullandığınız SEMUST-API-USER ve SEMUST-API-PASSWORD header'larıyla yapın.

Task durumuStatus codeBodyHeader'lar
Çalışıyor202Yukarıdaki pending body; elapsed_seconds artmaya devam ederRetry-After, Location, X-Task-Id, X-Task-Status: pending
TamamlandıPOST'un döndüreceği status, genellikle 200Endpoint'in normal response'u, bayt bayt aynısıX-Task-Status: completed, X-Task-Expires-At, X-Task-Expires-In, X-Task-Id
BaşarısızEndpoint'in normal hata status'u, ör. 502 veya 404Endpoint'in normal hata body'si, ör. REQUEST_FAILED, SERP_FAILED, VIDEO_NOT_FOUNDX-Task-Status: failed, X-Task-Expires-At, X-Task-Expires-In, X-Task-Id
Sonuç silinmiş410TASK_EXPIRED
Bilinmeyen id ya da başka bir API kullanıcısının task'ı404TASK_NOT_FOUND
UUID değil400INVALID_TASK_ID

Biten bir task, POST'un döndüreceği status'u ve body'yi birebir aynı baytlarla döndürür; yani elinizdeki response parser'ı hiçbir değişiklik gerekmeden çalışır. İkisini ayıran tek şey X-Task-* header'larıdır:

  • X-Task-Status: completed task'ın başarılı olduğunu ve ücretlendirildiğini söyler. failed ise ücret alınmadığı anlamına gelir.
  • X-Task-Expires-At sonucun silineceği zamanı (RFC 3339), X-Task-Expires-In ise o ana kalan saniyeyi verir.

Bu header'lar, POST'un doğrudan döndürdüğü sonuç dahil her bitmiş yanıtta gelir; böylece ücret alınıp alınmadığını ve sonucu daha ne kadar alabileceğinizi her zaman bilirsiniz:

HTTP/1.1 200 OK
X-Task-Id: 4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39
X-Task-Status: completed
X-Task-Expires-At: 2026-11-09T13:16:41Z
X-Task-Expires-In: 259200

Diğer kurallar:

  • İstediğiniz kadar alın. Biten bir sonuç, süresi dolana kadar istediğiniz kadar alınabilir. Sorgulamak ücretsizdir.
  • Yalnızca oluşturan okuyabilir. Bir task'ı yalnızca onu oluşturan API kullanıcısı okuyabilir. Aynı hesaptaki başka bir API kullanıcısı dahil herkes 404 TASK_NOT_FOUND alır.
  • Senkron yanıtlar da birer task'tır. Gerçek iş yapan her istek, wait içinde yanıtlananlar dahil, task olarak saklanır. X-Task-Id header'ı olan her response, saklama süresi boyunca o id ile yeniden alınabilir.
  • Sorgulama beklemez. GET /v1/tasks/{task_id} hemen yanıt verir. Çağrılar arasında Retry-After'a uyun: task'ın ilk 30 saniyesinde 3 saniye, 2 dakikaya kadar 5 saniye, sonrasında 10 saniye.

wait parametresi

?wait=, POST'un 202 dönmeden önce bağlantıyı kaç saniye açık tutacağını belirler.

waitDavranış
Gönderilmezse60 saniye
1–80Sonucu en fazla bu kadar saniye bekler
0Hiç beklemeden hemen 202 döner
Diğer değerler"field": "wait" ile 400 INVALID_PARAMETER

Üst sınır 80'dir, çünkü API'nin önündeki edge katmanı bağlantıları 100 saniyede keser. HTTP istemcinizin okuma timeout'unu wait'ten yüksek tutun, örneğin wait + 30 saniye.

wait=0 "gönder, sonra topla" modelidir: her istek için bağlantı açık tutmadan toplu istek gönderin, sonuçları sorgulayarak ya da listeleyerek toplayın. Önbellekten dönen yanıt gibi anında biten bir istek, yine sonucuyla birlikte hemen yanıtlanır.

POST https://data.semust.com/v1/domain-research?wait=0

Task'ları listeleme

GET /v1/tasks, sonucu hâlâ alınabilen son task'larınızı en yeniden eskiye listeler. Aynı header'ları kullanır ve ücretsizdir.

Query parametresiDeğerlerVarsayılan
statuspending, completed, failedhepsi
serviceserp, keyword_data, domain_research, youtube, trendshepsi
fetchedtrue veya falseikisi de
page1 veya daha büyük1
limit1–10050

Geçersiz bir status, fetched, page ya da limit değeri 400 INVALID_PARAMETER döndürür; field hangi parametre olduğunu söyler.

curl "https://data.semust.com/v1/tasks?status=completed&service=domain_research&limit=20" \
  -H "SEMUST-API-USER: kullanici_adiniz" \
  -H "SEMUST-API-PASSWORD: sifreniz"
{
  "success": true,
  "total": 2,
  "page": 1,
  "limit": 50,
  "items": [
    {
      "task_id": "9d3a6c1e-27b4-4f0a-8e5d-3c1b7a9f2e60",
      "service": "serp",
      "status": "pending",
      "http_status": null,
      "cost": null,
      "fetched": false,
      "poll_url": "https://data.semust.com/v1/tasks/9d3a6c1e-27b4-4f0a-8e5d-3c1b7a9f2e60",
      "created_at": "2026-11-06T13:17:02Z",
      "completed_at": null,
      "expires_at": null,
      "expires_in_seconds": null,
      "deadline_at": "2026-11-06T13:27:02Z"
    },
    {
      "task_id": "4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39",
      "service": "domain_research",
      "status": "completed",
      "http_status": 200,
      "cost": 0.0264,
      "fetched": false,
      "poll_url": "https://data.semust.com/v1/tasks/4b7e2f1c-9a3d-4e8b-b6c2-0d5f8a1e7c39",
      "created_at": "2026-11-06T13:15:26Z",
      "completed_at": "2026-11-06T13:16:41Z",
      "expires_at": "2026-11-09T13:16:41Z",
      "expires_in_seconds": 259080,
      "deadline_at": null
    }
  ]
}
AlanTipAçıklama
task_idstringTask'ın UUID'si
servicestringTask'ın ait olduğu endpoint
statusstringpending, completed veya failed
http_statusinteger | nullSonucun status code'u; çalışırken null
costnumber | nullAlınan ücret, USD; tamamlanmadıysa null
fetchedbooleanBiten sonucun size ulaşıp ulaşmadığı: POST'un kendisinde, bir tekrarda ya da GET /v1/tasks/{task_id} ile
poll_urlstringSonucun alınacağı adres
created_atstringTask'ın oluşturulma zamanı
completed_atstring | nullBitiş zamanı; çalışırken null
expires_atstring | nullSonucun silineceği zaman; çalışırken null
expires_in_secondsinteger | nullexpires_at'e kalan saniye; çalışırken null
deadline_atstring | nullÇalışırken: task'tan vazgeçilip iade edileceği zaman; bittikten sonra null

total filtreye uyan task sayısıdır; page ve limit uygulanan sayfalamadır.

Kaybolan işleri kurtarma

Süreciniz çöktüyse, bir 202'yi kaçırdıysanız ya da hiç beklemediyseniz, GET /v1/tasks?fetched=false size henüz ulaşmamış biten sonuçları listeler. Her poll_url'yi çağırın; task okunmuş olarak işaretlenir.

POST'un size doğrudan döndürdüğü sonuç da okunmuş sayılır. Böylece fetched=false bir gelen kutusu gibi çalışır: yalnızca kimsenin almadığı sonuçları içerir.

Idempotency

Bir POST'u güvenle tekrarlanabilir yapmak için Idempotency-Key header'ı gönderin. Her mantıksal istek için bir anahtar (ör. bir UUID) üretin; bir ağ hatası ya da istemci timeout'u sonrasında tekrar denerken aynı anahtarı gönderin.

  • En fazla 128 karakter. Daha uzun bir anahtar "field": "Idempotency-Key" ile 400 INVALID_PARAMETER döndürür.
  • Tüm task endpoint'lerinde çalışır. Domain Research, YouTube ve Trends body'deki idempotency_key alanını da kabul etmeye devam eder; ikisi birden gönderilirse body alanı geçerli olur.
  • Aynı API kullanıcısından aynı endpoint'e gelen aynı anahtar her zaman aynı task'ı gösterir:
Asıl taskTekrar gönderilen istek şunu alır
Hâlâ çalışıyorsaO task'ın 202'si
BittiyseSaklanan sonucu, aynı status ve body ile

Anahtarı kaç kez gönderirseniz gönderin task bir kez ücretlendirilir. Anahtar, sonuç saklandığı sürece, yani task bittikten sonra 72 saat boyunca, ayrılmış kalır. Bu süreden sonra aynı anahtar yeni bir task başlatır.

Tekrarlanan isteğin body'si asıl istekle karşılaştırılmaz. Parametreleri farklı bir istek için yeni bir anahtar kullanın.

curl -X POST https://data.semust.com/v1/serp \
  -H "SEMUST-API-USER: kullanici_adiniz" \
  -H "SEMUST-API-PASSWORD: sifreniz" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0c5d2e8a-61f4-4b39-a7de-58c90f2b4e17" \
  -d '{"keyword": "seo araçları", "language": "tr", "country": "TR"}'

Saklama süresi

Kural
Sonuç ne kadar saklanırTask bittikten sonra 72 saat
Kesin bitiş zamanıHer bitmiş yanıtta X-Task-Expires-At ve X-Task-Expires-In (kalan saniye) header'ları; listede expires_at ve expires_in_seconds
Süre doluncaSonuç silinir; GET /v1/tasks/{task_id} 410 TASK_EXPIRED döndürür
Sıkıştırılmış hâli 5 MB'tan büyük sonuçlarSaklanmaz; sorgulandığında 410 TASK_EXPIRED döner. Bu endpoint'lerde pratikte hiç yaşanmaz

Daha uzun süre ihtiyaç duyacağınız sonuçları kendi tarafınızda saklayın. Bir süre sonra task kaydının kendisi de silinir; o id artık 404 TASK_NOT_FOUND döndürür.

Ücretlendirme

  • Task başarıyla tamamlandığında ücretlendirilir, sonucu hiç sorgulamasanız bile.
  • Başarısız bir task'ın ücreti tamamen iade edilir, süresini aşan task'lar dahil.
  • Sorgulama ve listeleme ücretsizdir.
  • Önbellek eskisi gibi çalışır. Önbellekten dönen yanıtlar hemen gelir ve her endpoint'in kendi sayfasında anlatıldığı gibi ücretlendirilir.
  • Endpoint fiyatları değişmedi. Bakiye POST sırasında kontrol edilir ve bloke edilir; bu yüzden 402 INSUFFICIENT_CREDITS bir sorgulamada değil, her zaman POST'ta döner. Task'ın kullanmadığı tutar, task bittiğinde iade edilir.

Sınırlar

SınırDeğerAşılınca
POST istekleriHesabınızın dakikalık ve günlük limitleri, değişmedi429 RATE_LIMIT_EXCEEDED
GET /v1/tasks ve GET /v1/tasks/{task_id}API kullanıcısı başına dakikada 600, ayrı bir kovada429 RATE_LIMIT_EXCEEDED
Bitmemiş task'larAPI kullanıcısı başına 100429 TOO_MANY_PENDING_TASKS, Retry-After: 10

Sorgulamalar POST isteklerinizin dakikalık ya da günlük limitlerinden düşmez; sonuç toplamak yeni istek göndermenizi asla engellemez.

Aynı anda çok sayıda task çalışıyorsa her birini ayrı ayrı sorgulamak yerine birkaç saniyede bir GET /v1/tasks?fetched=false çağırın ve yalnızca bitenleri alın.

Endpoint'e özel eşzamanlılık sınırları geçerli

Domain Research ve Trends için API kullanıcısı başına aynı anda en fazla 5, YouTube için en fazla 10 istek çalışır. Beklemediğiniz task'lar da bitene kadar bu sayıya dahildir; bu yüzden wait=0 ile gönderilen büyük bir toplu iş Retry-After ile 429 TOO_MANY_CONCURRENT_REQUESTS alır. Biri bitince sıradaki isteği gönderin.

Güvenilirlik

Şunlara güvenebilirsiniz:

  • Task, iş başlamadan önce kaydedilir. Elinize bir task_id geçtiyse task vardır.
  • Sonuçlar kalıcı bir kuyruktan geçer. API'mizin deploy edilmesi ya da yeniden başlatılması bir task'ı kaybettirmez; task tamamlanır ve sonucunu alabilirsiniz.
  • Bağlantınız koparsa task durmaz. Beklerken bağlantınız kopsa, timeout'a düşse ya da süreciniz çökse bile task devam eder. Sonucu id ile alın ya da GET /v1/tasks?fetched=false ile bulun.
  • Her task'ın 10 dakikalık bir süresi vardır. Bu sürede sonuç gelmezse task, sürenin dolmasından en geç birkaç dakika sonra 504 TIMEOUT ile başarısız sayılır ve ücreti iade edilir. Tam 10. dakikada vazgeçmek yerine kesin bir yanıt alana kadar sorgulamaya devam edin.
  • Para her task için tek bir kez hareket eder. Bir blokaj, ardından tek bir ücretlendirme ya da tek bir iade; kaç kez sorgularsanız ya da aynı Idempotency-Key'i kaç kez gönderirseniz gönderin.

Hatalar

Hatalar {"error", "code", "hint", "retryable", ...} biçimindedir. Accept-Language: tr gönderirseniz error ve hint Türkçe gelir; code hiç değişmez.

DurumKodAnlamıNe yapmalı
400INVALID_TASK_IDTask id bir UUID değil202 yanıtındaki ya da GET /v1/tasks listesindeki task_id'yi kullanın
400INVALID_PARAMETERwait, Idempotency-Key ya da bir liste filtresi geçersiz; field hangisi olduğunu söylerYukarıdaki kurallara bakın
404TASK_NOT_FOUNDBöyle bir task yok ya da başka bir API kullanıcısına aitTask'ı oluşturan API kullanıcısıyla okuyun
410TASK_EXPIREDSonuç artık saklanmıyorSonuçlar task bittikten sonra 72 saat saklanır; isteği yeniden gönderin
429TOO_MANY_PENDING_TASKSBu API kullanıcısının 100 task'ı hâlâ çalışıyorRetry-After (10) saniye sonra tekrar deneyin
429RATE_LIMIT_EXCEEDEDBir dakikada 600'den fazla sorgulama ya da POST limitlerinize ulaşıldıYavaşlayın
504TIMEOUTTask zamanında bitmedi, ör. 10 dakikalık süresi doldu. Ücret alınmazİsteği yeniden gönderin

TIMEOUT artık yavaş bir HTTP isteği anlamına gelmiyor. Yavaş bir istek 202'ye dönüşür; 504 TIMEOUT ise bitirilemeyen bir task'ın son yanıtıdır. Tam liste için Hata Kodları sayfasına bakın.

{
  "error": "Task bulunamadı",
  "code": "TASK_NOT_FOUND",
  "hint": "Bir task yalnızca onu oluşturan API kullanıcısıyla okunabilir",
  "retryable": false
}

Örnekler

Aşağıdaki call(path, body) yardımcısı POST'u gönderir, 202 aldığı sürece Retry-After'a uyarak sorgular ve son response'u döndürür. Her isteğe bir Idempotency-Key eklenir; böylece ağ hatasıyla düşen bir POST güvenle tekrar gönderilir. Dönen response'u task'lardan önce nasıl işliyorsanız öyle işlersiniz.

import time
import uuid

import requests

BASE_URL = "https://data.semust.com"
AUTH = {
    "SEMUST-API-USER": "kullanici_adiniz",
    "SEMUST-API-PASSWORD": "sifreniz",
    "Accept-Language": "tr",
}


def call(path, body, wait=60, give_up_after=900):
    """Semust Data API endpoint'ine POST gönderir ve son response'u döndürür."""
    headers = {**AUTH, "Idempotency-Key": str(uuid.uuid4())}

    # Aynı Idempotency-Key ile tekrar deneme aynı task'a bağlanır ve bir kez ücretlendirilir.
    for attempt in range(3):
        try:
            response = requests.post(
                BASE_URL + path,
                params={"wait": wait},
                headers=headers,
                json=body,
                timeout=wait + 30,
            )
            break
        except (requests.ConnectionError, requests.Timeout):
            if attempt == 2:
                raise
            time.sleep(2 ** attempt)

    deadline = time.monotonic() + give_up_after
    while response.status_code == 202:
        task = response.json()
        if time.monotonic() > deadline:
            raise TimeoutError(f"task {task['task_id']} hâlâ çalışıyor")
        time.sleep(int(response.headers.get("Retry-After", task["retry_after"])))
        response = requests.get(task["poll_url"], headers=AUTH, timeout=30)

    return response


response = call(
    "/v1/domain-research",
    {"type": "keywords", "domain": "ornek.com.tr", "limit": 100},
)
data = response.json()
if response.status_code != 200:
    raise SystemExit(f"{data['code']}: {data['error']} (task {response.headers.get('X-Task-Id')})")

print(f"{data['count']} kelime, ücret ${data['cost']:.6f}")
Son güncelleme: 2026-11-06