init() parameters
Every tp.init() option in both SDKs, what firewall mode does, and the three environment variables.
Call tp.init() once at startup, before your provider clients are imported. In practice you
set three things — the key, the collector URL, and the firewall mode. Everything else has a
sensible default.
import token_police as tp
import os
tp.init(
api_key=os.environ["TOKENPOLICE_API_KEY"],
base_url="https://collect.tokenpolice.ai",
firewall="dry_run", # watch first; flip to "enforce" when ready
)import * as tp from "token-police";
import OpenAI from "openai";
tp.init({
apiKey: process.env.TOKENPOLICE_API_KEY,
baseUrl: "https://collect.tokenpolice.ai",
firewall: "dry_run",
instrumentModules: { openAI: OpenAI }, // Node: see Node & ESM
});https://collect.tokenpolice.ai is also the built-in default in both SDKs, so you can
leave base_url out. Set it anyway — an explicit URL is what makes the destination visible
in your repo, and it's the one line you change to point at a self-hosted or local collector.
The firewall mode
This is the dial that decides whether TokenPolice can ever stop a call. It takes exactly three values, spelled in lower case:
firewall | Pre-call check | On a block decision | What your app sees |
|---|---|---|---|
"dry_run" (default) | Runs in full | Recorded as a would-block | Nothing. The call proceeds. |
"enforce" | Runs in full | Raised into your code | TokenPoliceBlockedError — catch it |
"off" | Skipped | — | Nothing. No rule can act. |
A few things worth knowing:
"off"is not "disabled". Usage is still logged for every provider, so your dashboard and cost history keep filling up. Only the check and enforcement stop."dry_run"runs the whole check, including the network round-trip — that's what makes its would-block records trustworthy. Budget for up totimeoutof extra latency per call (0.5 s by default), and it fails open if the collector is slow.- The value is matched byte for byte.
"Enforce"or"DRY_RUN"is not recognised: the SDK falls back to"dry_run"— never to"enforce"— so a typo can never start blocking traffic by accident. Node prints a warning; Python only prints it whenlog_errors=True. - A reroute is enforcement too. A rule that swaps the model only takes effect under
"enforce"; in dry-run it is recorded and nothing changes.
Two dials, not one. Setting firewall="enforce" is only half of it. Each Block or
Reroute rule you create also has its own Enforce / Dry-run setting in the dashboard, and a
call is blocked only when both say enforce. See
Dry-run vs enforce.
The one exception to fail-open. If TokenPolice becomes unreachable, calls go through —
with one deliberate exception: under "enforce" on a daemon deployment, something the
firewall had already blocked stays blocked through the outage. An outage can't be used to
lift a limit you set. The exception needs the SDK's open update stream, which it opens only
when deployment is "daemon"; on "serverless" and "edge" there is no stream and an
outage fails fully open. See Fail-open.
Python parameters
| Parameter | Type | Default | What it does |
|---|---|---|---|
api_key | str | env TOKENPOLICE_API_KEY, else raises | Your tp_sk_… key. A key that doesn't start with tp_sk_ only warns. |
base_url | str | env TOKENPOLICE_BASE_URL, else https://collect.tokenpolice.ai | Collector URL. Trailing slashes are stripped. |
firewall | str | "dry_run" | "dry_run" / "enforce" / "off" — see above. |
timeout | float | 2.0 | Max seconds the SDK waits for TokenPolice before letting the call through anyway. Python takes the value as given — it isn't validated or clamped (Node's is). |
log_errors | bool | False | Turn on the SDK's own diagnostic output. An integration aid — turn it back off when you're done. |
capture_stream_usage | bool | on | Makes streamed calls report token counts. Off, a streamed call's cost is lost. TP_CAPTURE_STREAM_USAGE=0 disables it. |
error_detail | str | "redacted" | How much of a failed provider call's error text leaves your process: "none" (classifier only), "redacted" (exception class + a hash of the message), "raw" (the verbatim, truncated string). Unknown values become "redacted". |
deployment | str | "auto" | "auto" / "daemon" / "serverless" / "edge". Auto-detection is usually right, and setting it does not flush for you — see Flushing & serverless. |
max_workers | int | CPU-based | Python only. Size of the background pool that ships usage logs off your request path. |
tracer_provider | OTel provider | None | Python only. Attach TokenPolice to an OpenTelemetry provider you already run instead of the private one it builds. |
sse_reconnect_max_interval_seconds | int | 300 | Cap on the backoff between attempts to re-establish the rule feed. Rarely set. |
stream_stale_grace_seconds | float | 60 | Clamped to 0–3600. How long the SDK keeps trusting its cached rule view after the feed drops, before re-checking a matching call with the server. 0 re-checks from the first missed moment. |
enforce | bool | — | Deprecated alias — don't use it. Use firewall. |
Node parameters
Same options, camelCased.
| Option | Type | Default | What it does |
|---|---|---|---|
apiKey | string | env TOKENPOLICE_API_KEY, else throws | Your tp_sk_… key. A key that doesn't start with tp_sk_ only warns. |
baseUrl | string | env TOKENPOLICE_BASE_URL, else https://collect.tokenpolice.ai | Collector URL. Trailing slashes are stripped. |
firewall | FirewallMode | "dry_run" | Same three values, same byte-exact matching, same safe fallback. |
timeout | number | 2.0 | Max seconds. A non-positive or non-finite value warns and falls back to 2.0. |
logErrors | boolean | false | Same as Python; also un-gates the SDK's debug lines. |
captureStreamUsage | boolean | on | Same as Python; TP_CAPTURE_STREAM_USAGE=0 disables. |
errorDetail | ErrorDetailMode | "redacted" | Same three modes and the same safe coercion as Python. |
deployment | string | "auto" | Same as Python, including edge runtimes. |
instrumentModules | InstrumentModules | — | Node only. Hand the SDK the provider modules your app already imported — see Node & ESM. |
sseReconnectMaxIntervalSeconds | number | 300 | Same as Python. |
streamStaleGraceSeconds | number | 60 | Same as Python, same 0–3600 clamp. |
enforce | boolean | — | Deprecated alias — don't use it. Use firewall. |
Node has no maxWorkers or tracerProvider (it's fetch-based, with no thread pool).
Call init() once per process. Calling it again replaces the client and says so in the
log — and after uninstrument() a second init() can't re-attach
at all. Other than a missing API key, init() never throws: if anything inside it fails, the
SDK degrades instead of taking your app down.
Environment variables
Three, and three only:
| Variable | Replaces |
|---|---|
TOKENPOLICE_API_KEY | api_key / apiKey |
TOKENPOLICE_BASE_URL | base_url / baseUrl |
TP_CAPTURE_STREAM_USAGE | capture_stream_usage / captureStreamUsage (0 disables) |
The prefix is TOKENPOLICE_, one word. TOKEN_POLICE_API_KEY (with an underscore) is read by
nothing — a common copy-paste trap.

