Skip to content

Build a Streamlit app

A working Streamlit app on top of SweatStack in under fifteen minutes. It authenticates the user, lets them pick an activity, and renders a heart-rate chart from the timeseries.

What you'll build

A Streamlit app that:

  • Authenticates with SweatStack through OAuth2
  • Fetches the user's activities
  • Renders a heart-rate chart for the selected activity
  • Lets a coach switch between the athletes they have access to
In a hurry?
uv init
uv add "sweatstack[streamlit]"

Create a new app at app.sweatstack.no/applications/new with redirect URI http://localhost:8501. Save the credentials in a .env file:

.env
SWEATSTACK_CLIENT_ID=your_client_id_here
SWEATSTACK_CLIENT_SECRET=your_client_secret_here

Create app.py:

app.py
import os

import streamlit as st
from sweatstack.streamlit import StreamlitAuth

auth = StreamlitAuth(
    client_id=os.getenv("SWEATSTACK_CLIENT_ID"),
    client_secret=os.getenv("SWEATSTACK_CLIENT_SECRET"),
    redirect_uri="http://localhost:8501",
)

with st.sidebar:
    auth.authenticate()
    if auth.is_authenticated():
        auth.select_user()

if not auth.is_authenticated():
    st.write("# Heart-rate dashboard")
    st.write("Please log in to view your activity data.")
    st.stop()

st.write("# Heart-rate dashboard")

activity = auth.select_activity()

if not activity:
    st.warning("No activities found.")
    st.stop()

st.write(f"### {activity.sport.label} on {activity.start.date()}")

data = auth.client.activities.data(activity.id)

if data is None or data.empty or "heart_rate" not in data.columns:
    st.info("No heart-rate data available for this activity.")
    st.stop()

st.line_chart(data, x="timestamp", y="heart_rate")

Run it:

uv run --env-file .env streamlit run app.py

Prerequisites

Create a SweatStack application

Register your app to get OAuth2 credentials:

  1. Go to app.sweatstack.no/applications/new.
  2. Enter a name. Leave the public profile fields for later.
  3. Set the redirect URI to http://localhost:8501.
  4. Save.
  5. Click Create Secret, name it, and copy the secret immediately. SweatStack shows it only once.

Save the credentials in a .env file:

.env
SWEATSTACK_CLIENT_ID=your_client_id_here
SWEATSTACK_CLIENT_SECRET=your_client_secret_here

Warning

Treat the client secret like a password. Add .env to .gitignore before you commit anything.

Set up the project

Create a project folder and install the dependencies:

uv init
uv add "sweatstack[streamlit]"

Add authentication

Create app.py:

app.py
import os

import streamlit as st
from sweatstack.streamlit import StreamlitAuth

auth = StreamlitAuth(
    client_id=os.getenv("SWEATSTACK_CLIENT_ID"),
    client_secret=os.getenv("SWEATSTACK_CLIENT_SECRET"),
    redirect_uri="http://localhost:8501",
)

auth.authenticate()

if not auth.is_authenticated():
    st.write("# Heart-rate dashboard")
    st.write("Please log in to view your activity data.")
    st.stop()

st.write("# Heart-rate dashboard")
st.success("Connected to SweatStack.")

What this does:

  • StreamlitAuth handles the OAuth2 flow.
  • auth.authenticate() renders a login or logout button.
  • auth.is_authenticated() checks the session state.
  • st.stop() stops the rest of the script for users who are not signed in.

Use auth.client for API calls

StreamlitAuth.client is a per-session client tied to the signed-in user. The module-level interface (import sweatstack) shares one client across every Streamlit session and can leak data between users. Always call auth.client.<method>().

Run the app

uv run --env-file .env streamlit run app.py

Streamlit opens the browser (or open http://localhost:8501 yourself). Click the login button and authorize on SweatStack. You're back in your app, signed in.

Add activity selection

Replace the st.success(...) line with an activity picker. auth.select_activity() renders a dropdown of the user's recent activities:

app.py
st.write("# Heart-rate dashboard")

activity = auth.select_activity()

if not activity:
    st.warning("No activities found.")
    st.stop()

st.write(f"### {activity.sport.label} on {activity.start.date()}")

Add a heart-rate chart

Fetch the activity's timeseries data and render it. Add this after the activity-selection block:

app.py
data = auth.client.activities.data(activity.id)

if data is None or data.empty or "heart_rate" not in data.columns:
    st.info("No heart-rate data available for this activity.")
    st.stop()

st.line_chart(data, x="timestamp", y="heart_rate")

activities.data returns a pandas DataFrame with a timestamp column and one column per metric the activity recorded (heart_rate, power, speed, and so on). Not every activity has every metric. The check above guards against missing heart-rate data.

st.line_chart is Streamlit's native chart for a single timeseries. For more control over styling, use matplotlib or altair.

Switch between users (coaches)

If your users are coaches with access to several athletes, add a user selector. Move the auth widget into the sidebar and add auth.select_user():

app.py
with st.sidebar:
    auth.authenticate()
    if auth.is_authenticated():
        auth.select_user()

For single-user apps, skip this section. Without a selection, auth.client acts as the signed-in user.

What's next

  • The data model lists every metric that can appear in activities.data.
  • The Streamlit helper page covers the full StreamlitAuth API: select_sport, select_metric, select_tag, and more.
  • For analysis across months of activities at once, see the Analyze activity data guide.