Data API (documentat aici anterior drept Server API) permite backend-ului dumneavoastră să citească datele de identificare pe care TRACIO le-a colectat deja pentru workspace-ul dumneavoastră: istoricul unui vizitator, sesiunile individuale și contoarele de velocity pe ferestre scurte.
Completează Webhooks, fără să le înlocuiască:
| Webhooks | Data API | |
|---|---|---|
| Direcție | TRACIO trimite către endpoint-ul dumneavoastră | Backend-ul dumneavoastră interoghează la cerere |
| Moment | Pe măsură ce se produce fiecare identificare | Oricând, în cadrul ferestrei dumneavoastră de retenție |
| Potrivit pentru | Reacția la un eveniment | Consultarea datelor în timpul unei decizii, backfill-uri, investigații |
Ambele suprafețe sunt disponibile începând cu planul Pro.
https://api.tracio.ai/v1Acesta este un host diferit de endpoint-ul din browser (edge.tracio.ai) și de
dashboard (app.tracio.ai). Toate trei sunt separate: browserul vorbește cu edge-ul
folosind cheia dumneavoastră publică, iar backend-ul dumneavoastră vorbește cu
Data API folosind cheia secretă.
Fiecare cerere poartă cheia dumneavoastră secretă ca bearer token:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API este exclusiv server-to-server. Antetele CORS nu sunt returnate în mod deliberat, așa că un browser nu o poate apela — tocmai asta ține cheia dumneavoastră secretă în afara codului de pe client. Nu trimiteți niciodată cheia secretă în browser.
Creați-o în dashboard, la API Keys, alegând tipul secret.
tracio_sk_ urmat de 43 de caractere, 53 în total.
Dashboard-ul o listează după primele câteva caractere, ca să puteți deosebi cheile
între ele.Rotirea emite o cheie nouă și o menține pe cea veche funcțională timp de 7 zile, ca să o puteți desfășura fără întreruperi. Desfășurați cheia nouă, confirmați că traficul s-a mutat și lăsați-o pe cea veche să expire. Cheile publice nu se pot roti — nu sunt secrete și sunt vizibile prin design în sursa paginii dumneavoastră.
Fiecare rută este un GET. În Data API nu există operațiuni de scriere: ea citește
date, iar configurația dumneavoastră trăiește în dashboard.
| Metodă | Cale | Returnează |
|---|---|---|
GET | /v1/visitors/{visitorId} | Istoric agregat pentru un vizitator, plus ultima lui sesiune |
GET | /v1/visitors/{visitorId}/sessions | Listă paginată a sesiunilor acelui vizitator |
GET | /v1/visitors/{visitorId}/sessions/latest | Singura cea mai recentă sesiune |
GET | /v1/visitors/{visitorId}/velocity | Contoare de activitate pe o fereastră scurtă |
GET | /v1/sessions/{requestId} | O sesiune, după identificatorul cererii ei |
GET | /.well-known/webhook-keys | Chei publice pentru semnătura de platformă a webhook-urilor (fără autentificare) |
O bară finală este acceptată și ignorată. O cale necunoscută sau o metodă greșită returnează același plic JSON de eroare ca tot restul, niciodată o pagină HTML sau de text simplu.
Fiecare citire este delimitată de o fereastră temporală, controlată prin doi parametri de query opționali:
| Parametru | Acceptă |
|---|---|
from | YYYY-MM-DD sau un timestamp RFC 3339 complet |
to | YYYY-MM-DD sau un timestamp RFC 3339 complet |
to include întreaga zi respectivă.400 invalid_request și mesajul
time must be YYYY-MM-DD or RFC3339.meta, așa că verificați meta.from și meta.to în loc să
presupuneți că cererea dumneavoastră a fost onorată întocmai.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Răspunsul poartă istoricul agregat și include ultima sesiune, așa că în cazul obișnuit este nevoie de o singură cerere, nu de două:
{ "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" }}| Câmp | Semnificație |
|---|---|
visits, incognitoVisits | Vizite totale în fereastră și câte dintre ele au fost într-o fereastră privată |
uniqueIps, uniqueCountries | Adrese și țări distincte văzute în fereastră |
browsers, os, devices | Mediile distincte în care a apărut acest vizitator |
risk.maxRiskScore | Cel mai mare scor de risc înregistrat în fereastră, 0..100 |
risk.lastDecision | Decizia înregistrată pentru cea mai recentă vizită |
risk.avgBotScore, risk.botSessions | Media scorului de bot și numărul sesiunilor de bot — Business și peste |
network.*Seen | Dacă pentru acest vizitator au fost văzute vreodată VPN, proxy, nod de ieșire Tor sau adresă de datacenter |
network.proxyDetectedSeen | Dacă cel puțin o vizită din fereastră a ieșit printr-un proxy sau un VPN aflat în fața browserului — vedeți network.proxyDetected în secțiunea „Fapte despre dispozitiv” |
network.lastIsp | Cel mai recent ISP — Business și peste |
network.lastRealIp | Cea mai recentă adresă observată în spatele unui proxy sau VPN — Business și peste; absentă când nu a fost observată niciuna |
lastSession | Obiectul sesiune complet pentru cea mai recentă vizită |
meta | Planul, retenția lui în zile și fereastra aplicată efectiv |
Un vizitator fără date în fereastra de retenție returnează 404 not_found cu mesajul
visitor not found in the retention window — nu este o eroare în integrarea
dumneavoastră, ci înseamnă că vizitatorul este nou sau a ieșit din fereastră odată cu
trecerea timpului.
Sesiunea poartă două verdicte, iar ele răspund la întrebări diferite — dacă clientul a fost automatizat și ce a concluzionat în ansamblu motorul de risc:
| Câmp | Valori |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Prezent când bot.result este bot: fie un instrument anume (playwright, puppeteer, selenium, jsdom, claude_computer_use…), fie o familie când instrumentul nu este numit — automation, headless, antidetect, extension, privacy_browser, other |
decision.action | real, fake, suspicious |
bot.score și decision.riskScore merg amândouă pe 0..100. Pe Business și peste,
guidance le transformă în recomandări per scenariu, pe scara
allow → challenge → review → deny — vedeți
Guidance pentru ce înseamnă fiecare
treaptă.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Parametru | Implicit | Note |
|---|---|---|
limit | 50 | Plafonat la 500; o valoare mai mare este redusă, nu respinsă |
from, to | Retenția planului | Fereastra temporală comună descrisă mai sus |
cursor | — | Cursor opac de paginare, din pagina anterioară |
botResult | — | Păstrează doar sesiunile cu acest verdict de bot |
minRiskScore | — | Păstrează doar sesiunile cu scor de risc egal sau mai mare, 0..100 |
Sesiunile vin cu cele mai noi primele:
{ "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" }}Paginarea se face pe bază de cursor. Nu există parametru page sau offset: trimiteți
înapoi drept cursor valoarea nextCursor primită și continuați cât timp hasMore
este true.
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}Tratați cursorul ca opac — conținutul lui este un detaliu de implementare și se poate
schimba. Un cursor care a fost modificat este respins cu 400 invalid_request și
mesajul malformed cursor.
Pe lângă browserul și sistemul de operare luate din User-Agent, o sesiune poartă ceea ce browserul vizitatorului raportează despre mașină, curățat de partea noastră. Fiecare câmp lipsește când vizita nu a purtat astfel de date, așa că tratați-l pe fiecare drept opțional.
| Câmp | Semnificație |
|---|---|
gpu | Modelul plăcii video așa cum îl raportează browserul (WebGL), normalizat la un nume lizibil — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer înseamnă că nu există un GPU real (o mașină virtuală sau un mediu headless); Safari raportează Apple GPU |
network.proxyDetected | Traficul HTTP al vizitei și traseele ei de rețea brute ies prin rețele diferite — un proxy sau un VPN în fața browserului; două adrese ale aceluiași furnizor (NAT de operator, o a doua ieșire a aceluiași VPN) nu se pun la socoteală |
network.realIp.address, .country, .isp | Adresa publică observată pe traseul de rețea brut, adică adresa din spatele proxy-ului sau al VPN-ului, împreună cu țara și ISP-ul ei — Business și peste; absentă când nu a fost observată nicio astfel de adresă (country și isp lipsesc când nu au putut fi determinate) |
deviceInfo.deviceId, .crossBrowser, .confidence, .linkedBrowsers | Prezent când identitatea dispozitivului a putut fi determinată: un identificator stabil al dispozitivului fizic, comun browserelor de pe el, dacă această vizită a venit printr-un browser diferit față de înainte, încrederea în această potrivire și câți vizitatori distincți (browsere) împart dispozitivul — peste unu înseamnă o singură mașină sub mai multe identități de browser — Business și peste |
osEnvironment | Mediul desktop măsurat pe o mașină Linux (Mint 22+, Ubuntu, GNOME, KDE) — Business și peste; absent când nu este determinat |
spoofing | Ce a pretins vizita față de ce au măsurat verificări independente (claimed, real, spoofedAxes dintre os, gpu, screen, network, browser; anonymousBrowser cu numele produselor) — Business și peste; prezent doar când a fost detectată o falsificare |
screen.width, .height, .colorDepth, .pixelRatio | Rezoluția ecranului, adâncimea de culoare și device pixel ratio așa cum le raportează browserul — Business și peste |
locale.languages, locale.timezone | Limbile preferate și fusul orar ale browserului însuși — spre deosebire de geo.timezone, derivat din adresa IP; o nepotrivire între cele două este un semn frecvent al unei locații falsificate — Business și peste |
clientHints.architecture, .bitness, .model, .deviceName, .platformVersion | User-Agent Client Hints: arhitectura și bitness-ul procesorului, codul de model al dispozitivului (Android, de ex. SM-A556B) împreună cu numele său comercial din lista de dispozitive Google Play (deviceName, de ex. Samsung Galaxy A55 5G) și versiunea exactă a platformei; doar browsere bazate pe Chromium — Business și peste |
environment.virtualMachine, environment.hypervisor | Prezent doar când placa video s-a declarat ea însăși virtuală; hypervisor este un dicționar închis (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Un bloc lipsă înseamnă că nu există un astfel de indiciu — Business și peste |
extensions enumeră extensiile de browser detectate în timpul vizitei — Business și peste. Fiecare intrare este un obiect:
| Câmp | Semnificație |
|---|---|
slug | Identificator stabil, de mașină, al extensiei — aceeași valoare pe care o livrează webhook-ul |
name | Nume lizibil pentru om |
category | Clasă generală — adblock, privacy, automation, wallet, vpn, devtools, other și așa mai departe |
risky | true pentru extensiile asociate cu automatizarea, falsificarea sau furtul de credențiale |
storeUrl | Link către pagina extensiei din magazin, când este cunoscut |
O constatare este raportată abia după ce a trecut de verificările noastre de încredere — un mediu care răspunde „instalat” la fiecare sondare sau un lot mai lung de douăsprezece nume este eliminat ca nesigur. Prin urmare, o listă goală sau lipsă înseamnă „nimic ce am putut confirma”, nu „nu există extensii instalate”. Citiți-o ca pe o dovadă, nu ca pe un inventar.
Cea mai recentă:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Aceasta returnează un obiect sesiune simplu — nu un array și nu împachetat într-un plic.
Un vizitator fără sesiuni în fereastră returnează 404 not_found cu
no sessions for this visitor in the retention window.
Sau după requestId, identificatorul care apare și în payload-ul webhook-ului:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId este opțional aici, dar transmiterea lui, atunci când îl cunoașteți, face
căutarea vizibil mai rapidă.
Velocity răspunde la întrebarea „cât de mult a făcut acest vizitator în ultima vreme” — forma pe care o au credential stuffing-ul, testarea de carduri și înregistrările în masă.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window acceptă 1h, 24h sau 7d și are implicit 24h. Orice altă valoare este
respinsă cu 400 invalid_request și 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 numără valorile linkedId distincte pe care le-ați trimis pentru acest
dispozitiv — vedeți Asocierea conturilor. botEvents este
Business și peste.
Un câmp lipsă înseamnă „fără date”, niciodată zero. Câmpurile fără valoare sunt
omise complet, în loc să fie trimise ca 0, "" sau null: un vizitator nou-nouț nu
are matchConfidence, o vizită curată nu are antidetectScore sau suspectScore.
Singura excepție deliberată este bot.score, care este întotdeauna prezent, chiar și
când este zero. Citiți câmpurile defensiv.
Payload-ul depinde de planul dumneavoastră. Fiecare plan cu acces la API primește sesiunea de bază — identificatori, timestamp, URL, IP, user agent, browser, sistem de operare, dispozitiv, geo, rețea, bot, identificare și decizie. Pro adaugă identification.matchType, identification.matchConfidence, bot.type, bot.antidetectScore, gpu și network.proxyDetected. Business și Enterprise adaugă extensions, geo.isp, network.asn, network.realIp, decision.suspectScore, identification.driftScore, reasons, behavior, guidance, deviceInfo, spoofing, osEnvironment, screen, locale, clientHints și environment. Câmpurile la nivel de persoană (personId, reputation, linkedAccountsCount, linkedVisitorsCount) sunt rezervate pentru Business și Enterprise și vor apărea odată ce stratul de persoane va fi activat — astăzi acesta rulează în mod de observare, iar aceste câmpuri nu sunt livrate. Absența unui câmp Business pe un plan Pro nu este o eroare.
Detaliile interne la nivel de semnal nu sunt returnate niciodată, la niciun plan: numele semnalelor individuale, ponderile lor, pragurile din spatele unui verdict, valorile brute ale semnalelor și descompunerea scorurilor rămân de partea noastră. Un scor care poate fi supus ingineriei inverse până la intrările sale încetează să mai fie util ca apărare.
Fiecare eșec folosește un singur plic:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}Acest requestId nu este identificatorul vizitei. Două valori diferite poartă
același nume: în payload-ul unei sesiuni, requestId este UUID-ul vizitei, același pe
care îl livrează webhook-ul; în plicul unei erori, este un identificator de urmărire de
24 de caractere, generat pentru fiecare apel HTTP. Identificatorul de urmărire se
întoarce și în antetul X-Request-Id la fiecare răspuns, reușit sau nu. Includeți-l
când contactați suportul — așa vă găsim apelul exact.
| HTTP | code | Semnificație |
|---|---|---|
| 400 | invalid_request | Un parametru lipsește sau este malformat |
| 401 | unauthorized | Cheia lipsește, este invalidă, revocată sau expirată |
| 402 | upgrade_required | Planul dumneavoastră nu include acces la API |
| 404 | not_found | Nimic nu s-a potrivit în fereastra de retenție |
| 405 | method_not_allowed | Ruta există, dar nu pentru acea metodă |
| 429 | rate_limited | S-au depășit cererile pe secundă sau cota zilnică |
| 500 | internal | Ceva a eșuat de partea noastră |
| 503 | unavailable | Un depozit de date suport este temporar inaccesibil |
Verificările se fac într-o ordine fixă — cheia, apoi planul, apoi limitele — așa că o cerere cu o cheie greșită raportează întotdeauna mai întâi cheia, niciodată o problemă de cotă.
Două cazuri de 401 se citesc diferit în mod intenționat:
missing Authorization: Bearer <secret key> înseamnă că antetul nu a sosit niciodată,
în timp ce invalid or revoked API key înseamnă că a sosit și nu s-a potrivit. 402
poartă Data API requires the Pro plan or higher.
Fiecare răspuns autentificat poartă situația dumneavoastră curentă:
| Antet | Semnificație |
|---|---|
X-RateLimit-Limit | Cota dumneavoastră zilnică |
X-RateLimit-Remaining | Apeluri rămase astăzi |
X-RateLimit-Reset | Timpul Unix al resetării — miezul nopții UTC |
Retry-After | Secunde de așteptat, trimis doar cu un 429 |
| Plan | Cereri pe secundă | Cereri pe zi | Adâncimea istoricului |
|---|---|---|---|
| Free | Fără acces la API | — | 7 zile |
| Pro | 10 | 10.000 | 30 de zile |
| Business | 50 | 100.000 | 90 de zile |
| Enterprise | 200 | Nelimitat | 365 de zile |