RESTful endpoints for device identification, visitor history, event retrieval, and health monitoring
The tracio.ai Server API provides RESTful endpoints for identifying visitors, retrieving events, querying visitor history, and monitoring system health. All API requests are authenticated using an API secret key passed in the Authorization header. Responses are returned in JSON format with consistent error handling across all endpoints.
The API follows REST conventions: resources are addressed by URL path, HTTP methods indicate the operation, and standard status codes communicate success or failure. All timestamps use ISO 8601 format in UTC. Request and response bodies use JSON encoding with Content-Type: application/json.
The base URL depends on your chosen region. All API paths are relative to this base URL.
https://api.tracio.ai/api/v1All Server API requests require authentication using your API secret. Include the secret in the Authorization header using the Bearer scheme. The API secret is different from the public API key used in the client SDK. The API key identifies your application on the client side, while the API secret authenticates server-to-server requests and must never be exposed to browsers.
Authorization: Bearer YOUR_API_SECRETYou can generate and manage API secrets from your dashboard. Each workspace supports multiple active secrets for key rotation without downtime. When rotating keys, create the new secret first, update your application, then revoke the old secret.
| Method | Endpoint | Description |
|---|---|---|
POST | /api/identify | Submit browser signals for identification (called by the JS SDK) |
GET | /api/v1/events/:requestId | Retrieve a single identification event by request ID |
GET | /api/v1/visitors/:visitorId | Get visit history for a specific visitor |
DELETE | /api/v1/visitors/:visitorId | Delete all data for a specific visitor (GDPR compliance) |
GET | /api/health | Server health check (no authentication required) |
This endpoint receives encrypted browser signals from the JavaScript SDK, processes them through the identification engine, and returns a visitor ID along with all requested products (bot detection, smart signals, IP intelligence). In normal usage, the client SDK calls this endpoint automatically when you invoke tracio.get(). You do not need to call this endpoint directly unless you are building a custom integration.
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-API-Key | Yes | Your public API key |
X-Request-Id | No | Idempotency key for deduplication |
X-Forwarded-For | No | Original client IP when using a reverse proxy |
The request body contains the encrypted signal payload generated by the JavaScript SDK. The payload is AES-256-GCM encrypted using a session key derived from the API key. The server decrypts the payload, extracts individual signals, and runs them through the identification pipeline.
{ "payload": "aes256gcm:v1:base64_encrypted_signal_data...", "tag": { "userId": "user_12345", "action": "login" }, "linkedId": "session_abc", "extendedResult": true}| Parameter | Type | Required | Description |
|---|---|---|---|
payload | string | Yes | Encrypted signal data from the browser SDK |
tag | object | No | Custom metadata to attach to this event (max 16 keys, 256 chars per value) |
linkedId | string | No | External identifier for linking events to your system (max 256 chars) |
extendedResult | boolean | No | When true, returns smart signals and IP intelligence in the response |
The response contains the visitor identification result along with all products. The top-level products object contains one key per product, each with a data sub-object holding the results.
{ "products": { "identification": { "data": { "requestId": "1710432000_abc123def", "visitorId": "X7fh2Hg9LkMn3pQr", "visitorFound": true, "confidence": { "score": 0.995, "revision": "v3.0" }, "ip": "94.142.239.124", "ipLocation": { "accuracyRadius": 20, "latitude": 50.05, "longitude": 14.4, "timezone": "Europe/Prague", "city": { "name": "Prague" }, "country": { "code": "CZ", "name": "Czechia" }, "continent": { "code": "EU", "name": "Europe" }, "subdivisions": [ { "isoCode": "10", "name": "Hlavni mesto Praha" } ] }, "firstSeenAt": { "global": "2024-01-15T10:00:00Z", "subscription": "2024-01-15T10:00:00Z" }, "lastSeenAt": { "global": "2024-03-12T16:00:00Z", "subscription": "2024-03-12T16:00:00Z" }, "tag": { "action": "login" }, "linkedId": "user_12345" } }, "botd": { "data": { "bot": { "result": "notDetected", "probability": 0.0 } } }, "vpn": { "data": { "result": false, "confidence": "high", "originTimezone": "Europe/Prague", "methods": { "timezoneMismatch": false, "publicVPN": false, "osMismatch": false, "relay": false } } }, "proxy": { "data": { "result": false, "confidence": "high" } }, "tor": { "data": { "result": false } }, "tampering": { "data": { "result": false, "anomalyScore": 0.12 } }, "incognito": { "data": { "result": false } }, "virtualMachine": { "data": { "result": false } }, "suspectScore": { "data": { "result": 0 } }, "velocity": { "data": { "distinctIp": { "intervals": { "5m": 1, "1h": 1, "24h": 2 } }, "distinctCountry": { "intervals": { "5m": 1, "1h": 1, "24h": 1 } }, "events": { "intervals": { "5m": 1, "1h": 3, "24h": 12 } } } } }}Retrieve the full identification result for a specific event. This is the primary endpoint for server-side verification. After a client-side identification, send the requestId to your backend and use this endpoint to fetch the trusted server-side result. Never trust identification data sent directly from the browser.
| Parameter | Type | Description |
|---|---|---|
requestId | string | The unique event identifier returned from tracio.get() |
curl -X GET \ "https://api.tracio.ai/api/v1/events/1710432000_abc123def" \ -H "Authorization: Bearer YOUR_API_SECRET" \ -H "Accept: application/json"const response = await fetch( `https://api.tracio.ai/api/v1/events/${requestId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${process.env.TRACIO_API_SECRET}`, 'Accept': 'application/json', }, });if (!response.ok) { const error = await response.json(); throw new Error(`tracio.ai API error: ${error.error.code} - ${error.error.message}`);}const event = await response.json();const visitorId = event.products.identification.data.visitorId;const confidence = event.products.identification.data.confidence.score;const isBot = event.products.botd.data.bot.result !== 'notDetected';console.log(`Visitor: ${visitorId}, confidence: ${confidence}, bot: ${isBot}`);import requestsimport osAPI_SECRET = os.environ["TRACIO_API_SECRET"]BASE_URL = "https://api.tracio.ai/api/v1"def get_event(request_id: str) -> dict: response = requests.get( f"{BASE_URL}/events/{request_id}", headers={ "Authorization": f"Bearer {API_SECRET}", "Accept": "application/json", }, ) response.raise_for_status() return response.json()event = get_event("1710432000_abc123def")identification = event["products"]["identification"]["data"]bot = event["products"]["botd"]["data"]["bot"]print(f"Visitor: {identification['visitorId']}")print(f"Confidence: {identification['confidence']['score']}")print(f"Bot result: {bot['result']}")Retrieve the visit history for a specific visitor. Returns a paginated list of all identification events for the given visitor ID, ordered by timestamp descending (most recent first). Use this endpoint to review a visitor's activity over time, detect patterns, or build user profiles.
| Parameter | Type | Description |
|---|---|---|
visitorId | string | The stable visitor identifier |
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Number of results per page (1-100) |
before | string | - | ISO 8601 timestamp cursor. Returns events before this time. |
after | string | - | ISO 8601 timestamp cursor. Returns events after this time. |
linkedId | string | - | Filter events by linked ID |
curl -X GET \ "https://api.tracio.ai/api/v1/visitors/X7fh2Hg9LkMn3pQr?limit=10" \ -H "Authorization: Bearer YOUR_API_SECRET"{ "visitorId": "X7fh2Hg9LkMn3pQr", "visits": [ { "requestId": "1710432000_abc123def", "timestamp": "2024-03-12T16:00:00Z", "ip": "94.142.239.124", "ipLocation": { "city": { "name": "Prague" }, "country": { "code": "CZ" } }, "confidence": { "score": 0.995 }, "tag": { "action": "login" }, "linkedId": "user_12345", "bot": { "result": "notDetected" }, "incognito": false }, { "requestId": "1710345600_xyz789ghi", "timestamp": "2024-03-11T12:00:00Z", "ip": "94.142.239.124", "ipLocation": { "city": { "name": "Prague" }, "country": { "code": "CZ" } }, "confidence": { "score": 0.998 }, "tag": { "action": "pageview" }, "bot": { "result": "notDetected" }, "incognito": false } ], "paginationKey": "1710345600000", "totalVisits": 47}Permanently delete all stored data for a specific visitor. This endpoint supports GDPR right-to-erasure (Article 17) compliance. Once deleted, the visitor ID will be treated as a new visitor on subsequent identifications. This action is irreversible.
curl -X DELETE \ "https://api.tracio.ai/api/v1/visitors/X7fh2Hg9LkMn3pQr" \ -H "Authorization: Bearer YOUR_API_SECRET"# Response: 204 No ContentReturns the current health status of the server. This endpoint does not require authentication and is intended for load balancer health checks and monitoring systems. The response includes service version, uptime, and the service status.
curl https://api.tracio.ai/api/health{ "status": "healthy", "version": "1.0.0", "uptime": 86400}Each product in the response contains a data object with the detection results. Products are included when extendedResult: true is set in the client request, or always when querying via the Server API. The following tables document every field in each product.
The core identification product. Always present in the response. Contains the visitor ID, confidence score, IP geolocation, and visit history timestamps.
| Field | Type | Description |
|---|---|---|
requestId | string | Unique event identifier (format: timestamp_randomhex) |
visitorId | string | Stable visitor identifier (16-20 alphanumeric characters) |
visitorFound | boolean | Whether a matching visitor was found in the database. false for first-time visitors. |
confidence.score | number | Identification confidence (0.0-1.0). Typically 0.99+ for returning visitors. |
confidence.revision | string | Algorithm version used for this identification (e.g., "v3.0") |
ip | string | Client IP address (IPv4 or IPv6) |
ipLocation | object | Geolocation derived from IP (city, country, continent, coordinates, timezone, accuracy radius) |
firstSeenAt.global | string | ISO 8601 timestamp of the visitor's first ever identification |
firstSeenAt.subscription | string | ISO 8601 timestamp of the first identification under this API key |
lastSeenAt.global | string | ISO 8601 timestamp of the most recent identification |
lastSeenAt.subscription | string | ISO 8601 timestamp of the most recent identification under this API key |
tag | object | Custom metadata attached by the client (echoed back verbatim) |
linkedId | string | Linked identifier attached by the client (echoed back verbatim) |
Bot detection results. Uses multiple independent detection methods to classify visitors as human, good bot, or bad bot.
| Field | Type | Description |
|---|---|---|
bot.result | string | "notDetected", "good", or "bad" |
bot.probability | number | Bot probability score (0.0-1.0) |
bot.type | string | Bot type when detected: "selenium", "puppeteer", "playwright", "headless", "phantomjs", "unknown" |
VPN detection using four independent methods: timezone mismatch, public VPN ASN matching, relay detection, and WebRTC TURN probing.
| Field | Type | Description |
|---|---|---|
result | boolean | Whether VPN usage was detected |
confidence | string | "high", "medium", or "low" |
originTimezone | string | Device timezone (IANA format, e.g., "Europe/Prague") |
methods.timezoneMismatch | boolean | IP timezone differs from device timezone |
methods.publicVPN | boolean | IP belongs to a known VPN provider ASN |
methods.osMismatch | boolean | OS signals inconsistent with User-Agent |
methods.relay | boolean | Anonymizing relay detected (iCloud Private Relay, Cloudflare WARP) |
Proxy detection for datacenter, residential, HTTP, and SOCKS proxies.
| Field | Type | Description |
|---|---|---|
result | boolean | Whether proxy usage was detected |
confidence | string | "high", "medium", or "low" |
| Field | Type | Description |
|---|---|---|
result | boolean | Whether the IP is a known Tor exit node |
Detects browser environment modifications including User-Agent spoofing, navigator property injection, and native function overrides.
| Field | Type | Description |
|---|---|---|
result | boolean | Whether browser tampering was detected |
anomalyScore | number | Anomaly score (0.0-1.0). Values above 0.5 indicate significant tampering. |
| Field | Type | Description |
|---|---|---|
result | boolean | Whether private/incognito browsing mode is active |
| Field | Type | Description |
|---|---|---|
result | boolean | Whether the visitor is running inside a virtual machine (VMware, VirtualBox, Parallels, QEMU, Hyper-V) |
Tracks visitor activity over rolling time windows. Use velocity data to detect account sharing, credential stuffing, or geographic impossibility.
| Field | Type | Description |
|---|---|---|
distinctIp.intervals | object | Unique IPs for this visitor across 5m, 1h, 24h |
distinctCountry.intervals | object | Unique countries across intervals |
events.intervals | object | Total identification events across intervals |
Aggregated risk score combining all smart signal outputs into a single integer for simplified decision-making.
| Field | Type | Description |
|---|---|---|
result | number | Aggregate risk score (0-100). 0 = clean, 100 = maximum risk. |
All errors follow a consistent format with an error object containing a machine-readable code and a human-readable message. Use the code field for programmatic error handling.
| HTTP Status | Error Code | Description | Resolution |
|---|---|---|---|
| 400 | bad_request | Malformed request body or invalid parameters | Check request body format and required fields |
| 401 | unauthorized | Invalid or missing API secret | Verify the Authorization header contains a valid secret |
| 403 | forbidden | API key does not have permission for this endpoint | Check API key permissions in your deployment config |
| 404 | event_not_found | No event found for the given requestId | Verify the requestId is correct and not expired |
| 404 | visitor_not_found | No visitor found for the given visitorId | Verify the visitorId is correct |
| 422 | payload_invalid | Signal payload could not be decrypted or parsed | Ensure the client SDK version is compatible |
| 429 | rate_limited | Rate limit exceeded | Wait for the duration specified in retryAfter |
| 500 | internal_error | Unexpected server error | Retry with exponential backoff. Contact support if persistent. |
| 503 | service_unavailable | Server is temporarily unavailable | Retry after a short delay. Check /api/health for details. |
// 401 Unauthorized{ "error": { "code": "unauthorized", "message": "Invalid or missing API secret" }}// 404 Not Found{ "error": { "code": "event_not_found", "message": "No event found for the given requestId" }}// 429 Too Many Requests{ "error": { "code": "rate_limited", "message": "Rate limit exceeded", "retryAfter": 60 }}// 500 Internal Server Error{ "error": { "code": "internal_error", "message": "An unexpected error occurred. Request ID: req_abc123" }}Rate limits are applied per API key on a sliding window basis. When you exceed the limit, the server returns a 429 status with a Retry-After header indicating how many seconds to wait before retrying. The response body also includes a retryAfter field in the error object.
| Plan | Requests/minute | Requests/day | Burst limit |
|---|---|---|---|
| Free | 60 | 2,500 | 10 req/sec |
| Pro | 600 | 100,000 | 50 req/sec |
| Enterprise | Custom | Custom | Custom |
For enterprise deployments, rate limits are configurable via environment variables. Set TRACIO_RATE_LIMIT_RPM and TRACIO_RATE_LIMIT_RPD to customize the limits for your deployment. Setting either to 0 disables that particular limit.
List endpoints use cursor-based pagination for efficient traversal of large result sets. The paginationKey returned in each response can be passed as the before parameter to fetch the next page. This approach avoids the performance pitfalls of offset-based pagination and guarantees consistent results even when new events arrive during pagination.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Maximum number of results per page (1-100) |
before | string | - | Cursor for fetching results before a given point (use paginationKey from the previous response) |
after | string | - | Cursor for fetching results after a given point |
// Fetch all visits for a visitor, page by pageasync function getAllVisits(visitorId: string): Promise<Visit[]> { const allVisits: Visit[] = []; let paginationKey: string | undefined; do { const url = new URL(`https://api.tracio.ai/api/v1/visitors/${visitorId}`); url.searchParams.set('limit', '100'); if (paginationKey) { url.searchParams.set('before', paginationKey); } const response = await fetch(url.toString(), { headers: { 'Authorization': `Bearer ${API_SECRET}` }, }); const data = await response.json(); allVisits.push(...data.visits); paginationKey = data.paginationKey; } while (paginationKey); return allVisits;}Official server SDKs wrap the REST API with typed methods, automatic retry with exponential backoff, and built-in error handling. Each SDK is fully typed and provides IDE autocompletion for all response fields.
| Language | Package | Install |
|---|---|---|
| Node.js | @tracio/server | npm install @tracio/server |
| Python | tracio-server-python | pip install tracio-server-python |
| Go | github.com/tracio/server-go | go get github.com/tracio/server-go |
| Ruby | tracio-server-ruby | gem install tracio-server-ruby |
| PHP | tracio/server-php | composer require tracio/server-php |
import { TracioServerClient } from '@tracio/server';const client = new TracioServerClient({ apiSecret: process.env.TRACIO_API_SECRET, baseUrl: 'https://api.tracio.ai',});// Get a specific eventconst event = await client.getEvent('1710432000_abc123def');console.log(event.products.identification.data.visitorId);// Get visitor historyconst history = await client.getVisitorHistory('X7fh2Hg9LkMn3pQr', { limit: 50,});console.log(`Total visits: ${history.totalVisits}`);// Delete visitor data (GDPR)await client.deleteVisitor('X7fh2Hg9LkMn3pQr');from tracio_server import TracioServerClientclient = TracioServerClient( api_secret=os.environ["TRACIO_API_SECRET"], base_url="https://api.tracio.ai",)# Get a specific eventevent = client.get_event("1710432000_abc123def")visitor_id = event.products.identification.data.visitor_idconfidence = event.products.identification.data.confidence.score# Get visitor historyhistory = client.get_visitor_history("X7fh2Hg9LkMn3pQr", limit=50)for visit in history.visits: print(f"{visit.timestamp}: {visit.ip} ({visit.ip_location.city.name})")# Delete visitor data (GDPR)client.delete_visitor("X7fh2Hg9LkMn3pQr")