streamlit-analytics2
Privacy-first usage analytics for Streamlit apps. One with block gives
you pageviews, visitors, widget clicks, custom events, a built-in dashboard
and SQL over your own data. No JavaScript, no cookies, no IP addresses stored.
Use it
- Install:
pip install streamlit-analytics2 - Wrap your app:
import streamlit as st import streamlit_analytics2 as sa2 with sa2.track(): st.write("Hello") st.button("Click me")
- Open your app with
?analytics=onon the URL:http://localhost:8501/?analytics=on
That is the whole integration. Everything below is optional.
Keep the numbers across restarts
with sa2.track(save_to_json="analytics.json"): # counters + analytics.events.jsonl
...
with sa2.track(events_path="analytics.db"): # SQLite, unlocks the query tab
...
Protect the dashboard
with sa2.track(unsafe_password=st.secrets["analytics_password"]):
...
The password is plain text inside the app, so keep it in st.secrets or an
environment variable and pick something you would not reuse.
What the dashboard shows
- Views, visits, visitors, bounce rate, average visit time, active now.
- Views per page over time, hourly for today, with range and page filters.
- Pages, widgets, browsers, OS, devices, languages, regions, UTM sources and campaigns, custom events.
- A weekday-by-hour traffic-load heatmap and the busiest hour.
- Recent visits.
- Raw data query: read-only SQL over the SQLite log, example queries, CSV
download and a chart picker. Needs
events_pathon a.dbfile and a password.
Your own runs with ?analytics=on open are not counted.
Track your own events
if st.button("Generate report"):
sa2.event("report generated", rows=len(df))
Privacy
Stored per visit: a hash of address and browser that changes every day, browser, OS and device family, language, timezone, theme, UTM tags, pages. Stored per interaction: widget type, label, key, page and the chosen option.
Never stored: IP addresses, raw User-Agent strings, query strings, or anything
typed into text fields. Set store_values=True if you do want typed text.
Options
| Argument | Default | What it does |
|---|---|---|
unsafe_password |
None |
Password for the dashboard. Also gates the reset and the query tab. |
save_to_json |
None |
Counters file (0.10 shape). Events go to <name>.events.jsonl beside it. |
load_from_json |
None |
Load counters from that file at start. |
events_path |
None |
Event log path. .jsonl by default, .db / .sqlite for SQLite. |
store |
None |
Your own backend: any object with append(events) and read(). |
store_values |
False |
Record typed text instead of <text>. |
session_id |
None |
Also keep per-session counters in Firestore under this document. |
firestore_key_file, firestore_collection_name, firestore_document_name, firestore_project_name, streamlit_secrets_firestore_key |
Persist the counters in Firestore. See the wiki. | |
verbose |
False |
Log what is loaded and saved. |
start_tracking() and stop_tracking() take the same arguments if you
prefer them to the with block. Every event is a plain record (ts, kind,
session, visitor, page, name, widget_type, key, value, props),
so the log is easy to feed into pandas, DuckDB or an LLM.
Multipage apps
Call sa2.track() on every page. Views and widgets are recorded per page in
the event log and on the dashboard. The legacy counters in
streamlit_analytics2.data are shared across pages, as before.
How it works
Streamlit already knows which widget a user changed on each rerun. This
library reads that from one place instead of wrapping every st.* function,
so widgets inside columns, forms, expanders, tabs, dialogs and the sidebar are
all seen, and a widget rendering with its default is never counted as a click.
Upgrading from 0.10
Nothing to change in your code. The numbers will be lower because a widget rendering with its default no longer counts as an interaction, typed text is no longer stored unless you ask, and the dashboard reset now needs a password. Details in CHANGELOG.md.
Contributing
Issues and pull requests are welcome. See
CONTRIBUTING.md.
Development: uv sync --all-extras && uv run pytest. A dev app with every
widget type lives in examples/dev/; examples/dev/seed.py fills a dev log
with made-up traffic so the dashboard has something to show.
License
MIT. See LICENSE.
Release files for streamlit-analytics2 0.11.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 | |
|---|---|---|---|
| streamlit_analytics2-0.11.0.tar.gz | 28.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| streamlit_analytics2-0.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.2 kB
Release files / streamlit_analytics2-0.11.0.tar.gz
| Download URL | streamlit_analytics2-0.11.0.tar.gz |
|---|---|
| Size | 28.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65a1c490c7b5e88d4744d71d5c9f54af134b7dcd0380012b6cedd68199f08a50
|
|
BLAKE2b-256 checksum How to use checksums |
97fe5789bf741c359f7df4bfe48a2b266877e57f9203449242f6ff98983e1e51
|
| 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 25, 2026.
Transparency logRelease files / streamlit_analytics2-0.11.0-py3-none-any.whl
| Download URL | streamlit_analytics2-0.11.0-py3-none-any.whl |
|---|---|
| Size | 32.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d38f6f74f8d8d1c14bc74e5826d57f09bfea3c438b7275643ac3d7ec2acf54cd
|
|
BLAKE2b-256 checksum How to use checksums |
01fcdeaecc0861bc7fbaa81b91d1aac4c1bf56dee7097350628f1ae946e44ea4
|
| 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 25, 2026.
Transparency log