Skip to main content

Getting started

By the end of this page you will have sent one custom event from a Python script and found it in your Umami dashboard. That is the whole arc: install, configure, send, look. It assumes you already have a running Umami instance, self-hosted or Umami Cloud, with a website registered in it. If you are on Cloud, skim the Cloud section at the end before you configure anything, because one line is different.

Install

The package on PyPI is umami-analytics. The import name is umami.

uv add umami-analytics
# or
pip install umami-analytics

A warning that earns its own paragraph: do not run pip install umami. That name belongs to an unrelated project that also imports as umami. The symptoms are an import that succeeds and functions that are not there, followed by a confused half hour.

The package supports Python 3.10 and newer and depends on httpx2 and pydantic.

Configure

There is no client object to construct and nothing to pass around. The package is a module with a memory. You tell it three things once, when your application starts, and every later call reads them.

import umami

umami.set_url_base('https://umami.example.com')  # your instance root, without /api
umami.set_website_id('978435e2-7ba1-4337-9860-ec31ece2db60')
umami.set_hostname('example.com')

Each of those lines has a story:

  • set_url_base() goes first, always. It checks that the URL starts with http:// or https:// and trims a trailing slash. Leave /api off; the package appends the endpoint paths itself, and including it is the most common cause of a 404 on every single call.
  • set_website_id() takes the id from the Umami dashboard (Settings, Websites, Edit). It becomes the default for every later call, and any call can override it with its own website_id argument.
  • set_hostname() is the hostname stamped on each event. Umami groups traffic by it, so use the domain your visitors type, not the name of the server doing the sending. A hostname of worker-03.internal will work, and it will haunt your reports.

Notice what is missing: a login. Sending events needs no credential at all, because Umami’s ingestion endpoint is public by design, the same way the browser tracker on a public page needs none. Login only matters when you read data back, and Authentication covers that.

Send your first event

response = umami.new_event(
    event_name='purchase-course',
    url='/courses/python-for-absolute-beginners',
    custom_data={'plan': 'individual', 'price': 69},
)

event_name is the only required argument. url defaults to / and is the page the event is attributed to, so pick the page a visitor would associate with it. custom_data is any JSON-serializable dict and becomes the event’s properties in Umami.

If that call returns without raising, Umami accepted the event. Now go and look at it.

Read the result

Open the website in your Umami dashboard. The event appears in two places:

  • In the Events section at the bottom of the website page, listed under its event_name with a count.
  • In the Pages section, as traffic on the url you passed.

Click the event name to see the properties from custom_data. That is the plan and price you sent, now sitting alongside everything the browser tracker collects.

The return value is Umami’s JSON response parsed into a dict. On current Umami versions it holds a cache token plus sessionId and visitId. You can ignore it; the exact keys depend on the server version and the package does not use them.

If the call raised, or returned normally and the dashboard stays stubbornly empty, go to Troubleshooting. The empty dashboard has a usual suspect, Umami’s bot filter, and the page explains how to catch it.

Check the connection

heartbeat() answers one question: can this process reach the configured instance? It never raises. It returns False on any problem, including a missing set_url_base(), so it is safe to call from a health check.

if not umami.heartbeat():
    print('Umami is not reachable')

Umami Cloud

On Umami Cloud there is no URL to point at and no password to type. You create an API key in the Cloud dashboard, and that key replaces set_url_base():

import umami

umami.set_cloud_api_key('your-cloud-api-key')  # region='us' or 'eu' is optional
umami.set_website_id('978435e2-7ba1-4337-9860-ec31ece2db60')
umami.set_hostname('example.com')

umami.new_event(event_name='purchase-course', url='/courses/python-for-absolute-beginners')

Events go to https://cloud.umami.is/api/send and query calls go to https://api.umami.is/v1, with the key sent as an x-umami-api-key header. Do not call login() in Cloud mode; it raises OperationNotAllowedError, because the key already is the credential. Authentication explains both modes and how to switch between them.

Next steps