Data API (sebelumnya didokumentasikan di sini sebagai Server API) memungkinkan backend Anda membaca data identifikasi yang sudah dikumpulkan TRACIO untuk workspace Anda: riwayat seorang pengunjung, sesi satu per satu, dan penghitung velocity pada jendela pendek.
Ia melengkapi Webhook, bukan menggantikannya:
| Webhooks | Data API | |
|---|---|---|
| Arah | TRACIO mendorong ke endpoint Anda | Backend Anda menarik saat dibutuhkan |
| Waktu | Saat setiap identifikasi terjadi | Kapan saja, dalam jendela retensi Anda |
| Paling cocok untuk | Bereaksi terhadap sebuah peristiwa | Mencari data saat mengambil keputusan, pengisian ulang, investigasi |
Kedua permukaan tersedia mulai paket Pro ke atas.
https://api.tracio.ai/v1Ini adalah host yang berbeda dari endpoint peramban (edge.tracio.ai) dan dari dashboard
(app.tracio.ai). Ketiganya terpisah: peramban berbicara dengan edge memakai kunci
public Anda, sedangkan backend Anda berbicara dengan Data API memakai kunci
secret Anda.
Setiap permintaan membawa secret key Anda sebagai bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API hanya untuk komunikasi server ke server. Header CORS sengaja tidak dikembalikan, sehingga peramban tidak dapat memanggilnya — itulah yang menjaga secret key Anda tetap di luar kode sisi klien. Jangan pernah mengirimkan secret key ke peramban.
Buat di dashboard pada bagian API Keys, dengan memilih tipe secret.
tracio_sk_ diikuti 43 karakter, total 53. Dashboard
menampilkannya berdasarkan beberapa karakter pertama sehingga Anda bisa membedakan
antar kunci.Rotasi menerbitkan kunci baru dan membiarkan kunci lama tetap bekerja selama 7 hari, sehingga Anda bisa menggulirkannya tanpa waktu henti. Terapkan kunci baru, pastikan trafiknya sudah berpindah, lalu biarkan kunci lama kedaluwarsa. Public key tidak dapat dirotasi — kunci itu bukan rahasia dan memang terlihat di sumber halaman Anda.
Setiap route adalah GET. Tidak ada operasi tulis di Data API: ia membaca data, dan
konfigurasi Anda berada di dashboard.
| Metode | Path | Mengembalikan |
|---|---|---|
GET | /v1/visitors/{visitorId} | Riwayat teragregasi satu pengunjung, plus sesi terbarunya |
GET | /v1/visitors/{visitorId}/sessions | Daftar terpaginasi sesi-sesi pengunjung tersebut |
GET | /v1/visitors/{visitorId}/sessions/latest | Satu sesi paling baru saja |
GET | /v1/visitors/{visitorId}/velocity | Penghitung aktivitas pada jendela pendek |
GET | /v1/sessions/{requestId} | Satu sesi berdasarkan pengidentifikasi permintaannya |
GET | /.well-known/webhook-keys | Kunci publik untuk tanda tangan platform webhook (tanpa autentikasi) |
Garis miring di akhir diterima dan diabaikan. Path yang tidak dikenal atau metode yang salah mengembalikan amplop error JSON yang sama seperti segala hal lainnya, tidak pernah berupa halaman HTML atau teks biasa.
Setiap pembacaan dibatasi oleh sebuah jendela waktu, yang dikendalikan dua parameter query opsional:
| Parameter | Menerima |
|---|---|
from | YYYY-MM-DD atau timestamp RFC 3339 lengkap |
to | YYYY-MM-DD atau timestamp RFC 3339 lengkap |
to mencakup seluruh hari itu.400 invalid_request dan pesan
time must be YYYY-MM-DD or RFC3339.meta, jadi periksalah meta.from dan meta.to alih-alih
menganggap permintaan Anda dipenuhi apa adanya.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Responsnya membawa riwayat agregat dan menyertakan sesi terbaru, sehingga kasus umum cukup dengan satu permintaan alih-alih dua:
{ "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "firstSeenAt": "2026-05-02T10:11:12Z", "lastSeenAt": "2026-07-25T08:00:00Z", "visits": 42, "incognitoVisits": 3, "uniqueIps": 5, "uniqueCountries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "maxRiskScore": 63, "avgBotScore": 12.5, "botSessions": 7, "lastDecision": "real" }, "network": { "vpnSeen": false, "proxySeen": false, "torSeen": false, "datacenterSeen": true, "proxyDetectedSeen": true, "lastIsp": "Deutsche Telekom", "lastRealIp": "203.0.113.7" }, "lastSession": { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "accountId": "user_8842", "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", "browser": { "name": "Chrome", "version": "126.0" }, "os": { "name": "macOS", "version": "14.5" }, "device": "desktop", "gpu": "Apple M2", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "proxyDetected": true, "realIp": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" }, "asn": 3320 }, "screen": { "width": 2560, "height": 1600, "colorDepth": 30, "pixelRatio": 2 }, "locale": { "languages": ["en-US", "de"], "timezone": "Europe/Berlin" }, "clientHints": { "architecture": "arm", "bitness": "64", "platformVersion": "14.5.0" }, "extensions": [ { "slug": "ublock-origin", "name": "uBlock Origin", "category": "adblock", "risky": false, "storeUrl": "https://chromewebstore.google.com/detail/cjpalhdlnbpafiamejdnhcphjbkeiagm" } ], "bot": { "result": "human", "score": 4.5, "antidetectScore": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "matchType": "exact", "matchConfidence": 0.99 }, "deviceInfo": { "deviceId": "d_4f9c2e", "crossBrowser": true, "confidence": 0.88, "linkedBrowsers": 2 }, "decision": { "action": "real", "riskScore": 12 } }, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| Field | Arti |
|---|---|
visits, incognitoVisits | Total kunjungan dalam jendela, dan berapa yang terjadi di jendela pribadi |
uniqueIps, uniqueCountries | Alamat dan negara berbeda yang terlihat dalam jendela |
browsers, os, devices | Lingkungan berbeda tempat pengunjung ini pernah muncul |
risk.maxRiskScore | Skor risiko tertinggi yang tercatat dalam jendela, 0..100 |
risk.lastDecision | Keputusan yang tercatat untuk kunjungan paling baru |
risk.avgBotScore, risk.botSessions | Rata-rata skor bot dan jumlah sesi bot — Business ke atas |
network.*Seen | Apakah VPN, proxy, node keluar Tor, atau alamat datacenter pernah terlihat pada pengunjung ini |
network.proxyDetectedSeen | Apakah setidaknya satu kunjungan dalam jendela keluar lewat proxy atau VPN di depan peramban — lihat network.proxyDetected pada bagian Fakta perangkat |
network.lastIsp | ISP paling baru — Business ke atas |
network.lastRealIp | Alamat paling baru yang teramati di balik proxy atau VPN — Business ke atas; tidak ada bila tidak ada yang teramati |
lastSession | Objek sesi lengkap untuk kunjungan paling baru |
meta | Paket, retensinya dalam hari, dan jendela yang benar-benar diterapkan |
Pengunjung tanpa data di dalam jendela retensi mengembalikan 404 not_found dengan pesan
visitor not found in the retention window — itu bukan error pada integrasi Anda,
melainkan berarti pengunjungnya baru atau datanya sudah melewati masa retensi.
Sesi membawa dua verdict, dan keduanya menjawab pertanyaan berbeda — apakah kliennya otomatis, dan apa kesimpulan keseluruhan dari mesin risiko:
| Field | Nilai |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Hadir ketika bot.result bernilai bot: bisa berupa alat tertentu (playwright, puppeteer, selenium, jsdom, claude_computer_use…) atau sebuah keluarga bila alatnya tidak disebutkan — automation, headless, antidetect, extension, privacy_browser, other |
decision.action | real, fake, suspicious |
bot.score dan decision.riskScore sama-sama berjalan pada 0..100. Pada Business ke
atas, guidance mengubahnya menjadi saran per skenario pada tangga
allow → challenge → review → deny — lihat
Guidance untuk arti setiap anak tangganya.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Parameter | Default | Catatan |
|---|---|---|
limit | 50 | Dibatasi di 500; nilai lebih besar dipangkas, bukan ditolak |
from, to | Retensi paket | Jendela waktu bersama yang dijelaskan di atas |
cursor | — | Kursor paginasi buram dari halaman sebelumnya |
botResult | — | Hanya simpan sesi dengan verdict bot ini |
minRiskScore | — | Hanya simpan sesi pada skor risiko ini atau di atasnya, 0..100 |
Sesi dikembalikan dari yang terbaru lebih dulu:
{ "items": [ { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "nextCursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "hasMore": true, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}Paginasi berbasis kursor. Tidak ada parameter page atau offset: kirimkan kembali
nextCursor yang Anda terima sebagai cursor, dan teruskan selama hasMore bernilai
benar.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v1/visitors/${visitorId}/sessions`) url.searchParams.set("limit", "500") if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${secretKey}` } }) if (!res.ok) throw new Error(`Data API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.nextCursor } while (cursor)
return sessions}Perlakukan kursor sebagai buram — isinya adalah detail implementasi dan bisa berubah.
Kursor yang telah disunting ditolak dengan 400 invalid_request dan pesan
malformed cursor.
Selain peramban dan OS yang diambil dari User-Agent, sebuah sesi membawa apa yang dilaporkan peramban pengunjung tentang mesinnya, sudah dibersihkan di sisi kami. Setiap field tidak ada bila kunjungan tidak membawa data semacam itu, jadi perlakukan masing-masing sebagai opsional.
| Field | Arti |
|---|---|
gpu | Model adaptor video seperti yang dilaporkan peramban (WebGL), dinormalisasi menjadi nama yang terbaca — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer berarti tidak ada GPU nyata (mesin virtual atau lingkungan headless); Safari melaporkan Apple GPU |
network.proxyDetected | Lalu lintas HTTP kunjungan dan jalur jaringan mentahnya keluar lewat jaringan yang berbeda — ada proxy atau VPN di depan peramban; dua alamat dari penyedia yang sama (NAT operator, keluaran kedua dari VPN yang sama) tidak dihitung |
network.realIp.address, .country, .isp | Alamat publik yang teramati pada jalur jaringan mentah, yaitu alamat di balik proxy atau VPN, beserta negara dan ISP-nya — Business ke atas; tidak ada bila alamat semacam itu tidak teramati (country dan isp tidak ada bila tidak dapat ditentukan) |
deviceInfo.deviceId, .crossBrowser, .confidence, .linkedBrowsers | Hadir ketika identitas perangkat berhasil ditentukan: id stabil dari perangkat fisik yang menembus semua peramban di atasnya, apakah kunjungan ini datang lewat peramban yang berbeda dari sebelumnya, keyakinan atas kecocokan itu, dan berapa banyak pengunjung (peramban) berbeda yang berbagi perangkat tersebut — lebih dari satu berarti satu mesin di bawah beberapa identitas peramban — Business ke atas |
osEnvironment | Lingkungan desktop yang diukur pada mesin Linux (Mint 22+, Ubuntu, GNOME, KDE) — Business ke atas; tidak ada bila tidak dapat ditentukan |
spoofing | Apa yang diklaim kunjungan dibandingkan dengan apa yang diukur pemeriksaan independen (claimed, real, spoofedAxes dari os, gpu, screen, network, browser; anonymousBrowser dengan nama produk) — Business ke atas; hadir hanya bila pemalsuan terdeteksi |
screen.width, .height, .colorDepth, .pixelRatio | Resolusi layar, kedalaman warna, dan device pixel ratio seperti yang dilaporkan peramban — Business ke atas |
locale.languages, locale.timezone | Bahasa pilihan dan zona waktu peramban itu sendiri — berbeda dengan geo.timezone yang diturunkan dari alamat IP; ketidakcocokan di antara keduanya adalah tanda umum lokasi yang dipalsukan — Business ke atas |
clientHints.architecture, .bitness, .model, .deviceName, .platformVersion | User-Agent Client Hints: arsitektur dan bitness CPU, kode model perangkat (Android, mis. SM-A556B) beserta nama pemasarannya dari daftar perangkat Google Play (deviceName, mis. Samsung Galaxy A55 5G), dan versi platform yang persis; hanya peramban berbasis Chromium — Business ke atas |
environment.virtualMachine, environment.hypervisor | Hadir hanya bila adaptor video menyatakan dirinya sebagai adaptor virtual; hypervisor adalah kamus tertutup (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Blok yang tidak ada berarti tidak ada bukti semacam itu — Business ke atas |
extensions mencantumkan ekstensi peramban yang terdeteksi selama kunjungan — Business ke atas. Setiap entri berupa sebuah objek:
| Field | Arti |
|---|---|
slug | Pengidentifikasi mesin yang stabil untuk ekstensi, nilai yang sama dengan yang dikirim webhook |
name | Nama yang mudah dibaca manusia |
category | Kelas kasar — adblock, privacy, automation, wallet, vpn, devtools, other, dan seterusnya |
risky | true untuk ekstensi yang terkait dengan otomasi, pemalsuan, atau pencurian kredensial |
storeUrl | Tautan ke halaman ekstensi di toko, bila diketahui |
Sebuah temuan baru dilaporkan setelah lolos pemeriksaan kepercayaan kami — lingkungan yang menjawab "terpasang" pada setiap pemeriksaan, atau kumpulan yang lebih panjang dari 12 nama, dibuang karena tidak dapat diandalkan. Karena itu daftar yang kosong atau tidak ada berarti "tidak ada yang bisa kami konfirmasi", bukan "tidak ada ekstensi yang terpasang". Bacalah itu sebagai bukti, bukan sebagai daftar inventaris.
Yang paling baru:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Ini mengembalikan objek sesi telanjang — bukan array, dan tidak dibungkus dalam amplop.
Pengunjung tanpa sesi apa pun dalam jendela mengembalikan 404 not_found dengan
no sessions for this visitor in the retention window.
Atau berdasarkan requestId, pengidentifikasi yang juga muncul pada payload webhook:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId bersifat opsional di sini, tetapi mengirimkannya ketika Anda mengetahuinya
membuat pencariannya jauh lebih cepat.
Velocity menjawab pertanyaan "seberapa banyak yang dilakukan pengunjung ini belakangan ini" — bentuk khas dari credential stuffing, uji coba kartu, dan pendaftaran massal.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window menerima 1h, 24h, atau 7d dan secara default 24h. Nilai lain apa pun
ditolak dengan 400 invalid_request dan window must be one of: 1h, 24h, 7d.
{ "window": "1h", "events": 37, "uniqueIps": 9, "uniqueCountries": 3, "uniqueAccounts": 12, "botEvents": 4, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}uniqueAccounts menghitung nilai linkedId berbeda yang Anda kirim untuk perangkat ini
— lihat Penautan akun. botEvents tersedia mulai Business ke
atas.
Field yang tidak ada berarti "tidak ada data", tidak pernah berarti nol. Field tanpa
nilai dihilangkan sepenuhnya alih-alih dikirim sebagai 0, "", atau null: pengunjung
yang benar-benar baru tidak punya matchConfidence, kunjungan yang bersih tidak punya
antidetectScore atau suspectScore. Satu-satunya pengecualian yang disengaja adalah
bot.score, yang selalu ada bahkan ketika nilainya nol. Bacalah field secara defensif.
Payload bergantung pada paket Anda. Setiap paket dengan akses API mendapatkan sesi dasar — pengidentifikasi, timestamp, URL, IP, user agent, peramban, OS, perangkat, geo, network, bot, identification, dan decision. Pro menambahkan identification.matchType, identification.matchConfidence, bot.type, bot.antidetectScore, gpu, dan network.proxyDetected. Business dan Enterprise menambahkan extensions, geo.isp, network.asn, network.realIp, decision.suspectScore, identification.driftScore, reasons, behavior, guidance, deviceInfo, spoofing, osEnvironment, screen, locale, clientHints, dan environment. Field tingkat orang (personId, reputation, linkedAccountsCount, linkedVisitorsCount) disediakan untuk Business dan Enterprise dan akan muncul begitu lapisan orang diaktifkan — saat ini lapisan itu berjalan dalam mode pengamatan dan field-field ini tidak dikirimkan. Tidak adanya field Business pada paket Pro bukanlah sebuah error.
Bagian internal pada tingkat sinyal tidak pernah dikembalikan, pada paket mana pun: nama masing-masing sinyal, bobotnya, ambang batas di balik sebuah verdict, nilai sinyal mentah, dan rincian skor tetap berada di sisi kami. Skor yang bisa direkayasa balik menjadi masukannya berhenti berguna sebagai pertahanan.
Setiap kegagalan memakai satu amplop:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}requestId ini bukan pengidentifikasi kunjungan. Dua nilai berbeda berbagi nama yang
sama: di dalam payload sesi, requestId adalah UUID kunjungan, sama dengan yang dikirim
webhook; di dalam amplop error, ia adalah pengidentifikasi jejak sepanjang 24 karakter
yang dibuat untuk setiap panggilan HTTP. Pengidentifikasi jejak itu juga kembali di
header X-Request-Id pada setiap respons, berhasil maupun tidak. Sertakan nilainya saat
Anda menghubungi dukungan — dari situlah kami menemukan panggilan Anda yang persis.
| HTTP | code | Arti |
|---|---|---|
| 400 | invalid_request | Sebuah parameter hilang atau salah bentuk |
| 401 | unauthorized | Kuncinya tidak ada, tidak valid, dicabut, atau kedaluwarsa |
| 402 | upgrade_required | Paket Anda tidak mencakup akses API |
| 404 | not_found | Tidak ada yang cocok di dalam jendela retensi |
| 405 | method_not_allowed | Route-nya ada, tetapi tidak untuk metode itu |
| 429 | rate_limited | Permintaan per detik, atau kuota harian, terlampaui |
| 500 | internal | Ada yang gagal di sisi kami |
| 503 | unavailable | Sebuah penyimpanan pendukung sementara tidak terjangkau |
Pemeriksaan berjalan dalam urutan tetap — kunci, lalu paket, lalu batas — sehingga permintaan dengan kunci yang salah selalu melaporkan soal kunci lebih dulu, tidak pernah soal kuota.
Dua kasus 401 sengaja dibaca berbeda: missing Authorization: Bearer <secret key>
berarti header-nya tidak pernah tiba, sedangkan invalid or revoked API key berarti ia
tiba dan tidak cocok. 402 membawa Data API requires the Pro plan or higher.
Setiap respons terautentikasi membawa posisi Anda saat ini:
| Header | Arti |
|---|---|
X-RateLimit-Limit | Kuota harian Anda |
X-RateLimit-Remaining | Panggilan yang tersisa hari ini |
X-RateLimit-Reset | Waktu Unix saat reset — tengah malam UTC |
Retry-After | Detik yang harus ditunggu, hanya dikirim dengan 429 |
| Paket | Permintaan per detik | Permintaan per hari | Kedalaman riwayat |
|---|---|---|---|
| Free | Tanpa akses API | — | 7 hari |
| Pro | 10 | 10.000 | 30 hari |
| Business | 50 | 100.000 | 90 hari |
| Enterprise | 200 | Tanpa penghitungan | 365 hari |