HSL Kaupunkipyörä Exporter
Parse your HSL City Bike ride history and export each ride as a Strava-compatible TCX or GPX file.
Installation
Requires Python 3.13+. The easiest way to run the tool is via uvx:
uvx hsl-kaupunkipyora-exporter rides.txt
Or install with pip:
pip install hsl-kaupunkipyora-exporter
hsl-kaupunkipyora-exporter rides.txt
Web App
A browser-based version is available at the GitHub Pages site. Upload your ride history HTML file or paste the text, pick your options, and download the resulting files — no installation required. The web app runs entirely in your browser using Pyodide.
The browser build reuses the same Python modules as the CLI — it installs the project wheel into Pyodide and calls the
shared RideHistoryParser and writers from a thin adapter. Run it locally with:
just serve-web # builds the wheel and serves http://localhost:8000/
AI Agent Skill
This repo ships a skill that lets AI Agents like Claude Code run the exporter from
natural language, mapping requests like "export my HSL rides to GPX" to the right CLI flags, and offering to fetch the
ride-history page itself via a connected browser tool (e.g. Claude in Chrome) when you agree to it. The instructions
reference Claude Code's own tools (ToolSearch, Bash, Write), so treat it as Claude-Code-specific for now even
though it's installed via skills, which also supports other agents. skills/
in this repo is the source of truth; installing copies it into .claude/skills/ in your own project. Install it into
your project with for example:
npx skills add nikosavola/hsl-kaupunkipyora-exporter
Usage
- Open your ride history at https://www.hsl.fi/en/my-information/citybikes/ride-history.
- Save the page as HTML (
Ctrl+S) or copy-paste the visible text into a.txtfile. - Run the exporter:
uvx hsl-kaupunkipyora-exporter rides.html
Path Modes
The exporter supports three distinct modes for handling geographic data:
- Summary Only (Default): Exports a TCX file containing the exact distance and duration reported by HSL, but no GPS trackpoints. This is the most accurate way to record kilometers in Strava without guessing the path.
- Linear Path (
--linear): Includes a simple two-point straight line between the departure and return stations. Useful if you want a basic map visualization. - Routed Path (
--use-route): Fetches the suggested cycling route from the Digitransit API. This provides a realistic path on the map and preserves HSL distance data (when using TCX). Requires a free API key.
Options
| Flag | Description |
|---|---|
--output-dir DIR |
Directory to write files into (default: ./tcx_output) |
--format FMT |
Export format: tcx (default) or gpx. |
--linear |
Include a straight-line path between stations |
--use-route |
Use suggested HSL cycling route instead of a straight line |
--api-key KEY |
Digitransit API key (alternative to DIGITRANSIT_API_KEY env var) |
--refresh-stations |
Force re-download of the bike station coordinate list |
--cache-ttl-days DAYS |
Refresh station cache after this many days (default: 30, env var below) |
-n, --dry-run |
Preview which files would be written or skipped without writing |
--zip |
Bundle all exported files into a single rides.zip archive |
--merge |
Write one file with all rides (multi-track GPX / multi-activity TCX) |
-v, --verbose |
Enable verbose/debug logging |
The station cache TTL can also be set via the HSL_KAUPUNKIPYORA_CACHE_TTL_DAYS environment variable.
TCX vs GPX
While GPX is the most common format, it does not support an explicit "total distance" override. Strava calculates distance based on the GPS points provided.
TCX is the default and recommended format because it allows the tool to tell Strava exactly how many kilometers the ride was, regardless of the GPS path.
# Export as TCX with a straight line between stations
uvx hsl-kaupunkipyora-exporter rides.txt --linear
Routed Path Setup
To use the actual cycling route suggested by HSL, you need a Digitransit API key:
- Register for a free key at Digitransit Developer Portal.
- Provide the key via the
--api-keyflag or theDIGITRANSIT_API_KEYenvironment variable.
uvx hsl-kaupunkipyora-exporter rides.txt --use-route --api-key your_key_here
Kilometrikisa
This is a convenient way to add your Alepa Fillari kilometers to Kilometrikisa by importing your rides into Strava first.
How it Works
graph TD
classDef input fill:#4A90D9,stroke:#2171B5,color:#fff
classDef parser fill:#E67E22,stroke:#CA6F1E,color:#fff
classDef lookup fill:#8E44AD,stroke:#7D3C98,color:#fff
classDef decision fill:#FDEBD0,stroke:#E67E22,color:#000
classDef skip fill:#E74C3C,stroke:#CB4335,color:#fff
classDef pathNode fill:#A9DFBF,stroke:#27AE60,color:#000
classDef api fill:#76D7C4,stroke:#16A085,color:#000
classDef writer fill:#5DADE2,stroke:#2980B9,color:#fff
classDef output fill:#2ECC71,stroke:#27AE60,color:#000
A[HSL Ride History<br/>HTML or Text]:::input --> B(RideHistoryParser<br/>Parses HTML & plain-text<br/>ride history files):::parser
B --> C{StationLookup<br/>Resolves station names<br/>to GPS coordinates}:::lookup
C -- Coordinates Found --> D{Path Mode?}:::decision
C -- Not Found --> E[Skip Ride]:::skip
D -- Default --> F[Summary Only<br/>Exact HSL distance &<br/>duration, no GPS points]:::pathNode
D -- "--linear" --> G[Linear Path<br/>Straight line between<br/>departure & return stations]:::pathNode
D -- "--use-route" --> H[fetch_route<br/>Fetches cycling route<br/>via Digitransit GraphQL API]:::api
F --> I(TCX/GPX Writer<br/>Serialises rides into<br/>Strava-compatible files):::writer
G --> I
H --> I
I --> J[Strava-compatible<br/>TCX or GPX files]:::output
Development
Use just to run tasks.
git clone https://github.com/nikosavola/hsl-kaupunkipyora-exporter.git
cd hsl-kaupunkipyora-exporter
just install
just test
Metadata
Release files for hsl-kaupunkipyora-exporter 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hsl_kaupunkipyora_exporter-0.1.2.tar.gz | 29.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hsl_kaupunkipyora_exporter-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 64.6 kB
Release files / hsl_kaupunkipyora_exporter-0.1.2.tar.gz
| Download URL | hsl_kaupunkipyora_exporter-0.1.2.tar.gz |
|---|---|
| Size | 29.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ce26ee793f299cf49e8df3ed5f911861b8783597778043e1a99dfff17c0e36d
|
|
BLAKE2b-256 checksum How to use checksums |
f63490b2270af415ed3a097b31582aca327489dcd7c8de4b4d92a723b3feacd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Aug 12, 2026.
Transparency logRelease files / hsl_kaupunkipyora_exporter-0.1.2-py3-none-any.whl
| Download URL | hsl_kaupunkipyora_exporter-0.1.2-py3-none-any.whl |
|---|---|
| Size | 35.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fe778b8e791b56e68a6e2d332d7433578381bfd5d6b34c669fafe1684ce4a219
|
|
BLAKE2b-256 checksum How to use checksums |
c20ad80dbf83f861534d8540d148d614468d35d4b9a45bfdb449a4570503397e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Aug 12, 2026.
Transparency log