Data API (ранее описанный здесь как Server API) позволяет вашему бэкенду читать данные идентификации, которые TRACIO уже собрал для вашего workspace: историю посетителя, отдельные сессии и счётчики активности за короткое окно.
Он дополняет Webhooks, а не заменяет их:
| Webhooks | Data API | |
|---|---|---|
| Направление | TRACIO отправляет на ваш эндпоинт | Ваш бэкенд запрашивает сам, когда нужно |
| Момент | В момент каждой идентификации | В любое время, в пределах вашего окна хранения |
| Лучше для | Реакции на событие | Поиска данных в момент решения, дозагрузок, разбора инцидентов |
Обе поверхности доступны с тарифа Pro и выше.
https://api.tracio.ai/v1Это отдельный хост — не тот, что у браузерного эндпоинта (edge.tracio.ai), и не
тот, что у дашборда (app.tracio.ai). Все три разные: браузер общается с edge вашим
публичным ключом, ваш бэкенд общается с Data API вашим секретным ключом.
Каждый запрос несёт ваш секретный ключ как bearer-токен:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Data API работает только в режиме server-to-server. Заголовки CORS намеренно не возвращаются, поэтому браузер не может его вызвать — именно это удерживает ваш секретный ключ вне клиентского кода. Никогда не отдавайте секретный ключ в браузер.
Создайте его в дашборде, в разделе API Keys, выбрав тип secret.
tracio_sk_ плюс 43 символа, всего 53. Дашборд
показывает его по первым нескольким символам, чтобы вы могли отличать ключи
друг от друга.Ротация выпускает новый ключ и оставляет старый рабочим ещё 7 дней, так что вы можете выкатить замену без простоя. Задеплойте новый ключ, убедитесь, что трафик перешёл на него, и дайте старому истечь. Публичные ключи не ротируются — они не секреты и по замыслу видны в исходном коде вашей страницы.
Все маршруты — GET. Операций записи в Data API нет: он читает данные, а ваша
конфигурация живёт в дашборде.
| Метод | Путь | Возвращает |
|---|---|---|
GET | /v1/visitors/{visitorId} | Агрегированную историю одного посетителя плюс его последнюю сессию |
GET | /v1/visitors/{visitorId}/sessions | Постраничный список сессий этого посетителя |
GET | /v1/visitors/{visitorId}/sessions/latest | Одну самую свежую сессию |
GET | /v1/visitors/{visitorId}/velocity | Счётчики активности за короткое окно |
GET | /v1/sessions/{requestId} | Одну сессию по идентификатору её запроса |
GET | /.well-known/webhook-keys | Публичные ключи для подписи платформы у вебхуков (без аутентификации) |
Завершающий слеш принимается и игнорируется. Неизвестный путь или неверный метод возвращают тот же JSON-конверт ошибки, что и всё остальное, — никогда не HTML- и не текстовую страницу.
Любое чтение ограничено временным окном, которым управляют два необязательных параметра запроса:
| Параметр | Принимает |
|---|---|
from | YYYY-MM-DD или полный таймстамп RFC 3339 |
to | YYYY-MM-DD или полный таймстамп RFC 3339 |
to включает весь этот день целиком.400 invalid_request и сообщением
time must be YYYY-MM-DD or RFC3339.meta, поэтому
проверяйте meta.from и meta.to, а не считайте, что ваш запрос выполнен буквально.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Ответ несёт агрегированную историю и включает в себя последнюю сессию, поэтому в типичном случае хватает одного запроса, а не двух:
{ "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" }}| Поле | Значение |
|---|---|
visits, incognitoVisits | Всего визитов в окне и сколько из них были в приватном окне |
uniqueIps, uniqueCountries | Различные адреса и страны, встреченные в окне |
browsers, os, devices | Различные окружения, в которых появлялся этот посетитель |
risk.maxRiskScore | Наивысшая оценка риска, зафиксированная в окне, 0..100 |
risk.lastDecision | Решение, зафиксированное для самого свежего визита |
risk.avgBotScore, risk.botSessions | Средняя оценка бота и число ботовых сессий — Business и выше |
network.*Seen | Встречались ли когда-либо у этого посетителя VPN, прокси, выходной узел Tor или адрес ЦОД |
network.proxyDetectedSeen | Выходил ли хотя бы один визит в окне через прокси или VPN перед браузером — см. network.proxyDetected в разделе «Факты об устройстве» |
network.lastIsp | Самый свежий ISP — Business и выше |
network.lastRealIp | Самый свежий адрес, наблюдавшийся за прокси или VPN, — Business и выше; отсутствует, когда такого адреса не наблюдали |
lastSession | Полный объект сессии для самого свежего визита |
meta | Тариф, его срок хранения в днях и фактически применённое окно |
Посетитель, по которому нет данных внутри окна хранения, отдаёт 404 not_found с
сообщением visitor not found in the retention window — это не ошибка вашей
интеграции, а признак того, что посетитель новый или уже вышел за срок хранения.
Сессия несёт два вердикта, и они отвечают на разные вопросы — был ли клиент автоматизирован и к какому общему выводу пришёл движок риска:
| Поле | Значения |
|---|---|
bot.result | human, bot, uncertain |
bot.type | Присутствует, когда bot.result равно bot: либо конкретный инструмент (playwright, puppeteer, selenium, jsdom, claude_computer_use…), либо семейство, когда инструмент не назван, — automation, headless, antidetect, extension, privacy_browser, other |
decision.action | real, fake, suspicious |
bot.score и decision.riskScore оба идут в шкале 0..100. На Business и выше
guidance превращает их в рекомендации по каждому сценарию на лестнице
allow → challenge → review → deny — что означает каждая ступень, см. в разделе
Guidance.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| Параметр | По умолчанию | Примечания |
|---|---|---|
limit | 50 | Ограничен сверху 500; большее значение подрезается, а не отклоняется |
from, to | Срок хранения тарифа | Общее временное окно, описанное выше |
cursor | — | Непрозрачный курсор пагинации с предыдущей страницы |
botResult | — | Оставить только сессии с этим вердиктом бота |
minRiskScore | — | Оставить только сессии с оценкой риска не ниже этой, 0..100 |
Сессии возвращаются начиная с самых свежих:
{ "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" }}Постраничная выборка построена на курсорах. Параметров page или offset нет:
передайте полученный nextCursor обратно как cursor и продолжайте, пока hasMore
равно 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}Считайте курсор непрозрачным — его содержимое является деталью реализации и может
измениться. Отредактированный курсор отклоняется с 400 invalid_request и сообщением
malformed cursor.
Помимо браузера и ОС, взятых из User-Agent, сессия несёт то, что браузер посетителя сообщает о машине, очищенное на нашей стороне. Каждое поле отсутствует, когда визит таких данных не принёс, поэтому считайте любое из них необязательным.
| Поле | Значение |
|---|---|
gpu | Модель видеоадаптера, как её сообщает браузер (WebGL), нормализованная до читаемого имени — Intel Iris Xe Graphics, Apple M1 Pro, Qualcomm Adreno 830; Software renderer означает, что настоящего GPU нет (виртуальная машина или headless-окружение); Safari сообщает Apple GPU |
network.proxyDetected | HTTP-трафик визита и его сырые сетевые пути выходят через разные сети — перед браузером стоит прокси или VPN; два адреса одного и того же провайдера (NAT оператора, второй выход того же VPN) не считаются |
network.realIp.address, .country, .isp | Публичный адрес, наблюдаемый на сыром сетевом пути, то есть адрес за прокси или VPN, вместе с его страной и ISP — Business и выше; отсутствует, когда такого адреса не наблюдали (country и isp отсутствуют, когда их не удалось определить) |
deviceInfo.deviceId, .crossBrowser, .confidence, .linkedBrowsers | Присутствует, когда идентичность устройства удалось определить: стабильный идентификатор физического устройства, общий для браузеров на нём, пришёл ли этот визит через другой браузер, чем прежде, уверенность этого совпадения и сколько различных посетителей (браузеров) делят устройство — больше единицы означает одну машину под несколькими браузерными идентичностями — Business и выше |
osEnvironment | Окружение рабочего стола, измеренное на машине с Linux (Mint 22+, Ubuntu, GNOME, KDE) — Business и выше; отсутствует, когда не определено |
spoofing | Что визит заявил против того, что измерили независимые проверки (claimed, real, spoofedAxes из os, gpu, screen, network, browser; anonymousBrowser с названиями продуктов) — Business и выше; присутствует только тогда, когда подмена обнаружена |
screen.width, .height, .colorDepth, .pixelRatio | Разрешение экрана, глубина цвета и device pixel ratio, как их сообщает браузер — Business и выше |
locale.languages, locale.timezone | Собственные предпочитаемые языки браузера и его таймзона — в отличие от geo.timezone, которая выводится из IP-адреса; расхождение между ними — частый признак подменённого местоположения — Business и выше |
clientHints.architecture, .bitness, .model, .deviceName, .platformVersion | User-Agent Client Hints: архитектура и разрядность процессора, код модели устройства (Android, например SM-A556B) вместе с её маркетинговым именем из списка устройств Google Play (deviceName, например Samsung Galaxy A55 5G) и точная версия платформы; только браузеры на Chromium — Business и выше |
environment.virtualMachine, environment.hypervisor | Присутствует только тогда, когда видеоадаптер сам представился виртуальным; hypervisor — закрытый словарь (vmware, virtualbox, parallels, qemu, hyperv, bochs, intel-gvt, vgpu). Отсутствие блока означает, что таких свидетельств нет — Business и выше |
extensions перечисляет расширения браузера, обнаруженные во время визита, — Business и выше. Каждый элемент — объект:
| Поле | Значение |
|---|---|
slug | Стабильный машинный идентификатор расширения, то же значение, которое доставляет вебхук |
name | Читаемое человеком название |
category | Укрупнённый класс — adblock, privacy, automation, wallet, vpn, devtools, other и так далее |
risky | true для расширений, связанных с автоматизацией, подменой или кражей учётных данных |
storeUrl | Ссылка на карточку расширения в магазине, когда она известна |
Находка сообщается только после того, как прошла наши проверки доверия, — окружение, которое отвечает «установлено» на любую пробу, или партия длиннее двенадцати имён отбраковываются как ненадёжные. Поэтому пустой или отсутствующий список означает «ничего, что мы смогли подтвердить», а не «расширений не установлено». Читайте его как свидетельство, а не как инвентарный список.
Самая свежая:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"Возвращается голый объект сессии — не массив и не обёрнутый в конверт. Посетитель без
сессий в окне отдаёт 404 not_found с сообщением
no sessions for this visitor in the retention window.
Либо по requestId — идентификатору, который встречается и в payload вебхука:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId здесь необязателен, но если вы его знаете, передача заметно ускоряет
поиск.
Velocity отвечает на вопрос «сколько этот посетитель успел наделать за последнее время» — именно так выглядят перебор учётных данных, тестирование карт и массовые регистрации.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"window принимает 1h, 24h или 7d, по умолчанию — 24h. Любое другое значение
отклоняется с 400 invalid_request и сообщением
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 считает различные значения linkedId, которые вы отправили для
этого устройства, — см. Связывание аккаунтов. botEvents
доступно на Business и выше.
Отсутствующее поле означает «нет данных», а не ноль. Поля без значения опускаются
целиком, а не отправляются как 0, "" или null: у совсем нового посетителя нет
matchConfidence, у чистого визита нет antidetectScore и suspectScore.
Единственное намеренное исключение — bot.score: оно присутствует всегда, даже когда
равно нулю. Читайте поля защитно.
Payload зависит от вашего тарифа. Любой тариф с доступом к API получает базовую сессию — идентификаторы, таймстамп, URL, IP, user agent, браузер, ОС, устройство, гео, сеть, бот, идентификация и решение. Pro добавляет identification.matchType, identification.matchConfidence, bot.type, bot.antidetectScore, gpu и network.proxyDetected. Business и Enterprise добавляют extensions, geo.isp, network.asn, network.realIp, decision.suspectScore, identification.driftScore, reasons, behavior, guidance, deviceInfo, spoofing, osEnvironment, screen, locale, clientHints и environment. Поля уровня персоны (personId, reputation, linkedAccountsCount, linkedVisitorsCount) зарезервированы за Business и Enterprise и появятся, когда будет включён слой персон, — сегодня он работает в режиме наблюдения, и эти поля не отдаются. Отсутствие поля уровня Business на тарифе Pro — не ошибка.
Внутренности уровня сигналов не возвращаются никогда, ни на одном тарифе: имена отдельных сигналов, их веса, пороги за вердиктом, сырые значения сигналов и разложение оценок остаются на нашей стороне. Оценка, которую можно обратной разработкой свести к её входным данным, перестаёт быть защитой.
Любой сбой использует один конверт:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}Этот requestId — не идентификатор визита. Имя делят два разных значения: внутри
payload сессии requestId — это UUID визита, тот же самый, что доставляет вебхук; в
конверте ошибки это 24-символьный идентификатор трассировки, выпускаемый на каждый
HTTP-вызов. Идентификатор трассировки возвращается ещё и в заголовке X-Request-Id
на каждом ответе, успешном или нет. Приложите его при обращении в поддержку — именно
по нему мы находим ваш конкретный вызов.
| HTTP | code | Значение |
|---|---|---|
| 400 | invalid_request | Параметр отсутствует или неверно оформлен |
| 401 | unauthorized | Ключ отсутствует, недействителен, отозван или истёк |
| 402 | upgrade_required | Ваш тариф не включает доступ к API |
| 404 | not_found | Внутри окна хранения ничего не найдено |
| 405 | method_not_allowed | Маршрут существует, но не для этого метода |
| 429 | rate_limited | Превышены запросы в секунду или дневная квота |
| 500 | internal | Что-то сломалось на нашей стороне |
| 503 | unavailable | Хранилище за API временно недоступно |
Проверки идут в фиксированном порядке — ключ, затем тариф, затем лимиты, — поэтому запрос с плохим ключом всегда сообщает сначала о ключе, а не о проблеме с квотой.
Два случая 401 намеренно читаются по-разному: missing Authorization: Bearer <secret key>
означает, что заголовок вообще не пришёл, а invalid or revoked API key — что он
пришёл и не совпал. 402 несёт Data API requires the Pro plan or higher.
Каждый аутентифицированный ответ несёт ваше текущее положение:
| Заголовок | Значение |
|---|---|
X-RateLimit-Limit | Ваша дневная квота |
X-RateLimit-Remaining | Сколько вызовов осталось сегодня |
X-RateLimit-Reset | Unix-время сброса — полночь UTC |
Retry-After | Сколько секунд ждать, отправляется с 429 |
| Тариф | Запросов в секунду | Запросов в сутки | Глубина истории |
|---|---|---|---|
| Free | Доступа к API нет | — | 7 дней |
| Pro | 10 | 10 000 | 30 дней |
| Business | 50 | 100 000 | 90 дней |
| Enterprise | 200 | Без ограничений | 365 дней |