Skip to content

Activities

client.activities: /api/v1/activities/...

Bases: Resource

Activities: the summaries, their time series, and analyses over one or many.

app_metadata cached property

This app's metadata on activities: set(activity_id, data=...) and delete.

longitudinal cached property

Data and analyses across many activities: data, mean_max and awd.

awd(activity_id, *, metric=None, output=None)

awd(activity_id: str, *, metric: IntensityMetric | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
awd(activity_id: str, *, metric: IntensityMetric | None = None, output: Literal['pandas']) -> pd.DataFrame
awd(activity_id: str, *, metric: IntensityMetric | None = None, output: Literal['polars']) -> pl.DataFrame
awd(activity_id: str, *, metric: IntensityMetric | None = None, output: Literal['arrow']) -> pa.Table
awd(activity_id: str, *, metric: IntensityMetric | None = None, output: Literal['bytes']) -> bytes

Retrieves an activity's accumulated work duration (AWD) curve.

Endpoint: GET /api/v1/activities/{activity_id}/accumulated-work-duration

AWD is how long the activity spent at or above each intensity: the activity's samples sorted by intensity.

Parameters:

  • activity_id (str) –

    The activity's ID.

  • metric (IntensityMetric | None, default: None ) –

    "power" or "speed". Defaults to power for cycling, speed otherwise.

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes". Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame with the metric value and duration as columns.

Raises:

Examples:

from sweatstack import Client

client = Client()
latest = client.activities.latest(sport="cycling")
awd = client.activities.awd(latest.id, metric="power")

backfill_status()

Retrieves how far the activity backfill has reached: the first update of the stream.

Endpoint: GET /api/v1/activities/backfill-status

Returns:

  • BackfillStatus ( BackfillStatus ) –

    backfill_loaded_until, the earliest date loaded so far.

Raises:

  • ValueError –

    If the stream ends without an update.

  • SweatStackAPIError –

    If the API request fails.

Examples:

from sweatstack import Client

client = Client()
print(client.activities.backfill_status().backfill_loaded_until)

data(activity_id, *, segmentation_on=None, metrics=None, output=None)

data(activity_id: str, *, segmentation_on: IntensityMetric | None = None, metrics: MetricParam | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
data(activity_id: str, *, segmentation_on: IntensityMetric | None = None, metrics: MetricParam | None = None, output: Literal['pandas']) -> pd.DataFrame
data(activity_id: str, *, segmentation_on: IntensityMetric | None = None, metrics: MetricParam | None = None, output: Literal['polars']) -> pl.DataFrame
data(activity_id: str, *, segmentation_on: IntensityMetric | None = None, metrics: MetricParam | None = None, output: Literal['arrow']) -> pa.Table
data(activity_id: str, *, segmentation_on: IntensityMetric | None = None, metrics: MetricParam | None = None, output: Literal['bytes']) -> bytes

Retrieves an activity's time series, one row per sample.

Endpoint: GET /api/v1/activities/{activity_id}/data

Parameters:

  • activity_id (str) –

    The activity's ID.

  • segmentation_on (IntensityMetric | None, default: None ) –

    Downsample with AISC (Adaptive Intensity Segmentation Codec), keyed on "power" or "speed". Omit for every sample.

  • metrics (MetricParam | None, default: None ) –

    One metric or a list. Defaults to the server's selection.

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes" (the raw parquet response). Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame with a timezone-aware UTC timestamp column, timestamp_local, and

  • DataFrame | DataFrame | Table | bytes –

    one column per metric.

Raises:

Examples:

from sweatstack import Client

client = Client()
latest = client.activities.latest()
df = client.activities.data(latest.id, metrics=["power", "heart_rate"], output="polars")

latest(*, sport=None)

Retrieves the most recent activity, or None if there is none.

Endpoint: GET /api/v1/activities/latest

Parameters:

  • sport (Sport | str | None, default: None ) –

    Only consider this sport. A parent sport ("cycling") also matches its sub-sports.

Returns:

  • ActivityDetails | None –

    ActivityDetails | None: The most recent matching activity, or None when the

  • ActivityDetails | None –

    user has no matching activity.

Raises:

Examples:

from sweatstack import Client

client = Client()
latest = client.activities.latest(sport="cycling")
if latest is not None:
    data = client.activities.data(latest.id)

list(*, start=None, end=None, sport=None, tags=None, limit=100, offset=0, output=None)

list(*, start: date | None = None, end: date | None = None, sport: SportParam | None = None, tags: TagParam | None = None, limit: int = 100, offset: int = 0, output: Literal['models'] | None = None) -> builtins.list[ActivitySummary]
list(*, start: date | None = None, end: date | None = None, sport: SportParam | None = None, tags: TagParam | None = None, limit: int = 100, offset: int = 0, output: Literal['pandas']) -> pd.DataFrame
list(*, start: date | None = None, end: date | None = None, sport: SportParam | None = None, tags: TagParam | None = None, limit: int = 100, offset: int = 0, output: Literal['polars']) -> pl.DataFrame
list(*, start: date | None = None, end: date | None = None, sport: SportParam | None = None, tags: TagParam | None = None, limit: int = 100, offset: int = 0, output: Literal['arrow']) -> pa.Table

Lists activities, newest first.

Endpoint: GET /api/v1/activities/

Parameters:

  • start (date | None, default: None ) –

    Only activities on or after this date.

  • end (date | None, default: None ) –

    Only activities on or before this date.

  • sport (SportParam | None, default: None ) –

    One sport or a list; an activity matches any of them. A parent sport ("cycling") also matches its sub-sports.

  • tags (TagParam | None, default: None ) –

    One tag or a list; an activity must have all of them.

  • limit (int, default: 100 ) –

    How many activities to return. The client pages through the endpoint until it has this many or there are no more.

  • offset (int, default: 0 ) –

    How many of the newest matching activities to skip.

  • output (ListOutput | None, default: None ) –

    "models" (default), "pandas", "polars" or "arrow". Overrides the client-level default for this call.

Returns:

  • list[ActivitySummary] | DataFrame | DataFrame | Table –

    ActivitySummary objects, or a frame with one row per activity. Nested fields are

  • list[ActivitySummary] | DataFrame | DataFrame | Table –

    flattened to dotted columns in pandas and typed structs in Polars and Arrow.

Raises:

Examples:

from datetime import date

from sweatstack import Client

client = Client()
rides = client.activities.list(sport="cycling", start=date(2026, 1, 1), limit=20)
frame = client.activities.list(output="polars")

mean_max(activity_id, *, metric, durations=None, output=None)

mean_max(activity_id: str, *, metric: IntensityMetric, durations: Sequence[int] | Literal['all'] | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
mean_max(activity_id: str, *, metric: IntensityMetric, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['pandas']) -> pd.DataFrame
mean_max(activity_id: str, *, metric: IntensityMetric, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['polars']) -> pl.DataFrame
mean_max(activity_id: str, *, metric: IntensityMetric, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['arrow']) -> pa.Table
mean_max(activity_id: str, *, metric: IntensityMetric, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['bytes']) -> bytes

Retrieves an activity's mean-max curve: the best average for each duration.

Endpoint: GET /api/v1/activities/{activity_id}/mean-max

Parameters:

  • activity_id (str) –

    The activity's ID.

  • metric (IntensityMetric) –

    "power" or "speed".

  • durations (Sequence[int] | Literal['all'] | None, default: None ) –

    In seconds. None (default) for 19 durations from 1 s to 6 h, "all" for the full grid (1 s steps to 3 min, then 5, 10, 30 and 60 s steps), or a list. Durations the activity did not last are left out.

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes". Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame, one row per duration: duration, the metric (W or m/s) and start,

  • DataFrame | DataFrame | Table | bytes –

    the UTC time the best effort began. The curve can rise again at longer durations

  • DataFrame | DataFrame | Table | bytes –

    (intermittent efforts); it is returned as it is.

Raises:

Examples:

from sweatstack import Client

client = Client()
latest = client.activities.latest(sport="cycling")
curve = client.activities.mean_max(latest.id, metric="power", durations=[5, 60, 300, 1200])

retrieve(activity_id)

Retrieves one activity with its summary and laps.

Endpoint: GET /api/v1/activities/{activity_id}

Parameters:

  • activity_id (str) –

    The activity's ID.

Returns:

Raises:

Examples:

from sweatstack import Client

client = Client()
activity = client.activities.retrieve("act_123")
print(activity.sport, activity.start)

upload(files)

Uploads activity files (FIT or CSV); they are processed in the background.

Endpoint: POST /api/v1/activities/upload

FIT files carry their sport. CSV files need a sport column with an Open Sport Taxonomy code (cycling.road) and a timestamp column with offset-aware ISO 8601 datetimes (...+02:00 or ...Z); naive timestamps are rejected during processing.

Parameters:

  • files (str | Path | Sequence[str | Path]) –

    One path or a list of paths.

Returns:

  • list[SourceResponse] –

    list[SourceResponse]: One entry per file. status starts as "processing";

  • list[SourceResponse] –

    retrieve the activities by ID once processed.

Raises:

Examples:

from sweatstack import Client

client = Client()
sources = client.activities.upload(["ride.fit", "run.fit"])
print([source.status for source in sources])

watch_backfill_status(*, auto_reconnect=False)

Streams how far the activity backfill of a newly connected user has reached.

Endpoint: GET /api/v1/activities/backfill-status

Yields an update every few seconds. The server closes the stream after 60 seconds; with auto_reconnect=True the generator reconnects and keeps yielding until you stop iterating.

Parameters:

  • auto_reconnect (bool, default: False ) –

    Reconnect after the server closes the stream or the connection drops.

Yields:

  • BackfillStatus ( BackfillStatus ) –

    backfill_loaded_until, the earliest date loaded so far.

Raises:

Examples:

from sweatstack import Client

client = Client()
for status in client.activities.watch_backfill_status():
    print(status.backfill_loaded_until)

Bases: Resource

Data and analyses across many activities: /api/v1/activities/longitudinal-*.

awd(*, sport, metric, start=None, end=None, output=None)

awd(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
awd(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, output: Literal['pandas']) -> pd.DataFrame
awd(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, output: Literal['polars']) -> pl.DataFrame
awd(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, output: Literal['arrow']) -> pa.Table
awd(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, output: Literal['bytes']) -> bytes

Retrieves accumulated work duration (AWD) across a date range, at four intensities.

Endpoint: GET /api/v1/activities/longitudinal-accumulated-work-duration

Beta: the server marks this endpoint as in development.

Parameters:

  • sport (SportParam) –

    One sport or a list.

  • metric (IntensityMetric) –

    "power" or "speed".

  • start (date | None, default: None ) –

    First date of the range.

  • end (date | None, default: None ) –

    Last date of the range. Defaults to today.

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes". Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame with the AWD at the max (highest daily), hard, medium and easy levels.

Raises:

Examples:

from datetime import date

from sweatstack import Client

client = Client()
awd = client.activities.longitudinal.awd(
    sport="cycling", metric="power", start=date(2026, 1, 1)
)

data(*, sport, start, end=None, metrics=None, segmentation_on=None, output=None)

data(*, sport: SportParam, start: date, end: date | None = None, metrics: MetricParam | None = None, segmentation_on: IntensityMetric | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
data(*, sport: SportParam, start: date, end: date | None = None, metrics: MetricParam | None = None, segmentation_on: IntensityMetric | None = None, output: Literal['pandas']) -> pd.DataFrame
data(*, sport: SportParam, start: date, end: date | None = None, metrics: MetricParam | None = None, segmentation_on: IntensityMetric | None = None, output: Literal['polars']) -> pl.DataFrame
data(*, sport: SportParam, start: date, end: date | None = None, metrics: MetricParam | None = None, segmentation_on: IntensityMetric | None = None, output: Literal['arrow']) -> pa.Table
data(*, sport: SportParam, start: date, end: date | None = None, metrics: MetricParam | None = None, segmentation_on: IntensityMetric | None = None, output: Literal['bytes']) -> bytes

Retrieves the time series of every matching activity in a date range, concatenated.

Endpoint: GET /api/v1/activities/longitudinal-data

Responses are cached on disk when :func:sweatstack.enable_cache is on.

Parameters:

  • sport (SportParam) –

    One sport or a list. Required by the API.

  • start (date) –

    First date of the range.

  • end (date | None, default: None ) –

    Last date of the range. Defaults to today.

  • metrics (MetricParam | None, default: None ) –

    One metric or a list. Defaults to duration, power, heart_rate and speed.

  • segmentation_on (IntensityMetric | None, default: None ) –

    Downsample with AISC, keyed on "power" or "speed".

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes". Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame, one row per sample, with timestamp (UTC), timestamp_local,

  • DataFrame | DataFrame | Table | bytes –

    activity_id and sport columns plus one column per metric.

Raises:

Examples:

from datetime import date

from sweatstack import Client

client = Client()
season = client.activities.longitudinal.data(
    sport="cycling", start=date(2026, 1, 1), metrics=["power"], output="polars"
)

mean_max(*, sport, metric, start=None, end=None, after=None, durations=None, output=None)

mean_max(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, after: float | Sequence[float] | None = None, durations: Sequence[int] | Literal['all'] | None = None, output: None = None) -> pd.DataFrame | pl.DataFrame
mean_max(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, after: float | Sequence[float] | None = None, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['pandas']) -> pd.DataFrame
mean_max(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, after: float | Sequence[float] | None = None, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['polars']) -> pl.DataFrame
mean_max(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, after: float | Sequence[float] | None = None, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['arrow']) -> pa.Table
mean_max(*, sport: SportParam, metric: IntensityMetric, start: date | None = None, end: date | None = None, after: float | Sequence[float] | None = None, durations: Sequence[int] | Literal['all'] | None = None, output: Literal['bytes']) -> bytes

Retrieves the mean-max curve across every matching activity in a date range.

Endpoint: GET /api/v1/activities/longitudinal-mean-max

Responses are cached on disk when :func:sweatstack.enable_cache is on.

Parameters:

  • sport (SportParam) –

    One sport or a list.

  • metric (IntensityMetric) –

    "power" or "speed".

  • start (date | None, default: None ) –

    First date of the range.

  • end (date | None, default: None ) –

    Last date of the range. Defaults to today.

  • after (float | Sequence[float] | None, default: None ) –

    One or more fatigue states (at most 5). For each, the curve is computed over the part of every activity after that much accumulated work (kJ, for power) or distance (m, for speed; experimental), then enveloped across activities. The frame gains an after column. The date range is capped at one year.

  • durations (Sequence[int] | Literal['all'] | None, default: None ) –

    In seconds. None (default) for 19 durations from 1 s to 6 h, "all" for the full grid, or a list.

  • output (FrameOutput | None, default: None ) –

    "pandas", "polars", "arrow" or "bytes". Defaults to the installed frame library, Polars if both are.

Returns:

  • DataFrame | DataFrame | Table | bytes –

    A frame, one row per duration (and per after value): duration, the metric,

  • DataFrame | DataFrame | Table | bytes –

    start (UTC), and the activity_id and sport that set it.

Raises:

Examples:

from datetime import date

from sweatstack import Client

client = Client()
curve = client.activities.longitudinal.mean_max(
    sport="cycling", metric="power", start=date(2026, 1, 1), after=[0, 1000]
)

Bases: _RecordAppMetadata

This app's metadata on activitys. Requires an app token.

delete(activity_id)

Deletes this app's metadata from an activity.

Endpoint: DELETE /api/v1/activities/{activity_id}/app-metadata

Parameters:

  • activity_id (str) –

    The activity's ID.

Raises:

Examples:

from sweatstack import Client

client = Client(api_key="app-user-token")
client.activities.app_metadata.delete("act_123")

set(activity_id, *, data)

Replaces this app's metadata on an activity.

Endpoint: PUT /api/v1/activities/{activity_id}/app-metadata

The whole dict is replaced; there is no merge. Each app sees only its own metadata, and it appears as app_metadata on the activity when read with an app token.

Parameters:

  • activity_id (str) –

    The activity's ID.

  • data (dict[str, Any]) –

    Any JSON-serialisable dict, at most 1 KB and 32 levels deep.

Raises:

Examples:

from sweatstack import Client

client = Client(api_key="app-user-token")  # e.g. user.client in FastAPI
client.activities.app_metadata.set("act_123", data={"reviewed": True})