API documentation
Trackless receives data through a simple HTTP JSON API. PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6 and Sylius have ready-made modules and plugins. Any platform (Shoptet, Shopify, a custom solution) can connect to the API - just send a batch of visit and order data once a day (or more often).
How we connect you and what you get
The connection path depends on your platform. Where we can measure directly on the server, the data is most accurate; elsewhere a small snippet helps. For analytics itself, you can often work without your own analytics cookies; the legal basis and any cookie-banner setup still depend on the specific deployment.
| Connection | Consent / cookie banner | Browser dependency | What Trackless measures |
|---|---|---|---|
| Module/plugin - PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6, Sylius | Typically not | Low - measured on the server, the customer's browser is not the main measurement layer | Visits and real revenue, margin, returns, ROAS |
| JavaScript - Shoptet | Assess per deployment | High, not 100% - the snippet runs in the browser; under aggressive blocking, some visits may be missed | Visits and orders (automatically) |
| JavaScript - hosted platforms | Assess per deployment | High, not 100% - the snippet runs in the browser | Visits and orders; Upgates and Eshop-rychle automatically, others once the conversion snippet is added |
| JavaScript - Webnode | Assess per deployment | High, not 100% - the snippet runs in the browser | Traffic only - the platform does not expose order data to the browser |
| Custom integration (API, Shopify and others) | Depends on the integration | Low, if you measure from the server (POST /ingest) | Depends on your integration |
The module measures directly on the store's server, so it does not depend on a browser snippet and does not store analytics cookies of its own. Where a platform does not allow our own module (hosted builders such as Shoptet), we use a small snippet - it does not store analytics cookies in the browser, but still depends on whether the browser or an extension blocks it. Assess the legal basis and cookie banner according to the specific implementation.
Trackless does not create a persistent browser identifier and uses no own analytics cookies. The daily visitor fingerprint changes; aggregation and order attribution may still use pseudonymous technical data and internal store data inside the attribution window.
Connecting a hosted platform? We have step-by-step platform guides.
Ready-made plugins and modules
shaim_trackless module - in PrestaShop go to Modules → Upload a module → .zip → configure and paste your api_key.Requirements: PrestaShop 1.6+ and PHP 7.1+.
A PrestaShop module by PSModuly.cz
api_key.Requirements: WordPress 6.2+, PHP 7.4+ (WooCommerce optional - 6.0+, 8.5+ for order source attribution).
api_key and confirm the DPA.Requirements: Joomla 5.4+ or 6.x, PHP 8.1+; VirtueMart 5 for order tracking. Measures traffic, orders and order items server-side, with no template changes.
api_key and confirm the DPA.Requirements: OpenCart 3.0.3+ and PHP 7.1+. No theme or core modifications (event hooks).
api_key and confirm the DPA.Requirements: OpenCart 4.0+ and PHP 8.0+. Do not rename the downloaded file - OpenCart 4 derives the extension identity from its name. No theme or core modifications.
api_key and confirm the DPA.Requirements: Shopware 6.4+, PHP 7.4+. Measures on the server including orders and line items.
app/code/Shaim/Trackless, then run bin/magento module:enable Shaim_Trackless and setup:upgrade → under Stores → Configuration → Trackless paste your api_key and confirm the DPA.Requirements: Magento 2.4+, PHP 7.4+. Measures on the server including orders, line items and margins (from the cost field).
composer require shaim/trackless-sylius-plugin, 2) register the bundle in config/bundles.php, 3) set your api_key in config/packages/trackless_sylius.yaml, 4) run bin/console trackless:set-salt and trackless:send (creates the tables).Requirements: Sylius 1.12+, PHP 8.0+. Measures on the server including orders and line items.
Tracking JS snippet for hosted platforms
Hosted platforms and sites where a server-side module/plugin cannot be uploaded connect with a tracking snippet in the template or through Google Tag Manager, depending on what the platform allows. The snippet measures traffic and, on supported platforms, orders too - pages, sources and channels, devices, countries - without its own analytics cookies; the final consent-banner assessment still depends on the whole site setup. No measurement identifier is stored in the browser and the visitor is recognized only by a daily-rotating server-side fingerprint.
<script async src="https://c.trackless.cz/t.js" data-token="VAS_MERICI_TOKEN"></script>
After adding a site in My sites, you will see either a ready-made tracking snippet or, depending on the platform, just the token for the guide's code; later you manage it in My account. Where to add the integration:
- Shoptet: in the administration open HTML kód (HTML code) under Vzhled a obsah → Editor (Appearance and content → Editor) and paste the snippet into the Záhlaví (header) section - see the step-by-step guide for Shoptet.
- Eshop-rychle: administration → E-shop → Měřící kódy → Přidat vlastní kód (Tracking codes → Add custom code), position Hlavička (header) - orders load automatically from the official Eshop-rychle dataLayer; see the step-by-step guide for Eshop-rychle.
- Upgates: administration → Doplňky / Vlastní konverzní kódy (Add-ons / Custom conversion codes), "all pages" section, the
<head>field - see the step-by-step guide for Upgates. - Webareal: administration → Nastavení → Nastavení webu → Základní nastavení (Basic settings), the "Kód počítadla přístupů" (visit-counter code) field - see the step-by-step guide for Webareal.
- Webnode: Settings → Website settings → HTML header (requires a paid package; traffic, plus orders on the Profi/Business plan via the conversion-tracking field) - see the Webnode guide.
- Shopify: add a custom pixel under Settings → Customer events → Custom pixels; it does not use the regular template
<script>- see the Shopify guide. - FastCentrik: the admin accepts only a Google Tag Manager ID; add the Trackless snippet in GTM as a custom HTML tag on all pages, preferably using the dynamic variant from the guide so the
data-tokenattribute is preserved. Add the conversion snippet as a second tag tied to the e-commerce data layer - see the FastCentrik guide. - Mioweb: put the tracking snippet into Nastavení → Web → Vlastní kódy for the page header and the conversion snippet into Nastavení → Prodej → Vlastní kódy - see the Mioweb guide.
- Any other site: paste the snippet anywhere into the template's
<head>.
An honest note: JS-based measurement can miss part of the traffic if the browser or an extension blocks external measurement scripts. The PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6 and Sylius server-side modules measure on the server, so they are more accurate. Margins and order line-item detail, however, are available only through modules that send order data or through the /ingest batch API (orders and revenue can also come from the JS order event).
Standard and custom events
Alongside standard events, you can send any custom event, such as lead_submitted, pdf_downloaded or video_started. The same universal call works for a lead, registration, download, playback or any other action. Use the order event after an order is completed.
window.trackless && trackless('event', {
type: 'lead_submitted',
id: 123,
value: 1500.00,
currency: 'CZK'
});
window.trackless && trackless('event', {
type: 'order',
id: 2026001234,
value: 1169.00,
value_tax_excl: 966.12,
value_tax_incl: 1169.00,
products_tax_excl: 900.00,
products_tax_incl: 1089.00,
shipping_tax_excl: 66.12,
shipping_tax_incl: 80.00,
currency: 'CZK'
});
The name is 1-40 characters, starts with a lowercase letter and continues with lowercase letters, digits or underscores only: ^[a-z][a-z0-9_]{0,39}$. An invalid name is stored as pageview. Standard types pageview, view_item, view_category, search, view_cart, add_to_cart, begin_checkout, order and engagement have built-in semantics. A custom type appears in the event report but does not automatically affect the funnel or revenue.
The /collect endpoint contract
POST https://trackless.cz/collect
The t.js snippet POSTs a small JSON to /collect (as text/plain to avoid the CORS preflight). The target is built into t.js; data-endpoint is only an optional override for testing or a custom endpoint. Fields: t = the public tracking token, u = the full page URL (including utm_*), r = referrer, l = browser language, e = optional event. In normal operation the response is always 204; if you exceed the limit it returns 429.
{ "v": 1, "t": "tlc_…", "u": "https://www.muj-eshop.cz/akce?utm_source=newsletter",
"r": "https://www.google.com/", "l": "cs-CZ",
"e": { "event_type": "search", "search_query": "tričko" } }
Call it from the visitor's browser only: the visitor identity is derived from the request's IP address and User-Agent - calling it from a server would count your server as a single visitor. For server-side integrations use the /ingest batch API. Limits: 300 requests/min per visitor, 4 KB per request.
Custom platform (API)
A different platform or a custom solution? Connect to the API directly - the easiest way is to start from the ready-made example:
Custom server-side events
Add the same event_type as in JavaScript to events[] in a signed POST /ingest batch. The name can describe any action, with optional id_object, quantity, value and currency fields.
"events": [{
"id_event": 9001,
"visitor_uuid": "0123456789abcdef0123456789abcdef",
"date_add": "2026-07-30 10:15:00",
"event_type": "lead_submitted",
"page_type": "cms",
"id_object": 123,
"value": "1500.00",
"currency": "CZK",
"session_id": "abcdef0123456789abcdef0123456789"
}]
id_event must be stable and unique within the site so batch retries stay idempotent. To connect the event to its traffic source, also send the same visitor_uuid and session_id as in touches[]. Send purchases and revenue through orders[], not only as an event.
api_key, run it and you are done. Even a beginner can manage it.
OpenAPI 3.0
Machine-readable contract: /api/openapi.yaml - import it into Postman or a client generator.
Or browse it right here in the interactive Swagger UI.
How often to send (recommended approach)
It is not mandatory - the API accepts a batch of any size at any time. Even so, we strongly recommend not sending a separate request for every visit or order. Data in Trackless is not (and need not be) real-time - reports are computed in batches, so sending often gains you nothing; it only adds needless transfers and load on both your server and ours. The best and most economical option for both sides is to collect data on your end and send it in batches.
- Collect data locally in a queue (your own table or log) and send it in batches, typically once a day - ideally at night, off-peak, via cron.
- Send only new or changed data. More reliable than just the time of the last
200is to mark rows as sent (a "sent" flag or byupdated_at); for orders whose state changed, the idempotent upsert lets you simply resend them (a short overlap). - Want fresher numbers? Feel free to send more often (e.g. hourly). True real-time, however, is not needed.
- Keep each batch a reasonable size - instead of one huge batch, send several smaller ones in a row.
- Lookup tables (
dimensions) only need to be included occasionally, when they change - not in every batch. - Retrying is safe (storing is idempotent, no duplicates), but retry only on
429,5xxor a network failure -400/403/409/413are permanent client-side errors, so fix the request or the key instead of looping.
This is exactly how our official PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6 and Sylius modules and plugins work: they collect data continuously into a local queue and send it in batches via cron (typically once a day; a large queue goes out as several batches in a row).
Endpoint
POST https://trackless.cz/ingest
Authentication
Every request is signed with HMAC-SHA256 over the exact (raw) request body, keyed by that site's api_key (shown after adding the site in My sites, and later in My account). The signature is sent in the X-Shaim-Signature header as lowercase hex.
An honest note: JS-based measurement can miss part of the traffic if the browser or an extension blocks external measurement scripts. The PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6 and Sylius server-side modules measure on the server, so they are more accurate. Margins and order line-item detail, however, are available only through modules that send order data or through the /ingest batch API (orders and revenue can also come from the JS order event).
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | yes | application/json |
X-Shaim-Signature | yes | HMAC-SHA256(raw body, api_key), hex |
X-Shaim-Module | no | Integration identifier, e.g. woocommerce (for the ingest log overview only) |
X-Shaim-Version | no | Version of your integration |
Request body
A single JSON object - a batch. All collections are optional; send only what you have. Values may be strings (the API normalizes them). The key parts:
| Field | Type | Description |
|---|---|---|
shop | object | { "id": 1, "domain": "muj-eshop.cz" } |
touches | array | Visits / sources (UTM, referrer, channel, session). |
orders | array | Orders including revenue and attribution. |
order_items | array | Order items including wholesale_price (the purchase price used for margin). |
events | array | Events (product view, search, …). |
client | array | Visitor context (server-side): device, browser, OS, country, language. |
dimensions | object | Lookups: order states, carriers, categories, currencies, languages, payment modules. |
The full list of fields for each collection is in the OpenAPI specification.
Body example
{
"shop": { "id": 1, "domain": "muj-eshop.cz" },
"orders": [
{ "id_order": 5001, "id_customer": 42, "total_paid": "1290.00",
"currency": "CZK", "payment": "Card", "date_order": "2026-06-02 10:40:00",
"current_state": 2, "lt_source": "google", "lt_medium": "cpc" }
],
"order_items": [
{ "id_order_item": 9001, "id_order": 5001, "id_product": 7, "product_name": "Tričko",
"quantity": 2, "unit_price_tax_excl": "533.06", "total_price_tax_excl": "1066.12",
"total_price_tax_incl": "1290.00", "wholesale_price": "300.00" }
]
}
Responses
| Code | Meaning |
|---|---|
200 OK | The batch was stored successfully and persistently. |
400 | Missing body/signature or invalid JSON. |
403 | Invalid signature or unknown account. |
409 | The api_key was reused on another store (a different domain under the same shop id). Nothing was stored - a permanent configuration conflict, not an outage: do not retry, fix the key (use a separate api_key per site). The response body tells you what to do. |
413 | The request body is too large (32 MB limit) - split the batch into smaller parts. |
429 | Rate limit exceeded - wait briefly and resend the batch. |
500 | Storing failed - send the batch again. |
Idempotency: storing is idempotent (insert-ignore + upsert by ID), so you can safely resend the same batch. De-duplication is by natural key (orders by id_order - upserted, so a changed order can simply be resent; items by id_order_item; visits and events by their id) - always send the real id; a missing one collapses to 0 and rows overwrite each other. Treat status 200 as success only after the store is confirmed.
The body of a successful 200 response is JSON {"ok":true,"excluded_ips":[…],"geoip":{…}}. excluded_ips is the account's central list of excluded IPs (exact IPs and CIDR ranges, IPv4 and IPv6) - your integration should read it and locally drop hits whose IP matches an entry before the next send (your integration knows the real IP, the server only gets a salted hash). geoip points to the current IP→country database (DB-IP Lite, CC BY 4.0) with version, sha256 and url fields; compare sha256 with your cached copy and re-download only when it changed (same data as GET /api/geoip/country.json or /api/geoip/country.dat.gz). Clients that ignore the body and only check the status code keep working unchanged.
Both /ingest and /collect can return 429 (rate limit) during bursts - the client waits briefly and retries.
Signing and sending examples
The easiest way is to download the ready-to-run PHP example. Here is a short version:
PHP
$apiKey = 'VAS_API_KEY';
$body = json_encode(['shop' => ['id' => 1, 'domain' => 'muj-eshop.cz'], 'orders' => []],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$sig = hash_hmac('sha256', $body, $apiKey);
$ch = curl_init('https://trackless.cz/ingest');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Shaim-Signature: ' . $sig,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 200 = uloženo
// Na 200 nese tělo seznam vyloučených IP účtu - uložte si ho a před
// dalším odesláním zahoďte zásahy s odpovídající IP (přesná shoda i CIDR).
$excludedIps = $status === 200 ? (json_decode($response, true)['excluded_ips'] ?? []) : [];
Node.js
import crypto from 'node:crypto';
const apiKey = 'VAS_API_KEY';
const body = JSON.stringify({ shop: { id: 1, domain: 'muj-eshop.cz' }, orders: [] });
const sig = crypto.createHmac('sha256', apiKey).update(body).digest('hex');
const res = await fetch('https://trackless.cz/ingest', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Shaim-Signature': sig },
body,
});
console.log(res.status); // 200 = uloženo
An honest note: JS-based measurement can miss part of the traffic if the browser or an extension blocks external measurement scripts. The PrestaShop, WordPress/WooCommerce, Joomla / VirtueMart, OpenCart, Magento 2, Shopware 6 and Sylius server-side modules measure on the server, so they are more accurate. Margins and order line-item detail, however, are available only through modules that send order data or through the /ingest batch API (orders and revenue can also come from the JS order event).
Read-only API - exporting your data
Besides sending data into Trackless, you can also pull it back out programmatically as JSON - for your own reports, a BI tool or automation. This uses a separate read-only token (independent of the ingest api_key; you can regenerate it at any time, and it reads only your account).
You will find and manage your read-only token after signing in, in the My account section.
Endpoints
GET https://trackless.cz/api/v1/export/all
GET https://trackless.cz/api/v1/export/{report}
GET https://trackless.cz/api/v1/bi/daily
{report} is one of: pages, products, channels, categories,
campaigns, search, events, devices, os,
browsers, countries. Parameters (optional): from, to (YYYY-MM-DD), shop, basis=products|total, attr=lt|ft, vat=0|1.
Authentication
Send the token in the Authorization: Bearer <token> header (recommended) or X-Api-Key: <token>. The ?token= query parameter also works, but use it only for quick testing - it ends up in browser history and in server/proxy logs.
curl -H "Authorization: Bearer VAS_READ_TOKEN" \
"https://trackless.cz/api/v1/export/all?from=2026-05-01&to=2026-05-31"
The response is JSON with the keys kpis, traffic, behaviour, profit, cancelled and reports (each report is an array of objects). The same data as the dashboard and the in-app export.
Looker Studio and daily BI data
The GET /api/v1/bi/daily endpoint returns a flat daily time series for BI tools. The from to to range includes both boundary dates, rows are ordered ascending and a day without activity gets a zero-filled row. Filters: shop, attr=lt|ft, basis=products|total and vat=0|1.
curl -H "Authorization: Bearer VAS_READ_TOKEN" \
"https://trackless.cz/api/v1/bi/daily?from=2026-05-01&to=2026-05-31&attr=lt&basis=products&vat=0"
How the connection will work
- Sign in to Looker Studio with your own Google account.
- Enter your own Trackless read-only token in the connector. The token belongs in the connector credentials, never in a URL or a shared report template.
- The connector uses a secure header to load only the account identified by that token and creates your data source from it.
- Use your own report or a copy of the prepared template. The shared connector owner does not gain access to your Google account or report.
The response contains account, range, filters and rows. Each row has stable English keys and numeric values for traffic, orders, revenue, refunds, profitability, purchase-cost coverage, advertising cost and ROAS. Technical fields gross_profit_revenue, cost_lines_with_cost and cost_lines_total let the connector recalculate weighted metrics correctly when grouping days into weeks or months.
Metric meaning: conversion_rate, gross_margin_pct and cost_coverage_pct are 0-100 percentages. Profitability always uses tax-exclusive product lines. It uses their real purchase cost; when that is missing and the shop has a default margin configured, it uses that estimate. cost_coverage_pct, however, counts only lines with a real purchase cost. The profitability calculation is independent of the selected headline revenue basis. Advertising cost belongs to the whole account; with a single-shop filter, ad_cost and roas are null because Trackless does not fabricate a shop allocation.
MVP limitation: one Google user stores credentials for one Trackless account in the connector at a time. Multiple shops inside that account are supported through the shop filter.
The shared Looker Studio connector is active. Sign in with your own Google account and enter your own Trackless read-only token.