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.

Overview of the Trackless connection paths and what each one can do.
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

PrestaShop module
The 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
Download .zip
WordPress / WooCommerce plugin
Works on any WordPress (traffic); with WooCommerce it adds orders and margin. Plugins → Add Plugin → search for Trackless → Install → Activate → paste your api_key.
Requirements: WordPress 6.2+, PHP 7.4+ (WooCommerce optional - 6.0+, 8.5+ for order source attribution).
Install from WordPress.org
Joomla / VirtueMart plugin
For Joomla 5.4+ and 6.x - System -> Install Extensions -> Upload Package File -> .zip, then System -> Plugins -> System - Trackless, paste your 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.
Open on Joomla Extensions Directory
OpenCart 3.x module
For OpenCart 3.x - Extensions → Installer → upload the .ocmod.zip → install it under Modules, paste your api_key and confirm the DPA.
Requirements: OpenCart 3.0.3+ and PHP 7.1+. No theme or core modifications (event hooks).
Download from OpenCart Marketplace
OpenCart 4.x module
For OpenCart 4.x - Extensions → Installer → upload the .ocmod.zip → install it under Modules, paste your 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.
Download from OpenCart Marketplace
Shopware 6 plugin
For Shopware 6.4+ - Extensions → My extensions → Upload extension → .zip → install and activate → in the settings paste your api_key and confirm the DPA.
Requirements: Shopware 6.4+, PHP 7.4+. Measures on the server including orders and line items.
Download .zip
Magento 2 module
For Magento 2.4+ - developer install via the console (Magento has no admin module upload): extract into 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).
Download .zip
Sylius plugin
For Sylius 1.12+ - developer install via Composer (Sylius has no admin plugin upload): 1) 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.
Download .zip

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-token attribute 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.

⬇ Ready-to-run PHP example to download
PHP 7.1+ · fill in your api_key, run it and you are done. Even a beginner can manage it.
Download example (.php)

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 200 is to mark rows as sent (a "sent" flag or by updated_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, 5xx or a network failure - 400/403/409/413 are 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

HeaderRequiredDescription
Content-Typeyesapplication/json
X-Shaim-SignatureyesHMAC-SHA256(raw body, api_key), hex
X-Shaim-ModulenoIntegration identifier, e.g. woocommerce (for the ingest log overview only)
X-Shaim-VersionnoVersion 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:

FieldTypeDescription
shopobject{ "id": 1, "domain": "muj-eshop.cz" }
touchesarrayVisits / sources (UTM, referrer, channel, session).
ordersarrayOrders including revenue and attribution.
order_itemsarrayOrder items including wholesale_price (the purchase price used for margin).
eventsarrayEvents (product view, search, …).
clientarrayVisitor context (server-side): device, browser, OS, country, language.
dimensionsobjectLookups: 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

CodeMeaning
200 OKThe batch was stored successfully and persistently.
400Missing body/signature or invalid JSON.
403Invalid signature or unknown account.
409The 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.
413The request body is too large (32 MB limit) - split the batch into smaller parts.
429Rate limit exceeded - wait briefly and resend the batch.
500Storing 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

  1. Sign in to Looker Studio with your own Google account.
  2. 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.
  3. The connector uses a secure header to load only the account identified by that token and creates your data source from it.
  4. 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.

Connect Trackless to Looker Studio

← Back to home