detrack
Strip tracking parameters from URLs. Deterministically. Zero dependencies.
Install
pip install detrack
Quick start
import detrack
url = "https://example.com/post?utm_source=twitter&q=python&fbclid=123"
result = detrack.clean(url)
print(result.url)
# "https://example.com/post?q=python"
print(result.removed_params)
# {"utm_source": "twitter", "fbclid": "123"}
print(result.cleaned_params)
# {"q": "python"}
# Quick check if tracking was found
if result.has_tracking:
print(f"Stripped: {list(result.removed_params.keys())}")
# Stripped: ['utm_source', 'fbclid']
Why detrack?
Other URL cleaners do too much (host remapping, site-specific rules, semantic rewriting), while detrack does one thing and does it well: remove tracking parameters.
This makes detrack predictable, testable, and trivial to integrate.
Ecosystem
detrack is the shared cleaning layer for the seoslug (SEO metadata) and tagurl (semantic tagging) libraries.
Configuration
detrack ships with sensible defaults. Override them globally with configure(), or per-call with a Settings object.
Global configuration
from detrack import configure
# Raise the query length limit to 16KB
configure(max_query_length=16384)
Per-call override
from detrack import Settings, clean_query
# This call uses a 2KB limit, ignoring the global setting
clean_query(query, settings=Settings(max_query_length=2048))
Settings
@dataclass
class Settings:
max_query_length: int = 8192 # queries longer than this are returned unchanged
use_prefixes: bool = True # strip params matching known prefixes (utm_*, mtm_*, etc.)
| Field | Type | Default | Description |
|---|---|---|---|
max_query_length |
int |
8192 |
Maximum query string length (in characters). Longer queries are returned unchanged to prevent abuse. |
use_prefixes |
bool |
True |
When True, any param starting with a known prefix (utm_, mtm_, hsa_, pk_, etc.) is stripped even if not listed explicitly. |
Examples
Basic
>>> detrack.clean("https://example.com?utm_source=twitter&q=python")
DetrackResult(url="https://example.com?q=python", cleaned_params={"q": "python"},
removed_params={"utm_source": "twitter"})
Multiple trackers stripped
>>> detrack.clean("https://example.com?a=1&utm_source=x&b=2&fbclid=y&c=3")
DetrackResult(url="https://example.com?a=1&b=2&c=3",
cleaned_params={"a": "1", "b": "2", "c": "3"},
removed_params={"utm_source": "x", "fbclid": "y"})
All params stripped (query removed entirely)
>>> detrack.clean("https://example.com?utm_source=x&fbclid=y")
DetrackResult(url="https://example.com",
cleaned_params={},
removed_params={"utm_source": "x", "fbclid": "y"})
Custom patterns
>>> detrack.clean("https://example.com?session=abc123&page=1", patterns=["session"])
DetrackResult(url="https://example.com?page=1",
cleaned_params={"page": "1"},
removed_params={"session": "abc123"})
Query string only
>>> detrack.clean_query("a=1&utm_source=x&b=2")
"a=1&b=2"
>>> detrack.clean_query("utm_source=x&fbclid=y")
""
API
detrack.clean(url, patterns=None, settings=None)
Strip tracking parameters from a full URL.
| Parameter | Type | Description |
|---|---|---|
url |
str |
Any URL string |
patterns |
Iterable[str] | None |
Optional param names to strip (defaults to DEFAULT_PATTERNS) |
settings |
Settings | None |
Optional per-call settings override (defaults to DEFAULT_SETTINGS) |
Returns: DetrackResult -> dataclass with cleaned URL and metadata.
Raises: Nothing -> pure function, no exceptions.
Malformed URLs pass through unchanged. Queries exceeding max_query_length are returned unchanged.
detrack.clean_query(query, patterns=None, settings=None)
Strip tracking parameters from a query string only.
| Parameter | Type | Description |
|---|---|---|
query |
str |
URL query string, e.g. "a=1&utm_source=x&b=2" |
patterns |
Iterable[str] | None |
Optional param names to strip |
settings |
Settings | None |
Optional per-call settings override (defaults to DEFAULT_SETTINGS) |
Returns: str -> cleaned query string. Returns the input unchanged if it's malformed or exceeds max_query_length.
detrack.clean_url(url, patterns=None, settings=None)
Convenience shorthand — returns just the cleaned URL string.
| Parameter | Type | Description |
|---|---|---|
url |
str |
Any URL string |
patterns |
Iterable[str] | None |
Optional param names to strip |
settings |
Settings | None |
Optional per-call settings override |
Returns: str -> cleaned URL.
>>> from detrack import clean_url
>>> clean_url("https://example.com?utm_source=twitter&q=python")
'https://example.com?q=python'
detrack.clean_batch(urls, patterns=None, settings=None)
Strip tracking parameters from multiple URLs at once.
| Parameter | Type | Description |
|---|---|---|
urls |
Iterable[str] |
URLs to clean |
patterns |
Iterable[str] | None |
Optional param names to strip |
settings |
Settings | None |
Optional per-call settings override |
Returns: list[DetrackResult] -> one result per input URL.
>>> from detrack import clean_batch
>>> urls = [
... "https://example.com?a=1&utm_source=x",
... "https://example.com?fbclid=y&b=2",
... ]
>>> results = clean_batch(urls)
>>> [r.url for r in results]
['https://example.com?a=1', 'https://example.com?b=2']
>>> [r.has_tracking for r in results]
[True, True]
detrack.configure(**kwargs)
Update global settings. Only specified fields are changed.
from detrack import configure
configure(max_query_length=16384)
Raises: TypeError for unknown keyword arguments.
detrack.DEFAULT_PATTERNS
frozenset[str] # 330+ common tracking parameters
Covers 20+ platforms: UTM (50+ variants), Google Ads/Analytics, Facebook/Meta, TikTok, LinkedIn, Spotify, HubSpot (18 params), Matomo/Piwik, Adjust, AppsFlyer, Branch.io, Yandex, Microsoft/Bing, Pinterest, Snapchat, Quora, AT Internet, Adobe/Marketo, Coremetrics, MyTracker, email marketing (Mailchimp, Klaviyo, etc.), affiliate networks (CJ, Awin, etc.), cache busters, session IDs, and redirect params.
Prefix matching is enabled by default: any param starting with utm_, mtm_,
hsa_, pk_, af_, adj_, at_, cm_, bsft_, mc_, ir_, fb_,
hs_, piwik_, mt_, vgo_, sms_, eml_, or nb_ is also stripped.
Pass a custom patterns list to clean() to override entirely.
detrack.PREFIXES
tuple[str, ...] # 19 prefixes: ("utm_", "mtm_", "pk_", "hsa_", ...)
The prefixes used for automatic param matching when use_prefixes=True. Useful for understanding what gets stripped or building custom logic.
detrack.DEFAULT_SETTINGS
Settings(max_query_length=8192, use_prefixes=True)
Global default settings instance. Modify with :func:configure.
detrack.__version__
"0.3.0"
Current library version string.
DetrackResult
@dataclass
class DetrackResult:
url: str # Cleaned URL
parsed_url: SplitResult # urllib.parse result (for further processing)
cleaned_params: dict[str, str] # Parameters that remain
removed_params: dict[str, str] # Stripped parameters + their original values
has_tracking: bool # True if any tracking params were removed
String behavior: str(result) and f-strings return the cleaned URL directly.
>>> result = clean("https://example.com?utm_source=x&q=1")
>>> f"{result}"
'https://example.com?q=1'
Repr: Compact, without SplitResult internals.
>>> repr(result)
"DetrackResult(url='https://example.com?q=1', cleaned_params={'q': '1'}, removed_params={'utm_source': 'x'})"
Unpacking: Tuple unpacking yields (url, cleaned_params, removed_params).
>>> url, cleaned, removed = clean("https://example.com?utm_source=x&q=1")
>>> url
'https://example.com?q=1'
removed_params preserves the original values so you can log what was stripped
for analytics, debugging, or compliance.
Features
- 330+ default patterns: covers 20+ platforms — UTM, Google, Facebook, TikTok, LinkedIn, Spotify, HubSpot, Matomo, Adjust, AppsFlyer, and more
- Prefix matching: automatically strips params starting with
utm_,mtm_,hsa_,pk_, etc. even if not listed explicitly - Case-insensitive matching:
UTM_SOURCE,Utm_Source, andutm_sourceare all stripped - Zero dependencies: uses only
urllib.parsefrom the Python standard library - Deterministic: same input always yields the same output, across all systems
- Pure functions: no state, no I/O, no random numbers, no exceptions
- Metadata returned:
removed_paramstells you exactly what was stripped and its original value has_tracking: quick boolean check on the result —if clean(url).has_tracking- Batch cleaning: process multiple URLs at once with
clean_batch(urls) - Configurable: query length guard, prefix matching, and pattern lists are all adjustable
See MIT LICENSE.
Release files for detrack 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| detrack-0.4.0.tar.gz | 18.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| detrack-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.7 kB
Release files / detrack-0.4.0.tar.gz
| Download URL | detrack-0.4.0.tar.gz |
|---|---|
| Size | 18.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a73c9c750f58fd72d0ea6ed492f914a11e1005b34e050afc98059bc12e35c911
|
|
BLAKE2b-256 checksum How to use checksums |
121eb924fa6998b87cacef18500cd65340f591771efed7254fbce65eb615172a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logRelease files / detrack-0.4.0-py3-none-any.whl
| Download URL | detrack-0.4.0-py3-none-any.whl |
|---|---|
| Size | 12.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c0a0ef80a5544698cb73c0cbaefe740dad91da17eff6221f2fc0f0d8590b52f7
|
|
BLAKE2b-256 checksum How to use checksums |
d4a744c642ac713296cdefd532fb8cc9b48917fbf52a32d6ef92ae1d78918787
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency log