Telemetry

Everything the engine sends home

A backtester that runs on your machine should be worth trusting with what is on it. Manifold-BT makes two network calls, both written out below field by field, and neither one carries a line of your strategies, your data or your results. One command turns the optional one off.

$export MANIFOLDBT_NO_TELEMETRY=1

That is the whole opt-out. No account, no setting, no support ticket, and nothing about the library changes afterwards.

2
network calls the engine can make, both listed below
1 / day
ceiling on the anonymous ping, per machine
5 s
timeout, on a background thread, never blocking you
0
bytes of strategy code, market data or results
every install, both tiers

The anonymous census

Once a day, the first time you import the library, it says which build just ran. That is the entire message. It carries no email, no license key and no account, because on Community none of those exist in the first place.

The identifier is a hash, stable across runs so that one machine counts once instead of once per import. Nothing in the record describes what you ran, only what you ran it with.

the complete payload
POST https://www.manifoldbt.com/api/installs

{
  "install_id": "9f2c8b1e4a7d0c35e6b18f42a9d7c003",
  "version":    "0.17.0",
  "os":         "linux",
  "arch":       "x86_64",
  "python":     "3.12",
  "cuda":       false,
  "tier":       "community",
  "timestamp":  "2026-08-18T09:14:22.481Z"
}

Once a day, at most

The first import of the day pings, every import after it returns before touching the network. A version change pings immediately, so an upgrade shows up the day it happens instead of a day later.

It cannot fail your import

The request runs on a background thread with a five second timeout, and every error path is a silent return. The server answers {"ok": true} and the engine throws the answer away without reading it.

It records the attempt, not the success

The daily throttle is written before the request is made. A machine that is offline, or behind a firewall that drops the call, does not retry on every import for the rest of the day.

CI runners are excluded

Any truthy CI variable silences it. A throwaway runner mints a fresh identifier on every job, so counting them would inflate the numbers with something that is not a person.

pro only

The license check

This is the one that is not anonymous, and pretending otherwise would be dishonest: it names the license and the email it was sold to, because its job is to enforce the two-device limit that the price is built on.

It runs when the engine initialises. The server answers whether this machine is still one of the two allowed devices, and a negative answer drops the process back to Community rather than failing anything.

the complete payload
POST https://www.manifoldbt.com/api/telemetry

{
  "license_hash": "6b1f...c204",
  "license_id":   "4c6f638d-819c-4ac8-8cc4-54d8fba94df4",
  "email":        "you@example.com",
  "device_hash":  "25a5...f364",
  "version":      "0.17.0",
  "os":           "linux",
  "arch":         "x86_64",
  "cuda":         false,
  "timestamp":    "2026-08-18T09:14:22.481Z"
}

Pro only

Community never sends this request. There is nothing to validate, and no license, email or device identifier exists on that side.

It is what grants Pro

Unlike the census, this one is not optional: it enforces the two-device limit that the license is sold under. Blocking it does not break your code, it just returns you to Community.

72 hours, then 14 days

A machine that has never reached the server keeps Pro for 72 hours from first launch. Once it has checked in successfully, it has 14 days between checks before the engine drops itself back to Community, offline or not.

The clock only moves forward

The local state file records the highest instant it has ever seen, so setting the system clock backwards does not extend a trial. It is a MAC-protected file, and editing it resets it rather than granting anything.

What is never in either request

The two payloads above are complete. Nothing is elided for brevity, and there is no third endpoint. Everything in this list is absent because the engine never reads it in the first place.

Strategy code
Parameters and sweeps
Symbols and market data
Backtest results
File paths
Hostname or username
Environment variables
Anything from your notebook

One thing we will not pretend away: an HTTPS request arrives from an IP address, as every HTTPS request does. It is not written into either record, and nothing in the tables is keyed on it. If that is still one connection more than you want, the census switches off and the license check is only made by machines running Pro.

Two records, kept apart

On Community the question does not really arise. That installation never sends a license, an email or a device identifier, so there is no second record in existence for a census row to be matched against. It is anonymous to us in the only sense that means anything: the other half does not exist.

On Pro it is worth being exact. The license check stores a device fingerprint next to your email, and the census identifier is derived from that same fingerprint under a different salt. A hash keeps a stranger from recovering your machine, and it does that here too, but it does not stop the party that already holds both sides from correlating them. Nothing we operate does: they are separate tables, and no page, export or query reads them together.

We would rather write that down than claim an impossibility you have no way to check. And if a promise is not what you want to rely on, set the variable: there is then no census record to correlate with anything.

census record

One row per machine, overwritten on every ping, plus one row per machine per active day. Build and dates only.

license record

Your email and the machines your license runs on, kept for as long as the license exists, so support can find you and the device limit can be enforced.

on community

Only the first of those two records is ever created. There is no license, no email and no device identifier, so there is nothing on the other side to match a census row against.

Turning it off

One variable, read at import. Either name works, and DO_NOT_TRACK is the cross-vendor one you may already have set for other tools. An empty value, 0 and false all mean no, so setting DO_NOT_TRACK=0 is an explicit opt-in rather than an accident.

macOS and Linux
# any shell, before you start Python
export MANIFOLDBT_NO_TELEMETRY=1

# the cross-vendor variable works too
export DO_NOT_TRACK=1
Windows
# PowerShell, this session
$env:MANIFOLDBT_NO_TELEMETRY = "1"

# or permanently, for your user
setx MANIFOLDBT_NO_TELEMETRY 1
From Python
import os

# The ping fires on import, so set this first.
os.environ["MANIFOLDBT_NO_TELEMETRY"] = "1"

import manifoldbt

Continuous integration needs nothing: any truthy CI variable already silences the census. The license check is a separate matter, since it is what grants Pro, and it has no switch. Blocking it costs you Pro after the offline window, not your code.

What it is actually for

Not growth metrics, and not a dashboard anyone watches for fun. Three decisions that are otherwise made blind, on a library small enough that guessing wrong is expensive.

Knowing what we can stop supporting

versions

Three machines still on an old minor, on a Python we would like to drop, is a decision. Zero machines is a different decision. Without the census both look identical from here, so the safe move is to support everything forever, which is how a small library ends up slow to change.

Which wheels are worth building

platforms

Every extra target is a build, a test matrix and a release that can break. The operating system, the CPU architecture and whether the wheel carries the CUDA kernels tell us which of them anyone actually runs.

Reproducing what you report

support

A bug report that says it is slow is a different bug on a Windows CPU build than on a Linux CUDA one. The license check carries the build so a support thread starts from the right binary instead of a guess.

Check it rather than believe it

The engine ships as a compiled wheel, so a page telling you what it sends is worth exactly what you decide it is worth. Both hosts are named on this page precisely so you do not have to take it on faith.

Watch the traffic, or block the two endpoints and keep working. On Community, blocking changes nothing at all: the census answer is discarded before it is read.

the only two hosts
www.manifoldbt.com/api/installs
www.manifoldbt.com/api/telemetry
data connectors are yours

Exchange and vendor connectors talk to the venues you configure, with the keys you supply. Those calls go from your machine to your provider and never through us.

FAQ

The short version

Two requests, both printed above in full. Nothing you compute leaves the machine. The optional one switches off with a variable, and the other one exists only because a license has to mean something.

$export MANIFOLDBT_NO_TELEMETRY=1
Privacy policy