Skip to main content

detrack

Strip tracking parameters from URLs. Deterministically. Zero dependencies.

PyPI Python License no 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, and utm_source are all stripped
  • Zero dependencies: uses only urllib.parse from 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_params tells 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)

Source distribution for detrack 0.4.0
File Size Uploaded
detrack-0.4.0.tar.gz 18.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for detrack 0.4.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page