Skip to main content

asgi-matomo

PyPI version PyPI - Python Version PyPI - Downloads Docs MIT License

Maturity badge - level 3 Stage

Code Coverage

CI(check) CI(release) CI(rolling) CI(test)

Tracking requests with Matomo from ASGI apps.

MatomoMiddleware adds tracking of all requests to Matomo to ASGI applications (Starlette, FastAPI, Quart, etc.). The intended usage is for api tracking (backends).

Note If you serve HTML (directly or by templates), it is suggested to track those parts through Matomo's javascript tracking.

Installation

pip install asgi-matomo

Or, to add to your project:

uv add asgi-matomo

Alternative with the background extra:

uv add 'asgi-matomo[background]'

What is tracked

Currently this middleware tracks:

  • url
  • ua: user_agent
  • gt_ms: mesaured as the time before and after this middleware call next in the asgi stack.
  • send_image=0 for performance issues
  • cvar with at least http_status_code and http_method set.
  • lang if accept-lang is set
  • cip client ip, requires access_token to be given.
  • action_name that defaults to path, but can be specified.

You can also pass variable to track by adding an asgi_matomo dict in the state dict of the ASGI scope:

scope = {
    "state": {
        "asgi_matomo": {
            "e_a": "Playing",
            "cvar": {
                "your": "custom",
                "data": "here",
            },
        }
    }
}

The keys of the asgi_matomo dict is expected to be valid parameter for the Matomo HTTP Tracking API. cvar is serialized with the standard json lib.

You can also track time spent on different tasks with trackers.PerfMsTracker.

import asyncio
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware

from asgi_matomo import MatomoMiddleware
from asgi_matomo.trackers import PerfMsTracker


async def homepage(request):
    async with PerfMsTracker(scope=request.scope, key="pf_srv"):
        # fetch/compute data
        await asyncio.sleep(1)
        data = {"data": "a" * 4000}
    return JSONResponse(data)


app = Starlette(
    routes=[Route("/", homepage)],
    middleware=[
        Middleware(
            MatomoMiddleware,
            matomo_url="YOUR MATOMO TRACKING URL",
            idsite=12345,  # your service tracking id
        )
    ],
)

Examples

Starlette

from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware

from asgi_matomo import MatomoMiddleware


async def homepage(request):
    return JSONResponse({"data": "a" * 4000})


app = Starlette(
    routes=[Route("/", homepage)],
    middleware=[
        Middleware(
            MatomoMiddleware,
            matomo_url="YOUR MATOMO TRACKING URL",
            idsite=12345,  # your service tracking id
        )
    ],
)

Starlette with background tasks

Note that BackgroundTaskMiddleware needs to be added after MatomoMiddleware.

from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware

from asgi_matomo import MatomoMiddleware
from asgi_matomo.background import BackgroundTaskMiddleware


async def homepage(request):
    return JSONResponse({"data": "a" * 4000})


app = Starlette(
    routes=[Route("/", homepage)],
    middleware=[
        Middleware(
            MatomoMiddleware,
            matomo_url="YOUR MATOMO TRACKING URL",
            idsite=12345,  # your service tracking id
        ),
        Middleware(BackgroundTaskMiddleware),
    ],
)

FastAPI

from fastapi import FastAPI
from asgi_matomo import MatomoMiddleware

app = FastAPI()
app.add_middleware(
    MatomoMiddleware,
    matomo_url="YOUR MATOMO TRACKING URL",
    idsite=12345,  # your service tracking id
)


@app.get("/")
def home() -> dict:
    return {"data": "a" * 4000}

FastAPI with background tasks

Note that BackgroundTaskMiddleware needs to be added after MatomoMiddleware.

from fastapi import FastAPI
from asgi_matomo import MatomoMiddleware
from asgi_matomo.background import BackgroundTaskMiddleware

app = FastAPI()
app.add_middleware(
    MatomoMiddleware,
    matomo_url="YOUR MATOMO TRACKING URL",
    idsite=12345,  # your service tracking id
)
app.add_middleware(BackgroundTaskMiddleware)


@app.get("/")
def home() -> dict:
    return {"data": "a" * 4000}

API Reference

Overview

app.add_middleware(
  MatomoMiddleware,
  matomo_url="YOUR MATOMO TRACKING URL",
  idsite=12345, # your service tracking id
  access_token="SECRETTOKEN",
  assume_https=True,
  exclude_paths=["/health"],
  exclude_patterns=[".*/old.*"],
  route_details={
    "route": {
      "action_name": "Name",
    }
  }
  client=None,
  http_timeout=5,
  allowed_methods="all-methods",
  ignored_methods=None
)

Parameters:

  • (Required) matomo_url: The URL to make your tracking calls to.
  • (Required) idsite: The tracking id for your service.
  • (Optional) access_token: Access token for Matomo. If this is set cip is also tracked. Required for tracking some data.
  • (Optional) assume_https: If True, set tracked url scheme to https, useful when running behind a proxy. Defaults to True.
  • (Optional) exclude_paths: A list of paths to exclude, only excludes path that is equal to a path in this list. These are tried before exclude_patterns. Defaults to None.
  • (Optional) exclude_patterns: A list of regex patterns that are compiled, and then exclude a path from tracking if any pattern match. Defaults to None. These are tried after exclude_paths.
  • (Optional) route_details: A dict with custom route-specific tracking data. Defaults to None.
  • (Optional) client: A custom http client to use (must use the same API as httpx.Client). Defaults to None, a client is created.
  • (Optional) http_timeout: Timeout to use for the client being created. Ignored if a custom client is provided. Defaults to 5.
  • (Optional) allowed_methods: List of HTTP methods that should be tracked. Defaults to "all-methods".
  • (Optional) ignored_methods: List of HTTP methods to ignored. Ignored methods takes precedence over allowed methods. Defaults to None.

Notes:

Ideas for further work

  • filtering tracked of urls
  • custom extraction of tracked data

This project keeps a changelog.

Development

This project uses uv and pre-commit.

Metadata

Release files for asgi-matomo 0.8.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 asgi-matomo 0.8.0
File Size Uploaded
asgi_matomo-0.8.0.tar.gz 8.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for asgi-matomo 0.8.0
File Interpreter ABI Platform
asgi_matomo-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 17.4 kB

Release files / asgi_matomo-0.8.0.tar.gz

Download URL asgi_matomo-0.8.0.tar.gz
Size 8.2 kB
Tags Source
SHA-256 checksum
How to use checksums
269c3074e4314eb85bfe356d394cf48cef7f95842813d38af26cdf939d9c50bf
BLAKE2b-256 checksum
How to use checksums
b3f31c5375ed932c5e92bce37f4c1b5276c8e1947c2c74c3c51351c0efd3ac3d
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 17, 2026.

Transparency log

Release files / asgi_matomo-0.8.0-py3-none-any.whl

Download URL asgi_matomo-0.8.0-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c68d7a3b665a7d997e391d350a65e818d7c98d6841ead5c93d71678200f3997
BLAKE2b-256 checksum
How to use checksums
511747f8c4c29b164506e71c3877f2760e669c2e18d67344c9b6f73cd9eaf8d5
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 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

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