Hesap sayfanın bir site portföyü için yaptığı her şey, JSON olarak: siteleri toplu ekle, etiketle ve çıkar; erişilebilirliği, ads.txt, robots ve noindex sinyallerini, sunucuları ve güvenlik durumlarını oku; tarama başlat ve raporları oku.
Temel adres
https://approvalens.com/api/v1
Her ücretli plana dahil · her site ve sunucu planına dahil.
Her uç nokta JSON döner ve yalnızca anahtarın sahibi olan hesaba dokunur: sitelerine, sunucularına, etiketlerine ve e‑posta adresinle taranan ya da satın alınan raporlara.
1
Oturum aç. Her ücretli plan API'yi içerir: site planı da sunucu planı da.
2
Hesap → API anahtarları bölümünü aç ve bir anahtar oluştur. Betiğin bir şeyleri değiştirecekse "Okuma ve yazma"yı seç. Anahtarı kopyala: yalnızca bir kez gösterilir.
3
Anahtarı bir ortam değişkenine koy ve her istekte bearer token olarak gönder.
1. Anahtarı dene: kime ait, yetkileri ne, kaç boş site yerin var
API erişimi her ücretli planla gelir: site planı (ayda 4,99 $'dan başlar) ya da sunucu planı; plan etkinken ya da iptal edilmiş ama henüz bitmemişken. Tam rapora dahil bir aylık izleme API içermez. Plan yoksa anahtarların silinmez ama her istek 403 subscription_required döner; plan yeniden etkin olunca aynı anahtarlar çalışır.
Anahtarlar al_live_ ile başlar, ardından 32 karakter gelir. Authorization başlığında gönder:
Bir hesabın en fazla 5 etkin anahtarı olabilir. Hesap sayfasında iptal ettiğin anahtar hemen çalışmaz olur. Her anahtarın yalnızca SHA-256 parmak izini saklarız; kaybolan anahtar yeniden gösterilemez, yenisini oluştur.
API sunucular ve betikler içindir. CORS başlığı göndermez; başka bir sitedeki web sayfası onu ziyaretçinin tarayıcısından çağıramaz. Anahtarı asla ön yüz koduna koyma.
03
Yetkiler: okuma ve yazma
Her anahtarın, oluştururken seçtiğin bir yetkisi vardır:
okuma: tüm GET uç noktaları ve ücretsiz yapay zekâ botu kontrolü (hesabında hiçbir şeyi değiştirmez).
okuma ve yazma: okumanın yaptığı her şey, artı site ekleme, etiketleme ve çıkarma, etiketler, sunucu kayıt anahtarları, tarama başlatma ve hesabında zaten olan bir krediyi kullanma.
7 Ekim 2026'dan önce oluşturulan anahtarlar yalnızca okuma yetkilidir: o zaman yazma erişimi yoktu ve eski bir betikteki eski bir anahtarın sessizce bu yetkiyi kazanmasını istemedik. Bir şeyleri değiştiren betikler için yazma yetkili yeni bir anahtar oluştur. Yalnızca okuma yetkili bir anahtar yazma uç noktasını çağırırsa 403 insufficient_scope alır.
403
{
"error": "insufficient_scope",
"message": "This key is read-only. Make a key with the write scope on your account page to POST here.",
"details": {
"required": "write"
}
}
Hiçbir uç nokta senden ücret almaz. POST /api/v1/scans/:id/unlock yalnızca hesabında zaten olan bir site kredisini kullanır; kredin kalmadıysa 402 no_credits döner.
04
İstek sınırları
Anahtar başına dakikada 60 istek; kayan bir dakika üzerinden sayılır ve herhangi bir saniyede en fazla 10. Her yanıtta şunlar bulunur:
X-RateLimit-Limit dakikalık sınır
X-RateLimit-Remaining geçerli dakikada kalan istek
X-RateLimit-Reset en eski isteğin pencereden çıkacağı an (Unix saniyesi)
X-RateLimit-Burst saniyelik üst sınır
X-Request-Id bu isteğin kimliği; bize yazarsan bunu belirt
Bir sınır aşılınca saniye cinsinden Retry-After başlığıyla 429 rate_limited dönülür. Bazı uç noktaların, sitedekiyle aynı, kendi ek sınırları vardır:
POST /ai-checks: anahtar başına dakikada 6; son 10 dakikada kontrol edilmiş bir site o sonucu geri alır (cached: true).
POST /scans: anahtar başına dakikada 8, hesap başına da ücretsiz taramanın saatlik ve günlük sınırları. Son 20 dakikada taranmış bir site o taramayı geri verir.
POST /sites: hesap başına 10 dakikada 30 çağrı ve saatte 1.500 eklenen site; hesap sayfasındaki içe aktarmayla ortak.
POST /sites/remove: hesap başına 10 dakikada 20 çağrı. POST /scans/:id/unlock: 10 dakikada 12.
Yanlış anahtarlı istekler: IP adresi başına dakikada 20.
429
{
"error": "rate_limited",
"message": "Rate limit of 60 requests per minute per key reached. Retry in 12 s."
}
05
Sayfalama
Listeler (siteler, sunucular, taramalar, raporlar) sayfa sayfa gelir ve sen okurken bir sayfa kaymasın diye sıralıdır: siteler ve sunucular kimliğe göre, taramalar en yeni önce. limit sayfa boyutudur (varsayılan 100, en fazla 300; varsayılan en büyük site planını kapsar, yani hiç sayfalamayan istemciler de her siteyi görür).
Her yanıtta next_cursor bulunur. Sonraki sayfa için onu aynı filtrelerle, hiç değiştirmeden ?cursor= olarak geri gönder; son sayfada null olur. İmleçler opaktır, kendin oluşturma. total (siteler, sunucular) filtrelere uyan tüm öğeleri sayar.
Her POST ile bir Idempotency-Key başlığı gönder (en iyisi bir UUID). Bağlantı koparsa aynı anahtarla yeniden dene: istek bir kez çalışır, yeniden deneme ilk yanıtı Idempotent-Replayed: true ile geri alır. Yanıtlar 24 saat saklanır.
Bir anahtarı farklı bir istek için yeniden kullanırsan 422 idempotency_key_reused, ilki hâlâ çalışırken yeniden denersen 409 idempotency_in_progress döner. Bizim tarafımızda başarısız olan (5xx) bir istek saklanmaz, yeniden deneme onu tekrar çalıştırır. Zaten var olan bir siteyi eklemek ve zaten açık bir siteyi açmak her durumda güvenle tekrarlanabilir.
07
Kurallar
Zamanlar UTC'de, kesirsiz ISO-8601'dir: 2026-10-06T09:14:03Z. Olmayan zaman null'dır.
Sitelere alan adıyla erişilir ve www ile de www olmadan da eşleşir; sunuculara, etiketlere ve anahtarlara sayısal kimlikleriyle.
v1 yanıtlarına alan eklenebilir; mevcut alanların adı ve anlamı değişmez. Geriye uyumsuz değişiklikler yolda yeni bir sürümle gelir.
08
Hatalar
Hatalar HTTP durum kodunu ve tek bir JSON biçimini kullanır: makinenin okuyacağı bir kod, insanlar için bir cümle ve işe yarayacaksa details (bir sınır, planının doluluğu, gereken yetki):
409
{
"error": "plan_full",
"message": "Your site plan is full. Remove a site or move to a larger plan.",
"details": {
"plan": {
"limit": 25,
"used": 25,
"free": 0
}
}
}
Kod
Durum
Anlamı
bad_request
400
Bir parametre ya da JSON gövdesi geçersiz; message alanın adını verir.
bad_url
400
Tarayabileceğimiz ya da izleyebileceğimiz herkese açık bir web adresi değil.
invalid_key
401
Anahtar hatalı biçimde, bilinmiyor ya da iptal edilmiş.
missing_key
401
Authorization: Bearer başlığı yok.
no_credits
402
Açmak için site kredisi gerekir ve hesabında hiç yok. Ücret alınmadı.
insufficient_scope
403
Anahtar yalnızca okuma yetkili, uç nokta ise bir şey değiştirir. details.required gereken yetkiyi söyler.
subscription_required
403
Hesabın etkin bir ücretli planı yok. Plan olunca aynı anahtarlar yeniden çalışır.
not_found
404
Böyle bir uç nokta yok ya da hesabında bu kimlikte veya alan adında bir şey yok.
idempotency_in_progress
409
Bu Idempotency-Key ile gönderilen istek hâlâ çalışıyor.
limit_reached
409
Çok fazla etkin kayıt anahtarı var.
no_plan
409
Site eklemek için etkin bir site planı gerekir.
not_ready
409
Tarama henüz bitmedi.
plan_full
409
Site planının bütün yerleri dolu; sayılar details.plan içinde.
tag_exists
409
Bu adda bir etiket zaten var.
tag_limit
409
Hesabın 100'den fazla etiketi olurdu.
idempotency_key_reused
422
Bu Idempotency-Key farklı bir istek için kullanılmış.
unreachable
422
Sitenin adı çözümlenmiyor ya da site yanıt vermedi.
rate_limited
429
Çok fazla istek: Retry-After kadar saniye bekle.
unavailable
503
Bizim tarafımızda bir sorun oldu. Biraz sonra tekrar dene.
09
Uç noktalar
Biçimler birebir aynıdır; değerler örnek bir hesaptır. Her istek Authorization başlığı ister.
Hesap
Anahtar, yetkileri, planların ve boş yerleri.
GET/api/v1/accountYetki: read
Hesabın
Anahtarın ait olduğu e‑posta, yetkileri, site ve sunucu planların ile dolu ve boş yerleri ve kredi bakiyen. Toplu eklemeden önce boş yerlerine buradan bak.
İzlenen siteler: filtreli liste, toplu ekleme ve çıkarma, etiketler, erişilebilirlik, kesintiler ve günlük AdSense sinyalleri.
GET/api/v1/sitesYetki: readsayfalı
Siteleri listele
Hesabındaki her site ve şu anki durumu: ayakta mı, 30 günlük erişilebilirlik, son yanıt süresi, TLS, ads.txt, günlük kontrolün AdSense sinyalleri (ads.txt, Mediapartners ve Googlebot erişimi, noindex, yeni engellenen yapay zekâ botları), etiketler, plan kapsamı ve varsa süren kesinti. Etikete, duruma, izlenmeye, sinyale ya da alan adının bir kısmına göre filtrele.
Parametreler
tag
yalnızca bu etiketi taşıyanlar; birkaçından herhangi biri için tekrarla ya da virgülle ayır
status
up, down ya da unknown (henüz yoklanmadı)
monitoring
true ya da false: şu an izleniyor mu
signal
yalnızca bu sorunu olan siteler: down, ads_txt_problem, noindex, mediapartners_blocked, googlebot_blocked, ai_blocked_new, tls_expiring (14 gün ya da daha az), over_plan, not_monitored
Site planına bir site (url) ya da 300'e kadar site (urls) ekler; hesap sayfasındaki içe aktarmanın aynısı: her giriş denetlenir (biçim, DNS, herkese açık adres), tekrarlar birleştirilir ve yalnızca planındaki boş yerler kullanılır; hiçbir şey satın alınmaz. Etiketler eklenen ya da zaten olan her siteye konur. Tek url ile: eklendiyse 201, zaten varsa 200, değilse 409 no_plan ya da plan_full. urls ile: 200 ve her giriş için sırasıyla bir sonuç (added, already, invalid, duplicate, no_slot, no_plan). dry_run: true eklemeden önizler.
Listedeki her şey, artı yapay zekâ botu dökümü, günlük güvenlik kontrolleri (HTTPS yönlendirmesi, HSTS, başlıklar, açıkta kalan .env ya da .git), en son 5 kesinti ve en son 10 uyarı.
Siteyi planından çıkarır; yeri hemen boşalır. Ücretli bir rapor ayı onu sonuna kadar izlenir tutar (monitoring: true); artık hiçbir şeyin kapsamadığı bir site geçmişiyle birlikte silinir (deleted: true).
5 dakikalık erişilebilirlik kontrolü: şu anki durum, 30 günlük erişilebilirlik, son yanıt süresi (ölçülene kadar null) ve son 30 UTC günü tek tek, en eskisi önce. Site eklenmeden önceki günler null'dır.
Tek çağrıda 300'e kadar siteye (alan adıyla) ve sunucuya (kimlikle) etiket koyar (op add; olmayan adlar oluşturulur) ya da kaldırır (op remove). Bilinmeyen alan adları ve kimlikler not_found içinde döner.
Ajanın çalıştığı sunucular: şu anki değerleri, açık uyarılar, tüm ayrıntı ve makine eklemek için kayıt anahtarları.
GET/api/v1/serversYetki: readsayfalı
Sunucuları listele
Sunucuların ve şu anki değerleri (CPU, yük, bellek, en dolu disk ve ne zaman dolacağı), kaç uyarı, güvenlik sinyali ve bildirimin açık olduğu, etiketler ve planlarının sunucu kotası.
Parametreler
status
pending, up, silent ya da paused
tag
yalnızca bu etiketi taşıyanlar; birkaçından herhangi biri için tekrarla ya da virgülle ayır
Sunucu sayfasının gösterdiği her şey, kendi sözleşmesiyle: genel bakış (erişilebilirlik, kesintiler, yeniden başlatmalar), güvenlik (açıklamalı portlar, SSH, oturum açmalar, güvenlik duvarı, güncellemeler, öneriler, puan), aralık için grafik serileriyle kaynaklar, olaylar ve ajan. İçerideki zamanlar Unix saniyesidir.
Parametreler
id
sunucu kimliği
range
grafik aralığı: 1h, 24h (varsayılan), 7d ya da 30d
Yeni bir kayıt anahtarı. Gizli değeri ve kurulum satırı yalnızca bu yanıtta bulunur; satırı her makinede çalıştır (Ansible, cloud-init…). Sunucu kotanı aşan makineler duraklatılır.
Ücretsiz tarama başlat, ilerlemesini izle, raporları oku ve birini zaten sahip olduğun bir krediyle aç.
GET/api/v1/scansYetki: readsayfalı
Taramaları listele
API'den ya da oturum açıkken başlattığın taramalar ve e‑posta adresinle satın alınan raporlar, en yenisi önce: puan ve karar (bitene kadar ya da site bizi engellediyse null), neyin ne zamana kadar açık olduğu ve bir açmanın başlattığı derin tarama.
Bir sitenin ücretsiz taramasını ana sayfanın yaptığı gibi başlatır. Birkaç dakika sürer: GET /scans/:id ile yokla. Son 20 dakikada taranmış bir site o taramayı geri verir (cached: true).
Tarama sürerken ilerleme (aşama, yüzde, kuyruktaki sıra), sonra puan, karar ve rapor özeti. Bir bulgunun arkasındaki sayfalar ve parametreler yalnızca hesabında site açıksa (ya da her rapor ücretsizken) gelir.
Bitmiş bir taramanın tam raporunu hesabında zaten olan bir site kredisiyle açar: 30 gün, derin yeniden tarama ve sınırsız yeniden tarama. Asla ücret almaz: zaten açık bir site hiçbir şeye mal olmaz (already: true), kredin kalmadıysa 402 no_credits döner.
Puan, karar, önem derecesine göre sorun sayıları, kategori puanları ve başlığı ile önem derecesiyle her başarısız bulgu; kanıtlar yalnızca hesabında site açıksa.
Bir ana sayfanın ücretsiz “Yapay zekâ siteni görebiliyor mu?” kontrolünü çalıştırır (yaklaşık 20 saniye) ve tam sonucu döner: her yapay zekâ botunun robots.txt kuralı ve benzetilmiş isteği, meta yönergeleri, llms.txt ve site haritası. Yeni kontrol için 201, önbellekteki için 200. Yalnızca okuma yetkili anahtarlar da çalıştırabilir.
Uyarılarının nereye gittiği: hesap e‑postası, ek e‑posta adresleri, Slack, Telegram, Discord ve webhook'lar; her birinin uyarı türleri, en düşük önem derecesi, etiketleri, sessiz saatleri ve özeti. Gizli değerler asla gönderilmez: hedefler maskelenir. Değiştirmek için hesap sayfasını kullan.
Webhook hedefleri (hesap sayfanda kurulur, GET /alert-channels listeler) her uyarıyı bir JSON POST olarak alır; webhook'u oluştururken ya da yenilerken gösterilen gizli değerle imzalanır. X-Approvalens-Signature başlığı t=1791273600,v1=5f2c… biçimindedir: t gönderildiği Unix zamanı, v1 ise anahtar olarak gizli değerin kullanıldığı, zamanın, bir noktanın ve ham gövdenin hex HMAC-SHA256'sıdır.
Aynı HMAC'i ham gövde üzerinden (ayrıştırmadan önce) hesapla, sabit zamanlı karşılaştır ve 5 dakikadan eski bir t'yi reddet. X-Approvalens-Delivery yeniden denemelerde aynı kalır; tekrarları bununla ayıklayabilirsin.
Node.js
import crypto from "node:crypto";
// header: the X-Approvalens-Signature value; raw: the request body as received (a Buffer)
export function verify(secret, header, raw) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
if (!t || Date.now() / 1000 - t > 300) return false; // older than 5 minutes
const want = crypto.createHmac("sha256", secret).update(`${t}.`).update(raw).digest("hex");
const got = String(parts.v1 || "");
return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
}
Python
import hashlib, hmac, time
def verify(secret: str, header: str, raw: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t = int(parts.get("t", "0") or 0)
if not t or time.time() - t > 300: # older than 5 minutes
return False
want = hmac.new(secret.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(parts.get("v1", ""), want)
API'nin tamamı bir OpenAPI 3.1 belgesi olarak: her uç nokta, parametre, gövde ve yanıt şemasıyla ve örneğiyle; bu sayfayla aynı koddan üretilir. Postman'e, Insomnia'ya ya da bir kod üreticisine yükle.
v1.1 · 2026-10-07Yazma erişimi ve fazlası: siteleri toplu ekleme, etiketleme ve çıkarma, site filtreleri ve erişilebilirlik, etiketler, sunucular ve kayıt anahtarları, taramalar ve krediyle açma, hesap, uyarı hedefleri. Anahtar yetkileri (önceki anahtarlar yalnızca okuma yetkili), saniyelik sınır, idempotency anahtarları, imleçler ve OpenAPI. API artık sunucu planları dahil her ücretli planla geliyor.