Privatus Analytics for Sphinx
Real visitor numbers for your Sphinx docs. No cookies, no cookie pop-up, no bots.
You want to know if people read your Sphinx docs. But Google Analytics uses cookies, so in many countries you must show a cookie pop-up first (the box that asks visitors to say yes to cookies). Many people click no, so they never show up in your numbers. Bots add fake visits on top.
This extension adds the Privatus Analytics script to every page of your Sphinx HTML docs. It works with any HTML theme and on Read the Docs. The script sets no cookies and keeps no IP addresses. Bots are filtered out before they are counted.
So you see how many real people came, which pages they looked at and where they came from, on one simple dashboard. A report comes to your inbox each week, and you can set alerts for when traffic jumps or drops. And you don't need a cookie pop-up for your analytics.
Free plan, no card: 10,000 events a month (an event is a page view or an action you track) on up to 3 sites. Start free at privatusanalytics.com.
- No cookies. No cookies and no hidden IDs on your visitors' devices, and no IP addresses are kept. Laws differ by country, so check your own legal advice.
- Real people, not bots. Known bots, data centers and spam visits are left out on every plan.
- Small and fast. The script is about 3 KB when compressed.
What this package does
- A Sphinx extension that adds the tracking script to every page of your HTML docs.
- Works with any HTML theme and on Read the Docs.
- The extension only adds the browser script to your HTML pages. It never sends events from your computer or build server and sets no cookies.
You need a Privatus Analytics account to see your reports, and there is a free plan. Sign up at privatusanalytics.com.
Requirements
- Python 3.10 or later.
- Sphinx 7.2 or later.
- A Privatus Analytics site id (it starts with
pa_). You find it under Site settings, Tracking.
Install
pip install sphinx-privatus-analytics
For the full setup guide and every tracker option, see the Privatus Analytics docs.
Add the tag
Add the extension and your site id to conf.py:
extensions = [
"sphinx_privatus_analytics",
]
privatus_site_id = "pa_YOURSITEID"
Instead of writing the site id in conf.py, you can set the
PRIVATUS_SITE_ID environment variable on your build machine.
The html, dirhtml and singlehtml builders then add this to the <head> of
every page (Sphinx writes defer="defer", which means the same as defer):
<script data-site="pa_YOURSITEID" defer="defer" src="https://privatusstats.com/js/pa.js"></script>
No tag is added when there is no site id, and nothing for other builders such
as epub, latexpdf or the offline help formats. (With
privatus_queue_stub = True, web pages still get the small queue stub, see
below.)
Read the Docs
-
List the package with your other docs requirements, for example in
docs/requirements.txt:sphinx-privatus-analytics
and make sure
.readthedocs.yamlinstalls it:python: install: - requirements: docs/requirements.txt
-
Add the extension to
conf.pyas above. You can keep the site id out of the file: in your Read the Docs project, open Settings, Environment variables, and addPRIVATUS_SITE_ID. -
Read the Docs also builds a preview for each pull request. To leave those out of your reports, turn the tag off for them. Read the Docs sets
READTHEDOCS_VERSION_TYPEtoexternalfor pull request builds:import os privatus_enabled = os.environ.get("READTHEDOCS_VERSION_TYPE") != "external"
To count only your real address, set the hostnames too:
privatus_domains = ["docs.example.com"]
Read the Docs adds its own scripts to your pages (its "addons"). They do not get in the way of the tracker.
Configuration
Every setting is optional except the site id. All go in conf.py.
privatus_site_id = "pa_YOURSITEID" # or the PRIVATUS_SITE_ID environment variable
privatus_script_host = "https://privatusstats.com" # or PRIVATUS_SCRIPT_HOST
privatus_modules = ["engage", "auto", "vitals", "clicks", "ask"]
privatus_excluded_paths = ["/drafts/*"]
privatus_domains = ["docs.example.com"]
privatus_spa = "auto" # auto, history, hash or off
privatus_manual = False # True turns off the automatic pageview
privatus_enabled = None # True or False forces the tag on or off
privatus_builders = ["html", "dirhtml", "singlehtml"]
privatus_extra_attributes = {"data-mask": "/orders/*->/orders/:id"}
privatus_props = {"docs_version": "2.4"}
privatus_queue_stub = False
privatus_nonce = ""
| Setting | Default | What it does |
|---|---|---|
privatus_site_id |
PRIVATUS_SITE_ID |
The data-site attribute. Nothing is added without it |
privatus_script_host |
https://privatusstats.com |
Where /js/pa.js is served. Change it only if you host your own copy |
privatus_modules |
tracker default (engage) |
The data-modules attribute. Setting it replaces the default, so keep engage for time on page and scroll depth. [] loads none |
privatus_excluded_paths |
none | The data-exclude attribute. Path patterns the tracker never counts |
privatus_domains |
all hosts | The data-domains attribute. Count only these hostnames |
privatus_spa |
auto |
The data-spa attribute |
privatus_manual |
False |
The data-manual attribute |
privatus_enabled |
None |
False turns the tag off. True turns it on for every HTML builder. None uses privatus_builders |
privatus_builders |
html, dirhtml, singlehtml |
The builders that get the tag |
privatus_extra_attributes |
{} |
Any other data-* attribute |
privatus_props |
{} |
Page props sent with every pageview and event. A dict, or a function that gets the page name. See below |
privatus_queue_stub |
False |
Adds a small script first, so privatus.track calls made before the tracker loads are kept and sent later |
privatus_nonce |
none | A Content Security Policy nonce for the tags. Static sites rarely have one |
Each tracker attribute is explained in the script attributes reference, and the modules in Modules.
Development and test
Sphinx has no separate "development" build, so the tag is added to every
html build that has a site id. Your own visits are still not counted when you
open the pages from your disk or from localhost (for example with
sphinx-autobuild), because the tracker ignores them. To see the tracker work
there, pass data-allow-local or data-debug (see Debug
mode):
privatus_extra_attributes = {"data-debug": "true"}
To keep the tag out of local builds altogether, set the site id only on your
build server with PRIVATUS_SITE_ID, or use
privatus_enabled = os.environ.get("CI") == "true".
Leaving people out
Use privatus_excluded_paths for pages you do not want counted, like drafts.
privatus_domains makes sure only your real site is counted, not previews or
copies on other hosts.
Product analytics: see which features people use
Privatus Analytics can show which links and buttons people use in your docs, and which parts of your docs lead people to sign up. You add a few attributes to your HTML. The tracker reads them in the browser and sends the events. Your build still sends nothing.
Name events well
Use a short "Noun verb" name, like Example copied or Download started. Put
the details in props, not in the name. So write Download started with
format: pdf, not Download started as PDF. Then all downloads add up in one
place, and you can split them by format.
Events on buttons, links and forms
reStructuredText has no way to add data attributes to a link, so use a raw
block for the parts you want to track:
.. raw:: html
<a href="_static/guide.pdf" data-privatus-event="Download started" data-privatus-prop-format="pdf">Download the guide</a>
With MyST Markdown, write the HTML straight into the page. On a form, the
event is sent when the form is submitted. Use a short event name (at most 120
characters). Prop keys are lowercase letters, digits and _, and there can be
up to 30 of them.
Click maps
A click map shows where people click on a page. Give the important parts a name so they stay easy to find, even when the page changes:
.. raw:: html
<a href="https://example.com/pricing" data-privatus-click="docs.pricing-link">See plans</a>
A name may use letters, digits and _ . : -, up to 100 characters.
Page props: version, section and other groups
Page props are sent with every pageview and event on the page. Use them to split your reports by docs version, section or product.
A dict sets them for every page:
privatus_props = {"docs_version": release, "product": "api"}
A function gets the page name (like index or guides/install) and returns
the props for that page:
def privatus_props(pagename):
return {"docs_version": release, "section": pagename.split("/")[0]}
The extension then adds a small script before the tracker and points the tracker at it:
<script>window.privatusPageProps=function(){return {"docs_version":"2.4","section":"guides"}}</script>
<script data-props="privatusPageProps" data-site="pa_YOURSITEID" defer="defer" src="https://privatusstats.com/js/pa.js"></script>
Keys are made lowercase, and anything that is not a letter, a digit or _
becomes _. Numbers and true or false stay as they are, and lists stay lists
(up to 30 items). Text is cut to 500 characters. The props are escaped so they
are safe inside a script.
Sphinx only rewrites pages that changed. When you change only
privatus_props, build with -E (or make clean html) so every page gets the
new props.
Never put names, emails, user ids or anything that points to one person in props. Privatus Analytics counts visitors without any id. Put groups (like the docs version or the section), not people. The server also removes emails and long ids it finds.
Track from your own JavaScript
For things that are not a click or a submit, call the tracker from your
JavaScript (add your file with html_js_files), as the JavaScript API
docs explain:
privatus.track("Feature used", { feature: "search" })
If your code may run before the tracker has loaded, set
privatus_queue_stub = True. It adds a tiny script before the tracker that
keeps your calls in a list and sends them once the tracker is ready. The stub
is added to every web page even when the tag is off (with
privatus_enabled = False or with no site id), so your privatus.track
calls never fail while you write: they wait in the list and are never sent.
Then the extension adds the stub alone, once per page, with no tracker and no
page props, and it does not call a privatus_props function. With
privatus_queue_stub = False nothing is added when the tag is off.
See the results
- Funnels: how many people go from one step to the next.
- Click maps: where people click.
- Goals: turn an event into a goal to count conversions.
- Events: every custom event and its props.
Privacy
The script sets no cookies and stores nothing in the browser. The extension only adds HTML to your pages. Keep people out of your props and event props: no names, emails or user ids. See the privacy documentation for what is collected.
If you have a privacy policy, it should say which tools handle your visitors' data. You can paste this:
This website uses Privatus Analytics to count visits. It does not use cookies and does not follow you from one website to another. Your IP address is used for a moment to count visits and is never stored.
Change it to fit your site. The privacy policy generator makes a longer version with your site's name in it.
Development
The tests run in Docker so your local Python does not matter:
./run-docker.sh
It runs pytest (with real Sphinx builds of a tiny project with the html,
dirhtml, singlehtml, epub and text builders), Ruff, and the package
build.
The example/ folder holds a tiny site for the conformance
tests, which open it in a
real browser with the real tracker. Run them from a copy of that repo with
./run.sh ../sphinx.
Contributing
Bug reports and pull requests are welcome at https://github.com/Privatus-Analytics/sphinx.
License
MIT. See LICENSE. Privatus Analytics is a product of Two Phase LLC, Wyoming, USA.
Metadata
Release files for sphinx-privatus-analytics 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sphinx_privatus_analytics-1.0.1.tar.gz | 15.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sphinx_privatus_analytics-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.3 kB
Release files / sphinx_privatus_analytics-1.0.1.tar.gz
| Download URL | sphinx_privatus_analytics-1.0.1.tar.gz |
|---|---|
| Size | 15.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d6496bc8779b9439d44c7d62f2a9fe21a20531623e83aeadef51901e7e469eb0
|
|
BLAKE2b-256 checksum How to use checksums |
23b9e0dd5117e8514f83899afa7296b9cb34f2fd466d3a36582fd6ee12494f19
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / sphinx_privatus_analytics-1.0.1-py3-none-any.whl
| Download URL | sphinx_privatus_analytics-1.0.1-py3-none-any.whl |
|---|---|
| Size | 12.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8641b598e6c26b109b83acf36ca2862eea89230efe0e72d07c3709bb88b3edc5
|
|
BLAKE2b-256 checksum How to use checksums |
d60f7ea9c5496f10760683edbd1c484969c6fe2d8b494b8c8c69f3a502f82957
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logSecurity for sphinx-privatus-analytics 1.0.1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for sphinx_privatus_analytics-1.0.1.tar.gz
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 Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.