Sayfalama
Platform standardı cursor tabanlı (keyset) sayfalamadır: limit + cursor.
Endpoint'ler bu standarda kademeli geçiyor. Bugün hepsi aynı biçimi kabul etmiyor — hangi endpoint'in ne kabul ettiği aşağıdaki tabloda. Yeni entegrasyonda limit + cursor kullanın; eski parametreler çalışmaya devam ediyor ama artık uyarı başlığı dönüyorlar.
İstek
| Parametre | Tip | Varsayılan | Açıklama |
|---|---|---|---|
limit | number | 25 | Sayfa başına kayıt (1–100). Tavanın üstü 400 döner. |
cursor | string | — | Önceki yanıtın pageInfo.nextCursor değeri. İlk sayfada gönderilmez. |
includeTotal | boolean | false | true ise pageInfo.totalCount doldurulur. Maliyetlidir. |
curl "https://api-gateway.lunixi.com/api/v1/wallet/operations?limit=25" \
-H "Authorization: Bearer $LUNIXI_TOKEN"Yanıt
pageInfo her zaman data'nın yanındadır — içinde değil.
{
"status": "success",
"code": "SUCCESS",
"data": [ { "id": "wop_01J8ZU..." } ],
"pageInfo": {
"hasMore": true,
"nextCursor": "eyJ2IjoxLCJ0IjoiMjAyNi0wOC0xN1QxMDowMDowMC4xMjM0NTZaIiwiaWQiOiJ3b3BfMDFKOFpVIn0",
"totalCount": null
},
"requestId": "req_01J8ZV..."
}Sayfalama bilgisini pageInfo'dan okuyun, data'nın içinden değil.
data endpoint'e göre bir dizi ya da bir nesne olabilir — ikisi de olur ve bu değişmeyecek. Bu yüzden sayfalama bilgisi data'nın içine konmadı: diziyi nesneye çevirmek mevcut entegrasyonları kırardı. pageInfo her iki durumda da aynı yerde, data'nın kardeşi olarak durur.
Bazı endpoint'ler geçiş süresince data.pageInfo içinde bir kopya da döndürüyor. Ona güvenmeyin — kanonik konum üst seviyedir ve kopya ileride kaldırılabilir.
data nesne dönen bir endpoint'te aynı yanıt şöyle görünür — pageInfo yine aynı yerde:
{
"status": "success",
"code": "SUCCESS",
"data": { "items": [ { "id": "sub_01J8ZU..." } ] },
"pageInfo": { "hasMore": false, "nextCursor": null, "totalCount": null },
"requestId": "req_01J8ZV..."
}Bilmeniz gerekenler
totalCountvarsayılan olaraknull'dır. Toplam kayıt sayısı cursor sayfalamada pahalıdır (filtreliCOUNT(*)= tam tarama). İlerlemek içinhasMorekullanın. Gerçekten gerekiyorsaincludeTotal=truegönderin — bu istek daha yavaştır ve rate-limit'e daha ağır sayılır.- Sayfalama yalnızca ileri yöndedir.
prevCursoryoktur. Geri navigasyon için gördüğünüz cursor'ları kendi tarafınızda saklayın. cursoropaktır. İçeriğini çözmeyin, üretmeyin, elle değiştirmeyin — biçimi haber verilmeden değişebilir. Yalnızca bir önceki yanıttan aldığınız değeri geri gönderin.- Sıralama her zaman en yeniden en eskiye ve deterministiktir. Siz sayfalarken liste yazılmaya devam etse bile kayıt tekrarlanmaz veya atlanmaz.
- Filtreleri değiştirirseniz cursor'ı sıfırlayın (baştan başlayın). Cursor, üretildiği sorguya bağlıdır.
Sıralama neden hep bir zaman damgası? Cursor sayfalaması, sıralama anahtarının değişmez olmasını şart koşar — imleç o anahtarın bir değerine çapa atar. Anahtar sonradan değişirse (risk skoru, "son görülme", "son güncelleme" gibi) bir kayıt ya iki kez görünür ya da hiç görünmez. Bu yüzden sayfalanan listelerde sıralama, kaydın oluşturulma anına bağlıdır; "en son güncellenen" gibi sıralamalar cursor almaz.
INVALID_CURSOR
Bozuk, tanınmayan ya da desteklenmeyen sürümdeki bir cursor 400 INVALID_CURSOR döndürür.
{
"status": "failure",
"code": "INVALID_CURSOR",
"statusCode": 400,
"message": "Sayfalama imleci geçersiz — …"
}Ne yapmalı: cursor'ı elle üretmeyin; bir önceki yanıttaki pageInfo.nextCursor değerini aynen gönderin. Filtreleri değiştirdiyseniz cursor'ı sıfırlayın.
Neden hata dönüyor da sessizce ilk sayfaya dönmüyor? Sessiz sıfırlama, mutabakat döngülerinde aynı sayfanın iki kez işlenmesine yol açıyordu. Açık hata, sessiz veri tekrarından iyidir.
Tüm sayfaları dolaşma
let cursor: string | undefined;
do {
const qs = new URLSearchParams({ limit: '100', ...(cursor ? { cursor } : {}) });
const res = await fetch(
`https://api-gateway.lunixi.com/api/v1/wallet/operations?${qs}`,
{ headers: { Authorization: `Bearer ${process.env.LUNIXI_SECRET_KEY}` } },
);
const body = await res.json();
// `data` bu endpoint'te bir nesne; başka endpoint'lerde dizi olabilir.
// `pageInfo` ise HER İKİ durumda da aynı yerde — `data`'nın yanında.
process(body.data.operations ?? body.data.items ?? body.data);
cursor = body.pageInfo.hasMore ? body.pageInfo.nextCursor : undefined;
} while (cursor);Eski parametreler
Bazı endpoint'ler geçmişten gelen page, offset, pageSize, pageToken, beforeSeq, cursorCreatedAt+cursorId parametrelerini kabul eder. Bunlar çalışmaya devam eder ama kullanıldıklarında yanıt şu başlıkları taşır:
Deprecation: true
Sunset: Sun, 31 Jan 2027 00:00:00 GMT
X-Deprecated-Params: offsetGeçişi planlamanız için tek sinyal bu başlıklardır. İstemcinizde Deprecation: true gördüğünüzde loglayacak bir yer bırakın — Sunset tarihine kadar sessizce çalışmaya devam eder ve o tarihte fark etmeniz gerekir.
Endpoint durumu
Geçiş sürüyor. Bugünkü fotoğraf:
Cursor kabul eden endpoint'ler
| Endpoint | Kabul ettiği | Yanıt |
|---|---|---|
GET /payments | limit + cursor · page deprecated | {data[], total, page, limit, totalPages, nextCursor} |
POST /payments/transactions:search | gövdede page.limit + page.after | Relay zarfı + pageInfo |
GET /payments/split | limit + after | Relay zarfı + pageInfo |
GET /payments/links, /payments/links/{id}/attempts, /payments/links/{id}/payments | limit + cursor (listede includeTotal de) | {items} + pageInfo |
GET /kyc/sessions | limit + cursor | {items, nextCursor} + pageInfo |
GET /wallet/operations, /end-users, /corporate-accounts, /qr | limit + cursor | {operations|items, nextCursor} + pageInfo |
GET /subscriptions/* (13 uç) | limit + cursor · pageSize/pageToken deprecated | {items, nextPageToken, pageSize} + pageInfo |
GET /marketplace/dealers | limit + cursor · offset deprecated | {items, total, nextCursor} |
GET /automation/audit-events, /connector-deliveries, /trigger-events | limit + cursor · offset deprecated | {items, total, limit, offset, nextCursor} |
GET /identity/live-sessions | limit + cursor | data dizi + pageInfo |
GET /identity/mobile-threat/decisions, /pos-threat/decisions | limit + cursor | data dizi + pageInfo |
GET /fraud/decision-logs | limit + cursor | data dizi + pageInfo |
GET /automation/flows/{id}/runs | limit + cursor · offset deprecated | {items, total, nextCursor} + pageInfo |
Son iki satırda data bir dizidir ve öyle kalacaktır — sayfalama bilgisi yanındaki pageInfo'dan okunur. Diziyi nesneye çevirmek mevcut entegrasyonları kıracağı için bilinçli olarak yapılmadı.
Henüz cursor kabul etmeyenler — burada limit bir sayfa boyutu değil tavandır: ötesindeki kayıtlara bugün erişilemez ve yanıt bunu belirtmez. "En yeni N kaydı döner" diye düşünün.
| Endpoint | Bugün |
|---|---|
GET /automation/shadow-runs, /approvals | limit + offset (deprecated) |
GET /identity/mobile-apps, /live-sessions/top-sources, /mobile-threat/package-ioc* | yalnız limit |
GET /marketplace/audit-log, /payout-ledger, /transactions | yalnız limit |
GET /ledger/payouts | limit + beforeSeq |
Tasarım gereği sayfalanmayanlar — bunlarda cursor beklemeyin, gelmeyecek:
| Endpoint | Neden |
|---|---|
GET /identity/live-sessions/{traceId}/timeline, /related-sessions, /account-links | Tek bir trace'in detayı — doğası gereği sınırlı |
GET /sanction/intelligence/entity-search | Alaka sıralı arama; "en iyi N eşleşme" doğru davranıştır |
GET /identity/admin/intelligence/{device,ip,identity}-profiles | Risk skoruna göre sıralı triyaj listesi — sıralama anahtarı DEĞİŞKEN |
GET /identity/admin/bot-trust/actors | Kayan pencere (varsayılan 30 gün) üzerinde toplulaştırma |
Risk sıralı listeler neden cursor almıyor? Keyset sayfalaması, sıralama
anahtarının değişmez olmasını şart koşar — imleç, o anahtarın bir
değerine çapa atar. Risk skoru her yeni oturumda yeniden hesaplanır: 2.
sayfayı isterken 1. sayfadaki bir profilin skoru düşerse o profil 2. sayfaya
kayar ve iki kez görünür; yükselen bir profil ise hiç görünmez. Bu
yüzeylerde doğru davranış zaten "en riskli N" — sıralamanın tamamını
dolaşmak değil. Daraltmak için scopeType, fingerprintId, deviceId,
ipAddress, identityKey filtrelerini kullanın.
Panel / operatör yüzeyi — bu endpoint'ler RBAC izni ister ve Swagger'da listelenmez; entegrasyon akışında değil, panelde kullanılır.
| Endpoint | Kabul ettiği |
|---|---|
GET /sanction/screening/history | limit + cursor ✅ |
GET /sanction/monitoring/{alerts,entities} | limit + cursor + includeTotal ✅ |
GET /sanction/analyst/{case-queue,hit-queue} | limit + cursor ✅ |
GET /sanction/monitoring/matches | limit + cursor ✅ |
GET /sanction/blacklist | limit + cursor + sayaçlar ✅ |
GET /fraud/admin/{shadow-runs,outbox-events,blacklists} | limit + cursor ✅ |
GET /fraud/admin/script-assets/{id}/execution-logs | limit + cursor ✅ |
GET /identity/admin/bot-trust/{issuers,keys} | limit — kayıtlı sağlayıcı kümesi küçüktür |
GET /identity/admin/bot-trust/review-events | limit (tavan 100) |
bot-trust/review-events bir denetim izidir ve yalnız yönetici işlemiyle
(sağlayıcı/anahtar durum değişikliği) yazılır — insan hızında büyür, akış
değildir. Bugün 100'lük tavan yeterli; issuerId ile daraltıldığında bir
sağlayıcının tüm geçmişi tek yanıta sığar. Tavana dayanan bir kurulum
görürsek cursor eklenecek; "tasarım gereği sayfalanmaz" DEĞİL.
İlgili
- Hata Yönetimi — zarf ve kodlar
- API'ye Genel Bakış — taban adres ve istek biçimi