FastAPI
Add Rybbit analytics to your FastAPI app
FastAPI serves HTML through Jinja2Templates, so the snippet goes into the <head> of the base template your pages extend. Keep the site ID in a pydantic-settings class and register it as a Jinja global so every template can render the tag without passing it through each route.
The tracking script runs in a browser, so it only applies to routes that return template-rendered HTML. JSON endpoints have nothing for it to track.
Get your tracking snippet
In your Rybbit dashboard, open Site Settings → Tracking Script and copy your snippet. It looks like this:
<script src="https://app.rybbit.io/api/script.js?siteId=YOUR_SITE_ID" defer></script>YOUR_SITE_ID is the numeric ID of your site. If you self-host Rybbit, app.rybbit.io is the domain of your own instance.
Add the snippet to FastAPI
Configure the site ID
Install pydantic-settings and jinja2 if you have not already:
pip install pydantic-settings jinja2Define the settings, reading from the environment and an optional .env file:
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
rybbit_instance_url: str = "https://app.rybbit.io"
rybbit_site_id: str = ""
settings = Settings()RYBBIT_INSTANCE_URL=https://app.rybbit.io
RYBBIT_SITE_ID=YOUR_SITE_IDRegister the values with Jinja
Jinja2Templates exposes its environment as templates.env, so add the settings as globals once, next to where you create the templates object:
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from config import settings
app = FastAPI()
templates = Jinja2Templates(directory="templates")
templates.env.globals["rybbit_instance_url"] = settings.rybbit_instance_url
templates.env.globals["rybbit_site_id"] = settings.rybbit_site_id
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse(request=request, name="index.html")Add the tag to your base template
Open the template your pages extend, usually templates/base.html, and add the tag inside <head>:
<head>
<meta charset="utf-8">
<title>{% block title %}My App{% endblock %}</title>
{% if rybbit_site_id %}
<script src="{{ rybbit_instance_url }}/api/script.js?siteId={{ rybbit_site_id }}" defer></script>
{% endif %}
</head>The {% if %} guard skips the tag when RYBBIT_SITE_ID is empty, so local development is not tracked unless you set the variable. Templates that do not extend the base need the same tag in their own <head>.
Verify installation
Open your live site in a new tab and click through a few pages. Within a few seconds the pageviews appear in the Rybbit dashboard.
If nothing shows up:
- View the page source and search for
script.js?siteId=to confirm the snippet is on the page. - Open the browser Network tab and check that
script.jsreturns200and thatPOSTrequests go to/api/track. - Disable ad blockers, or set up a proxy so the script loads from your own domain.
- See the script troubleshooting guide for other common causes.
Troubleshooting
- Tag missing:
Settings()reads the environment when the module is imported, so restart Uvicorn (or your process manager) after changingRYBBIT_SITE_ID. - Several
Jinja2Templatesinstances: globals are per environment. If routers create their ownJinja2Templates, set the globals on each, or share one instance from a module. - Content Security Policy: if middleware sets one, add your Rybbit host to
script-srcandconnect-src, or proxy the script through your own domain. - Self-hosted instance: set
RYBBIT_INSTANCE_URLto your own domain without a trailing slash; the template appends the script path to it.
Next steps
- Track custom events such as signups, purchases and button clicks.
- Identify users to connect sessions to accounts.
- Proxy the script through your own domain to bypass ad blockers.
- Script attributes let you skip or mask URLs and tag events.