intervals-icu-pause-split
Splits an intervals.icu activity into moving legs and pauses, so a café stop, a lunch break on a hike or a night in a hotel no longer drags down the averages of the leg it sits in. Works for rides, hikes, walks and runs alike.
uvx intervals-icu-pause-split https://intervals.icu/activities/i87942121 --api-key=YOUR_KEY
That is the whole install. uvx fetches it, runs it, and throws it away again.
Pass the activity id or just paste its URL.
What it looks like
A gravel ride on Rügen. intervals.icu had detected 97 intervals on its own: power surges of a few seconds, with "recovery" in between, which says nothing about how the ride actually went.
❯ uvx intervals-icu-pause-split --edges 5km https://intervals.icu/activities/i87942121
╭────────────────── intervals.icu · i87942121 ──────────────────╮
│ Sassnitz Gravel/Offroad-Radfahren │
│ GravelRide · Sat 10:58, 2025-05-17 · 96.9 km · 5:33 h elapsed │
╰───────────────────────────────────────────────────────────────╯
Pause: at least 5 min below 3 km/h or not recording; stops less than 1 min apart are merged
Warmup: first 5 km · Cooldown: last 5 km
Speed │▄▅▆▇▇▇▇▆▅▃▄▄▅▆▆▄█▇███▅▇▇▁ ▅▇▆▇▇▇▇▇▇▆▇▆▇▆▆▆▄▆▄ ▄▇▇▆▇█▇▆▇▇▇█▇▇█▇▆▇▇▆▅▄▅▆▆▅█▅▅▅▅▇▇▆▅▆██▇██│
Now │╎░╎╎╎╎█╎█╎╎█░░█████░░█░░░░░░░░░░░░░░░░░░░░░█░░░░░█╎░░░░░░█░░░█░░░░░░╎░█░╎█░░░░░╎█╎░╎╎╎█░│
New │████████████████████████░░░█████████████████░░░█████████████████████████████████████████│
└┴───────────────┴──────────────┴───────────────┴───────────────┴───────────────┴────────┘
11:00 12:00 13:00 14:00 15:00 16:00
██ moving ░ pause ╎ shorter pause ▁…█ speed up to 27 km/h · one column ≈ 3 min
Now 97 intervals, 88 not shown
# Type Label Start Duration Distance km/h W
1 RECOVERY — 10:58 1 min 0.4 km — —
2 WORK — 11:00 3 min 0.7 km 11.3 255
3 RECOVERY — 11:04 5 min 1.5 km — —
4 WORK — 11:09 6 s 0.0 km 17.3 510
5 RECOVERY — 11:09 1 min 0.6 km — —
6 WORK — 11:11 5 s 0.0 km 20.8 418
… … … … … … … …
95 RECOVERY — 16:23 1 min 0.7 km — —
96 WORK — 16:25 10 s 0.1 km 27.1 468
97 RECOVERY — 16:25 6 min 2.8 km — —
New
# Type Label Start Duration Distance km/h W
1 WORK Warmup 10:58 17 min 5.0 km 17.1 195
2 WORK Leg 1 11:16 1:13 h 21.0 km 19.6 133
3 RECOVERY — 12:29 10 min 0.0 km — —
4 WORK Leg 2 12:40 1:06 h 22.0 km 19.8 148
5 RECOVERY — 13:47 11 min 0.0 km — —
6 WORK Leg 3 13:58 2:20 h 43.9 km 19.4 140
7 WORK Cooldown 16:19 12 min 5.0 km 23.5 99
5 legs · 5:11 h · 96.9 km | 2 pauses · 21 min
Replace the 97 intervals with these 7? [y/n] (n):
Now is what the activity has, New what it will get: 5 km out of town and
5 km back in as warmup and cooldown, three legs, and the two real stops
between them. A long Now list is cut down to its first and last few rows;
the timeline above it always shows everything.
The timeline is drawn to scale. A pause shorter than one column still shows up,
as a thin ╎, so a short stop stays visible even on a two-day bikepacking
trip without looking longer than it was. In the terminal, consecutive legs alternate between
green and cyan.
How pauses are found
- Standing still: slower than
--stop-speed: 1 km/h on foot (hike, walk, run), 3 km/h for everything else. A steep climb on foot easily drops below 3 km/h, while GPS drift at a real stop stays under 1 km/h. - Not recording: auto-pause leaves no samples, just a jump in the time stream. That counts as standing still too.
- Merged: stops with less than
--merge(60 s) of moving between them are one pause. Wheeling the bike from the café back to the road does not split it in two. - Long enough: a pause needs
--min-pause(5 min) of actual standstill. The moving between merged stops does not count towards that, so stop-and-go through town stays moving.
Getting out of town and back in can be split off as well: --edges 10m turns
the first and last ten minutes into Warmup and Cooldown intervals. Give a
distance instead with --edges 5km, or two values to set start and end
separately: --edges 3km,10m. A distance ignores the time spent at traffic
lights, so it tends to match "until I'm out of town" better.
What gets written
Only the moving legs, as labelled WORK intervals. intervals.icu fills the gaps between them with RECOVERY intervals by itself. It does not accept RECOVERY intervals through the API (they come back as WORK), and it drops any label on them, which is why the pauses in the table have none.
Writing replaces all intervals on the activity. Before that, the current
ones are saved to ~/.local/state/intervals-icu-pause-split/backups/ (or
$XDG_STATE_HOME), and the tool prints the command that puts them back:
uvx intervals-icu-pause-split i87942121 --restore ~/.local/state/intervals-icu-pause-split/backups/i87942121-20261006-044729.json
Usage
uvx intervals-icu-pause-split i87942121 # preview, then ask before writing
uvx intervals-icu-pause-split i87942121 --dry-run # preview, never write
uvx intervals-icu-pause-split i87942121 --min-pause 10m # only the longer stops
uvx intervals-icu-pause-split i87942121 --edges 10m # warmup and cooldown of 10 minutes
uvx intervals-icu-pause-split i87942121 --edges 3km,10m # 3 km warmup, 10 minute cooldown
uvx intervals-icu-pause-split i87942121 --label Etappe # "Etappe 1", "Etappe 2", …
uvx intervals-icu-pause-split i87942121 --yes # write without asking
Durations are written as 90s, 5m or 1.5h; a bare number means minutes.
Distances for --edges are written in km (0.5km, not 500m, because m
already means minutes).
Nothing is written without a confirmation prompt unless you pass --yes.
Credentials
The API key sits at the bottom of https://intervals.icu/settings. Provide it
by flag, by environment variable, or in a .env file next to where you run it:
INTERVALS_ICU_API_KEY=your_key_here
Options
| Flag | Default | Meaning |
|---|---|---|
--api-key |
$INTERVALS_ICU_API_KEY |
Your intervals.icu API key |
--min-pause |
5m |
Minimum standstill for a pause |
--merge |
60s |
Stops with less moving than this in between count as one pause |
--stop-speed |
1 on foot, 3 otherwise |
Below this speed (km/h) you count as standing still |
--edges |
Split off the first and last stretch as Warmup and Cooldown, as a duration (10m) or a distance (3km); one value for both ends or two, comma-separated, for start and end |
|
--label |
Leg |
Label prefix for the moving intervals |
--width |
terminal width | Width of the timeline |
--restore |
Put back the intervals from a backup file | |
--dry-run |
Show the preview, never write | |
--yes |
Skip the confirmation prompt |
Use it as a library
Everything the command line does is available from Python, for example for a web service that lets people pick an activity, shows the preview and writes it when they confirm:
from intervals_icu_pause_split import Client, Options, apply, plan, plan_restore
with Client(access_token=token) as client: # or Client(api_key)
p = plan(client, "https://intervals.icu/activities/i87942121", Options.parse(edges="5km"))
preview = p.to_dict() # JSON-ready, to draw your own preview
if p.has_changes:
result = apply(client, p)
save_somewhere(result.previous) # the intervals before the write
# Later, to undo:
apply(client, plan_restore(client, "i87942121", load_from_somewhere()))
plan()only reads. It takes an activity id or URL and returns the current and the proposed intervals;apply()writes the proposal.Options.parse()takes the same strings as the command line ("5m","3km,10m") and raisesValueErrorwith a readable message, handy for form input.Options(...)takes seconds and km/h directly.to_dict()has the activity, both interval lists with start time, duration, distance, average speed and power per interval, a summary, and the speed over time in equal slices for drawing a chart.- Errors are exceptions, never an exit or console output:
AuthError(401/403),NotFoundError(404),IntervalsError(any other HTTP error, or the network), andDataError(the activity can't be split as asked). All of them derive fromPauseSplitError. Clienttakes an API key, or an OAuth access token for a service acting on behalf of other athletes. It is synchronous; in FastAPI, declare the endpoints withdefrather thanasync defso they run in the threadpool.
The lower-level pieces (find_pauses, build_segments, Streams, Segment)
are plain functions and dataclasses that work on lists, without any network.
Development
just test # run the suite with coverage
just coverage # the same, as a browsable HTML report
just check i87942121 # dry run against your own account
just run i87942121 # run for real, with the confirmation prompt
just build # build the distribution
Releases are cut by tagging:
git tag v0.1.0 && git push --tags
CI runs the tests, refuses a tag that disagrees with the version in
pyproject.toml, and publishes to PyPI through Trusted Publishing, so no API
token is stored in the repository.
License
MIT
Metadata
Release files for intervals-icu-pause-split 0.2.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 | Provenance | Actions |
|---|---|---|---|---|
| intervals_icu_pause_split-0.2.0.tar.gz | 51.9 kB | With provenance | Details for intervals_icu_pause_split-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| intervals_icu_pause_split-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for intervals_icu_pause_split-0.2.0-py3-none-any.whl |
Total release size: 75.4 kB
Release files / intervals_icu_pause_split-0.2.0.tar.gz
| Download URL | intervals_icu_pause_split-0.2.0.tar.gz |
|---|---|
| Size | 51.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d19b043c73297be7b0c36f214441b0a59bf80fc8dd6ea2ca42dfe47f537572d1
|
|
BLAKE2b-256 checksum How to use checksums |
8935f9957cc53418a8503d8af4578de762a1e40ed49782b6460f778e99e2fe7f
|
| 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 Oct 6, 2026.
Transparency logRelease files / intervals_icu_pause_split-0.2.0-py3-none-any.whl
| Download URL | intervals_icu_pause_split-0.2.0-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d2ba00056736fee0707402929cd384d6998a95af4672edfada1e5af977163b16
|
|
BLAKE2b-256 checksum How to use checksums |
9646ff4786ac392f0d71f7535af6237c33cd0154028fa5443e0ff2d3a0411fa6
|
| 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 Oct 6, 2026.
Transparency logSecurity for intervals-icu-pause-split 0.2.0
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
Both files in this release have been uploaded with provenance metadata.
Attestation for intervals_icu_pause_split-0.2.0.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 6, 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.