{"id":75,"date":"2026-06-04T15:59:49","date_gmt":"2026-06-04T14:59:49","guid":{"rendered":"https:\/\/wagenhofer.ch\/me\/strava-api-docs\/"},"modified":"2026-06-04T16:11:56","modified_gmt":"2026-06-04T15:11:56","slug":"strava-api-docs","status":"publish","type":"page","link":"https:\/\/wagenhofer.ch\/me\/strava-api-docs\/","title":{"rendered":"Strava API Documentation"},"content":{"rendered":"<style>\n.doc-page { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #1a1a2e; line-height: 1.7; max-width: 860px; margin: 0 auto; padding: 40px 20px 80px; }\n.doc-page h1 { font-size: 32px; font-weight: 800; margin: 0 0 8px; color: #1a1a2e; border: none; padding: 0; }\n.doc-page h2 { font-size: 20px; font-weight: 700; margin: 48px 0 16px; color: #1a1a2e; border-bottom: 2px solid #e0e7ff; padding-bottom: 8px; }\n.doc-page h3 { font-size: 16px; font-weight: 700; margin: 28px 0 10px; color: #374151; border: none; padding: 0; }\n.doc-page p { margin: 0 0 16px; color: #374151; }\n.doc-page ul { margin: 0 0 16px; padding-left: 24px; }\n.doc-page li { margin-bottom: 6px; color: #374151; }\n.doc-lead { font-size: 17px; color: #6b7280; margin-bottom: 40px; }\n.doc-badge { display: inline-block; background: #ede9fe; color: #5b21b6; font-size: 12px; font-weight: 700; padding: 4px 12px; border-radius: 999px; letter-spacing: 0.5px; margin-bottom: 32px; }\n.doc-key-box { background: #1a1a2e; color: #a5b4fc; font-family: 'Courier New', monospace; font-size: 15px; padding: 20px 24px; border-radius: 10px; margin: 20px 0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; }\n.doc-key-box .label { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: rgba(255,255,255,0.4); display: block; margin-bottom: 4px; }\n.doc-key-box .val { color: #6ee7b7; font-size: 16px; }\n.endpoint-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px 24px; margin: 16px 0; box-shadow: 0 1px 4px rgba(0,0,0,0.05); }\n.endpoint-card .method { display: inline-block; background: #dcfce7; color: #166534; font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: 4px; margin-right: 8px; }\n.endpoint-card .url { font-family: 'Courier New', monospace; font-size: 14px; color: #1a1a2e; word-break: break-all; }\n.endpoint-card .desc { font-size: 14px; color: #6b7280; margin-top: 8px; }\n.params-table { width: 100%; border-collapse: collapse; margin: 12px 0 0; font-size: 13px; }\n.params-table th { text-align: left; padding: 6px 12px; background: #f9fafb; color: #374151; font-weight: 600; border-bottom: 1px solid #e5e7eb; }\n.params-table td { padding: 7px 12px; border-bottom: 1px solid #f3f4f6; color: #374151; vertical-align: top; }\n.params-table td:first-child { font-family: 'Courier New', monospace; color: #6366f1; font-size: 12px; white-space: nowrap; }\n.params-table td.opt { color: #9ca3af; font-size: 12px; }\ncode { background: #f3f4f6; padding: 2px 7px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 13px; color: #374151; }\n.url-block { background: #1e293b; color: #7dd3fc; font-family: 'Courier New', monospace; font-size: 13px; padding: 14px 18px; border-radius: 8px; margin: 10px 0; overflow-x: auto; white-space: nowrap; line-height: 1.8; }\n.url-block a { color: #7dd3fc; text-decoration: none; }\n.json-block { background: #0f172a; color: #e2e8f0; font-family: 'Courier New', monospace; font-size: 12px; padding: 16px 18px; border-radius: 8px; margin: 10px 0; overflow-x: auto; line-height: 1.8; white-space: pre; }\n.json-key { color: #93c5fd; }\n.json-str { color: #86efac; }\n.json-num { color: #fcd34d; }\n.json-comment { color: #6b7280; font-style: italic; }\n.callout { border-left: 4px solid #6366f1; background: #f8f9ff; padding: 14px 18px; border-radius: 0 8px 8px 0; margin: 20px 0; font-size: 14px; color: #374151; }\n.callout strong { color: #4f46e5; }\n.callout-warn { border-left-color: #f59e0b; background: #fffbeb; }\n.callout-warn strong { color: #92400e; }\n.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 12px 0; }\n@media (max-width: 600px) { .field-grid { grid-template-columns: 1fr; } }\n.field-item { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px 14px; }\n.field-item .fn { font-family: 'Courier New', monospace; font-size: 12px; color: #6366f1; font-weight: 700; margin-bottom: 4px; }\n.field-item .fd { font-size: 13px; color: #374151; }\n.toc { background: #f8f9ff; border: 1px solid #e0e7ff; border-radius: 10px; padding: 20px 24px; margin: 32px 0; }\n.toc h3 { margin: 0 0 12px; font-size: 14px; color: #6366f1; text-transform: uppercase; letter-spacing: 1px; border: none; padding: 0; }\n.toc ol { margin: 0; padding-left: 20px; }\n.toc li { margin-bottom: 4px; font-size: 14px; }\n.toc a { color: #4f46e5; text-decoration: none; }\n.toc a:hover { text-decoration: underline; }\n.sport-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }\n.sport-chip { background: #e0e7ff; color: #3730a3; font-size: 12px; font-weight: 600; padding: 3px 12px; border-radius: 999px; }\nhr.doc-hr { border: none; border-top: 1px solid #e5e7eb; margin: 48px 0; }\n<\/style>\n<div class=\"doc-page\">\n<div class=\"doc-badge\">STRAVA API \u2014 PASCAL WAGENHOFER<\/div>\n<h1>Training Data API<\/h1>\n<p class=\"doc-lead\">This guide explains how to access Pascal&#8217;s Strava training data \u2014 runs, hikes, ski tours, bike rides \u2014 including heart rate, pace, elevation profile, and per-second streams. No Strava account or login required.<\/p>\n<div class=\"toc\">\n<h3>Contents<\/h3>\n<ol>\n<li><a href=\"#access\">Access &amp; Authentication<\/a><\/li>\n<li><a href=\"#quickstart\">Quick Start \u2014 3 URLs to bookmark<\/a><\/li>\n<li><a href=\"#endpoints\">All Endpoints<\/a><\/li>\n<li><a href=\"#fields\">Field Reference<\/a><\/li>\n<li><a href=\"#streams\">Heart Rate &amp; Elevation Streams (per-second)<\/a><\/li>\n<li><a href=\"#usecases\">Coach Use Cases<\/a><\/li>\n<li><a href=\"#tips\">Tips &amp; Limits<\/a><\/li>\n<\/ol>\n<\/div>\n<p><!-- ACCESS --><\/p>\n<h2 id=\"access\">1. Access &amp; Authentication<\/h2>\n<p>The API is accessible over HTTPS from any browser, curl command, Python script, or tool like Postman. Every request needs the API key added as a URL parameter.<\/p>\n<div class=\"doc-key-box\">\n<div>\n<span class=\"label\">Base URL<\/span><br \/>\n<span class=\"val\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1<\/span>\n<\/div>\n<div>\n<span class=\"label\">API Key (append to every request)<\/span><br \/>\n<span class=\"val\">?key=<em style=\"color:#f9a8d4;\">contact Pascal<\/em><\/span>\n<\/div>\n<\/div>\n<p>Example \u2014 paste this directly into a browser to verify it works:<\/p>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/stats?key=YOUR_KEY<\/div>\n<div class=\"callout\">\n<strong>Keep the key private.<\/strong> Do not share it publicly or commit it to code repositories. Contact Pascal if you need the key rotated.\n<\/div>\n<p><!-- QUICK START --><\/p>\n<h2 id=\"quickstart\">2. Quick Start \u2014 3 URLs to bookmark<\/h2>\n<h3>&#127939; Last 10 runs with HR and pace<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&amp;limit=10&amp;type=Run<\/div>\n<h3>&#128200; Full lifetime training stats<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/stats?key=YOUR_KEY<\/div>\n<h3>&#128149; Per-second HR + elevation for a specific activity<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073\/streams?key=YOUR_KEY<\/div>\n<p style=\"font-size:13px;color:#6b7280;\">Replace <code>18781993073<\/code> with the Strava activity ID (visible in the Strava URL or in the <code>strava_id<\/code> field of any activity).<\/p>\n<p><!-- ENDPOINTS --><\/p>\n<h2 id=\"endpoints\">3. All Endpoints<\/h2>\n<p><!-- STATS --><\/p>\n<div class=\"endpoint-card\">\n<div><span class=\"method\">GET<\/span><span class=\"url\">\/stats<\/span><\/div>\n<div class=\"desc\">Lifetime totals across all activities \u2014 total distance, hours, elevation gain, calories, average HR, and breakdown by sport.<\/div>\n<p>\n<strong style=\"font-size:13px;\">Example<\/strong><\/p>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/stats?key=YOUR_KEY<\/div>\n<p><strong style=\"font-size:13px;\">Response (excerpt)<\/strong><\/p>\n<div class=\"json-block\"><span class=\"json-key\">&#8220;total_activities&#8221;<\/span>: <span class=\"json-num\">757<\/span>,<br \/>\n<span class=\"json-key\">&#8220;total_km&#8221;<\/span>:          <span class=\"json-num\">8544.9<\/span>,<br \/>\n<span class=\"json-key\">&#8220;total_moving_h&#8221;<\/span>:    <span class=\"json-num\">900.1<\/span>,<br \/>\n<span class=\"json-key\">&#8220;total_elevation_m&#8221;<\/span>: <span class=\"json-num\">208192<\/span>,<br \/>\n<span class=\"json-key\">&#8220;total_calories&#8221;<\/span>:    <span class=\"json-num\">607014<\/span>,<br \/>\n<span class=\"json-key\">&#8220;avg_heartrate&#8221;<\/span>:     <span class=\"json-num\">135.4<\/span>,<br \/>\n<span class=\"json-key\">&#8220;by_type&#8221;<\/span>: {<br \/>\n  <span class=\"json-key\">&#8220;Run&#8221;<\/span>:        { <span class=\"json-key\">&#8220;count&#8221;<\/span>: <span class=\"json-num\">581<\/span>, <span class=\"json-key\">&#8220;km&#8221;<\/span>: <span class=\"json-num\">6843.2<\/span> },<br \/>\n  <span class=\"json-key\">&#8220;Hike&#8221;<\/span>:       { <span class=\"json-key\">&#8220;count&#8221;<\/span>: <span class=\"json-num\">49<\/span>,  <span class=\"json-key\">&#8220;km&#8221;<\/span>: <span class=\"json-num\">192.3<\/span>  },<br \/>\n  <span class=\"json-key\">&#8220;AlpineSki&#8221;<\/span>: { <span class=\"json-key\">&#8220;count&#8221;<\/span>: <span class=\"json-num\">44<\/span>,  <span class=\"json-key\">&#8220;km&#8221;<\/span>: <span class=\"json-num\">846.2<\/span>  },<br \/>\n  <span class=\"json-key\">&#8220;EBikeRide&#8221;<\/span>: { <span class=\"json-key\">&#8220;count&#8221;<\/span>: <span class=\"json-num\">34<\/span>,  <span class=\"json-key\">&#8220;km&#8221;<\/span>: <span class=\"json-num\">492.3<\/span>  }<br \/>\n}<\/div>\n<\/div>\n<p><!-- ACTIVITIES --><\/p>\n<div class=\"endpoint-card\">\n<div><span class=\"method\">GET<\/span><span class=\"url\">\/activities<\/span><\/div>\n<div class=\"desc\">List activities, newest first. Filter by sport type and\/or date range.<\/div>\n<p><\/p>\n<table class=\"params-table\">\n<tr>\n<th>Parameter<\/th>\n<th>Required<\/th>\n<th>Description<\/th>\n<\/tr>\n<tr>\n<td>key<\/td>\n<td>Yes<\/td>\n<td>API key<\/td>\n<\/tr>\n<tr>\n<td>limit<\/td>\n<td class=\"opt\">Optional<\/td>\n<td>Number of results, 1\u2013200. Default: <code>10<\/code><\/td>\n<\/tr>\n<tr>\n<td>type<\/td>\n<td class=\"opt\">Optional<\/td>\n<td>Filter by sport. See sport types below.<\/td>\n<\/tr>\n<tr>\n<td>since<\/td>\n<td class=\"opt\">Optional<\/td>\n<td>Only activities from this date onwards. Format: <code>YYYY-MM-DD<\/code><\/td>\n<\/tr>\n<\/table>\n<p>\n<strong style=\"font-size:13px;\">Sport types<\/strong><\/p>\n<div class=\"sport-chips\">\n<span class=\"sport-chip\">Run<\/span><span class=\"sport-chip\">Hike<\/span><span class=\"sport-chip\">AlpineSki<\/span><span class=\"sport-chip\">EBikeRide<\/span><span class=\"sport-chip\">Ride<\/span><span class=\"sport-chip\">Workout<\/span><span class=\"sport-chip\">Kayaking<\/span>\n<\/div>\n<p><strong style=\"font-size:13px;\">Examples<\/strong><\/p>\n<div class=\"url-block\">\n<span class=\"json-comment\"># Last 20 runs<\/span><br \/>\nhttps:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&amp;limit=20&amp;type=Run<\/p>\n<p><span class=\"json-comment\"># All activities since 1 Jan 2026<\/span><br \/>\nhttps:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&amp;since=2026-01-01&amp;limit=200<\/p>\n<p><span class=\"json-comment\"># Last 5 hikes<\/span><br \/>\nhttps:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&#038;limit=5&#038;type=Hike\n<\/div>\n<\/div>\n<p><!-- SINGLE --><\/p>\n<div class=\"endpoint-card\">\n<div><span class=\"method\">GET<\/span><span class=\"url\">\/activity\/{strava_id}<\/span><\/div>\n<div class=\"desc\">Full detail for one activity \u2014 includes per-km splits, per-lap HR, best efforts, and route polyline.<\/div>\n<p><\/p>\n<table class=\"params-table\">\n<tr>\n<th>Parameter<\/th>\n<th>Required<\/th>\n<th>Description<\/th>\n<\/tr>\n<tr>\n<td>key<\/td>\n<td>Yes<\/td>\n<td>API key<\/td>\n<\/tr>\n<tr>\n<td>streams<\/td>\n<td class=\"opt\">Optional<\/td>\n<td>Set to <code>1<\/code> to also include per-second HR, distance, altitude in the same response<\/td>\n<\/tr>\n<\/table>\n<p>\n<strong style=\"font-size:13px;\">Examples<\/strong><\/p>\n<div class=\"url-block\">\n<span class=\"json-comment\"># Activity detail only<\/span><br \/>\nhttps:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073?key=YOUR_KEY<\/p>\n<p><span class=\"json-comment\"># Activity detail + per-second streams in one call<\/span><br \/>\nhttps:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073?key=YOUR_KEY&#038;streams=1\n<\/div>\n<\/div>\n<p><!-- STREAMS --><\/p>\n<div class=\"endpoint-card\">\n<div><span class=\"method\">GET<\/span><span class=\"url\">\/activity\/{strava_id}\/streams<\/span><\/div>\n<div class=\"desc\">Per-second time series: heartrate, distance, and altitude for the full activity. Fetched live from Strava \u2014 takes 1\u20132 seconds to respond.<\/div>\n<p>\n<strong style=\"font-size:13px;\">Example<\/strong><\/p>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073\/streams?key=YOUR_KEY<\/div>\n<p><strong style=\"font-size:13px;\">Response<\/strong><\/p>\n<div class=\"json-block\">{<br \/>\n  <span class=\"json-key\">&#8220;strava_id&#8221;<\/span>:    <span class=\"json-num\">18781993073<\/span>,<br \/>\n  <span class=\"json-key\">&#8220;resolution&#8221;<\/span>:   <span class=\"json-str\">&#8220;high&#8221;<\/span>,      <span class=\"json-comment\">\/\/ always &#8220;high&#8221; (1-second resolution)<\/span><br \/>\n  <span class=\"json-key\">&#8220;data_points&#8221;<\/span>:  <span class=\"json-num\">3115<\/span>,        <span class=\"json-comment\">\/\/ number of seconds recorded<\/span><br \/>\n  <span class=\"json-key\">&#8220;hr_summary&#8221;<\/span>: {<br \/>\n    <span class=\"json-key\">&#8220;min&#8221;<\/span>: <span class=\"json-num\">111<\/span>,<br \/>\n    <span class=\"json-key\">&#8220;max&#8221;<\/span>: <span class=\"json-num\">184<\/span>,<br \/>\n    <span class=\"json-key\">&#8220;avg&#8221;<\/span>: <span class=\"json-num\">159.6<\/span><br \/>\n  },<br \/>\n  <span class=\"json-key\">&#8220;data&#8221;<\/span>: [<br \/>\n    { <span class=\"json-key\">&#8220;t_s&#8221;<\/span>: <span class=\"json-num\">78<\/span>, <span class=\"json-key\">&#8220;dist_m&#8221;<\/span>: <span class=\"json-num\">194.1<\/span>, <span class=\"json-key\">&#8220;alt_m&#8221;<\/span>: <span class=\"json-num\">472.0<\/span>, <span class=\"json-key\">&#8220;hr_bpm&#8221;<\/span>: <span class=\"json-num\">141<\/span> },<br \/>\n    { <span class=\"json-key\">&#8220;t_s&#8221;<\/span>: <span class=\"json-num\">79<\/span>, <span class=\"json-key\">&#8220;dist_m&#8221;<\/span>: <span class=\"json-num\">197.5<\/span>, <span class=\"json-key\">&#8220;alt_m&#8221;<\/span>: <span class=\"json-num\">472.6<\/span>, <span class=\"json-key\">&#8220;hr_bpm&#8221;<\/span>: <span class=\"json-num\">141<\/span> },<br \/>\n    { <span class=\"json-key\">&#8220;t_s&#8221;<\/span>: <span class=\"json-num\">80<\/span>, <span class=\"json-key\">&#8220;dist_m&#8221;<\/span>: <span class=\"json-num\">200.7<\/span>, <span class=\"json-key\">&#8220;alt_m&#8221;<\/span>: <span class=\"json-num\">472.8<\/span>, <span class=\"json-key\">&#8220;hr_bpm&#8221;<\/span>: <span class=\"json-num\">141<\/span> },<br \/>\n    <span class=\"json-comment\">\/\/ &#8230; one entry per second &#8230;<\/span><br \/>\n  ]<br \/>\n}<\/div>\n<\/div>\n<p><!-- FIELDS --><\/p>\n<h2 id=\"fields\">4. Field Reference<\/h2>\n<p>Every field returned by <code>\/activities<\/code> and <code>\/activity\/{id}<\/code>, explained in plain terms.<\/p>\n<h3>Activity summary fields<\/h3>\n<div class=\"field-grid\">\n<div class=\"field-item\">\n<div class=\"fn\">strava_id<\/div>\n<div class=\"fd\">Unique Strava activity ID. Use this to fetch detail or streams for a specific session.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">name<\/div>\n<div class=\"fd\">Activity title as Pascal set it in Strava.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">type \/ sport_type<\/div>\n<div class=\"fd\">Sport category (e.g. <code>Run<\/code>) and sub-type (e.g. <code>TrailRun<\/code>).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">date \/ start_time<\/div>\n<div class=\"fd\">Date and local start time of the activity (Switzerland timezone).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">distance_km<\/div>\n<div class=\"fd\">Total distance in kilometres.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">moving_time<\/div>\n<div class=\"fd\">Net moving time (HH:MM:SS) \u2014 excludes pauses.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">elapsed_time<\/div>\n<div class=\"fd\">Total clock time including all pauses.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">pace_per_km<\/div>\n<div class=\"fd\">Average pace in M:SS per kilometre (runs\/hikes).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">elevation_m<\/div>\n<div class=\"fd\">Total elevation gain in metres.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">elev_high_m \/ elev_low_m<\/div>\n<div class=\"fd\">Highest and lowest GPS altitude during the activity.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">avg_hr<\/div>\n<div class=\"fd\">Average heart rate in bpm for the whole activity.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">max_hr<\/div>\n<div class=\"fd\">Peak heart rate in bpm.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">cadence<\/div>\n<div class=\"fd\">Average step cadence in steps per minute (running).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">calories<\/div>\n<div class=\"fd\">Estimated kcal burned.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">suffer_score<\/div>\n<div class=\"fd\">Strava Suffer Score \u2014 a measure of training load based on HR zones.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">avg_watts \/ w_avg_watts<\/div>\n<div class=\"fd\">Average and weighted-average running power (Garmin Running Power, if recorded).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">kilojoules<\/div>\n<div class=\"fd\">Total work done in kJ (power-based activities).<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">device<\/div>\n<div class=\"fd\">Recording device, e.g. <code>Garmin f\u0113nix 8<\/code>.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">prs<\/div>\n<div class=\"fd\">Number of segment personal records set in this activity.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">strava_url<\/div>\n<div class=\"fd\">Direct link to the activity on Strava.com.<\/div>\n<\/div>\n<\/div>\n<h3>Detail fields (single activity only)<\/h3>\n<div class=\"field-grid\">\n<div class=\"field-item\">\n<div class=\"fn\">splits_metric[]<\/div>\n<div class=\"fd\">Per-kilometre breakdown: distance, moving time, elevation change, avg HR, avg speed. One entry per km split.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">laps[]<\/div>\n<div class=\"fd\">Per-lap breakdown (usually auto-lapped every km): avg HR, max HR, cadence, watts, pace zone.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">best_efforts[]<\/div>\n<div class=\"fd\">Strava&#8217;s best effort times for standard distances (400m, 1km, 1 mile, 5km, 10km\u2026) with PR rank.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">map_polyline<\/div>\n<div class=\"fd\">Encoded route polyline. Paste into <a href=\"https:\/\/developers.google.com\/maps\/documentation\/utilities\/polylineutility\" target=\"_blank\">Google&#8217;s decoder<\/a> to visualise the GPS track.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">start_latlng \/ end_latlng<\/div>\n<div class=\"fd\">GPS coordinates of start and finish points.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">segment_count<\/div>\n<div class=\"fd\">Number of named Strava segments matched during the activity.<\/div>\n<\/div>\n<\/div>\n<p><!-- STREAMS --><\/p>\n<h2 id=\"streams\">5. Heart Rate &amp; Elevation Streams (per-second)<\/h2>\n<p>The <code>\/streams<\/code> endpoint goes beyond averages and gives you the complete second-by-second recording. Every row in the <code>data<\/code> array represents one second of the activity:<\/p>\n<div class=\"field-grid\">\n<div class=\"field-item\">\n<div class=\"fn\">t_s<\/div>\n<div class=\"fd\">Elapsed seconds from activity start. Use this as your X-axis when charting.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">dist_m<\/div>\n<div class=\"fd\">Cumulative distance in metres at that moment. Convert to km by dividing by 1000.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">alt_m<\/div>\n<div class=\"fd\">GPS altitude in metres. Plot this to get the elevation profile.<\/div>\n<\/div>\n<div class=\"field-item\">\n<div class=\"fn\">hr_bpm<\/div>\n<div class=\"fd\">Heart rate in beats per minute at that second. Plot this to see cardiac response to terrain.<\/div>\n<\/div>\n<\/div>\n<div class=\"callout\">\n<strong>What you can do with stream data:<\/strong> plot HR over time to identify cardiac drift, overlay elevation and HR to see how Pascal responds to climbs, calculate time-in-zone, detect steady-state vs. surges, or export to any analysis tool.\n<\/div>\n<h3>How to find a Strava activity ID<\/h3>\n<p>Two ways:<\/p>\n<ul>\n<li>From the <code>\/activities<\/code> endpoint: look for the <code>strava_id<\/code> field in any activity.<\/li>\n<li>From Strava.com: the number in the URL, e.g. <code>strava.com\/activities\/<strong>18781993073<\/strong><\/code><\/li>\n<\/ul>\n<p><!-- USE CASES --><\/p>\n<h2 id=\"usecases\">6. Coach Use Cases<\/h2>\n<h3>Reviewing the last training block<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&amp;since=2026-05-01&amp;limit=200<\/div>\n<p>Returns every activity since 1 May 2026 with distance, HR, pace, elevation, and calories. Load into Excel or Google Sheets by pasting the URL into the JSON import tool.<\/p>\n<h3>Analysing a specific long run<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073?key=YOUR_KEY<\/div>\n<p>Returns per-km splits and per-lap HR. Look at <code>splits_metric[].avg_hr<\/code> to see if HR was drifting upward (cardiac decoupling) or stable across the run.<\/p>\n<h3>Seeing exactly where HR spiked on a climb<\/h3>\n<div class=\"url-block\">https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activity\/18781993073\/streams?key=YOUR_KEY<\/div>\n<p>The full second-by-second log. Plot <code>alt_m<\/code> and <code>hr_bpm<\/code> against <code>t_s<\/code> on the same chart to correlate climbs with cardiac response.<\/p>\n<h3>Checking Google Sheets import<\/h3>\n<p>In Google Sheets, use <strong>Extensions \u2192 Apps Script<\/strong> and paste:<\/p>\n<div class=\"json-block\">function importStrava() {<br \/>\n  var url = &#8220;https:\/\/wagenhofer.ch\/me\/wp-json\/strava\/v1\/activities?key=YOUR_KEY&#038;limit=50&#038;type=Run&#8221;;<br \/>\n  var data = JSON.parse(UrlFetchApp.fetch(url).getContentText()).activities;<br \/>\n  var sheet = SpreadsheetApp.getActiveSheet();<br \/>\n  var headers = [&#8220;date&#8221;,&#8221;name&#8221;,&#8221;distance_km&#8221;,&#8221;moving_time&#8221;,&#8221;pace_per_km&#8221;,&#8221;avg_hr&#8221;,&#8221;max_hr&#8221;,&#8221;elevation_m&#8221;,&#8221;calories&#8221;,&#8221;suffer_score&#8221;,&#8221;strava_url&#8221;];<br \/>\n  sheet.getRange(1,1,1,headers.length).setValues([headers]);<br \/>\n  data.forEach(function(a, i) {<br \/>\n    sheet.getRange(i+2,1,1,headers.length).setValues([[<br \/>\n      a.date, a.name, a.distance_km, a.moving_time, a.pace_per_km,<br \/>\n      a.avg_hr, a.max_hr, a.elevation_m, a.calories, a.suffer_score, a.strava_url<br \/>\n    ]]);<br \/>\n  });<br \/>\n}<\/div>\n<p>Run the function once to pull the last 50 runs into a spreadsheet. Change <code>limit=50<\/code> or add <code>&amp;since=YYYY-MM-DD<\/code> to adjust the range.<\/p>\n<p><!-- TIPS --><\/p>\n<h2 id=\"tips\">7. Tips &amp; Limits<\/h2>\n<ul>\n<li><strong>Data freshness:<\/strong> Activities sync automatically from Strava via webhook. New activities appear within a few minutes of Pascal finishing a session.<\/li>\n<li><strong>Streams are live:<\/strong> The <code>\/streams<\/code> endpoint calls Strava&#8217;s API in real time. It takes 1\u20133 seconds to respond and requires an active internet connection on the server. If Strava is down, it will return an error.<\/li>\n<li><strong>Max results:<\/strong> The <code>limit<\/code> parameter goes up to 200. For larger exports, use <code>since<\/code> to paginate by date range.<\/li>\n<li><strong>HR not always present:<\/strong> Some activities (e.g. indoor workouts without a chest strap) may have no <code>avg_hr<\/code>. These fields will simply be absent from the response.<\/li>\n<li><strong>Streams resolution:<\/strong> Strava records at &#8220;high&#8221; resolution (1-second intervals during movement). Pauses in the activity are excluded from the stream \u2014 the <code>t_s<\/code> counter may jump.<\/li>\n<li><strong>Activity IDs never change:<\/strong> Once a Strava activity ID is noted, it always refers to the same activity. Safe to bookmark or store.<\/li>\n<\/ul>\n<div class=\"callout callout-warn\">\n<strong>Questions or issues?<\/strong> Contact Pascal Wagenhofer directly \u2014 <a href=\"mailto:pascal@wagenhofer.ch\">pascal@wagenhofer.ch<\/a>. The API key can be rotated at any time if needed.\n<\/div>\n<hr class=\"doc-hr\">\n<p style=\"font-size:13px;color:#9ca3af;text-align:center;\">Strava Training Data API \u00b7 wagenhofer.ch \u00b7 Updated June 2026<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>STRAVA API \u2014 PASCAL WAGENHOFER Training Data API This guide explains how to access Pascal&#8217;s Strava training data \u2014 runs, hikes, ski tours, bike rides \u2014 including heart rate, pace, elevation profile, and per-second streams. No Strava account or login required. Contents Access &amp; Authentication Quick Start \u2014 3 URLs to bookmark All Endpoints Field&#8230;<\/p>\n","protected":false},"author":0,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-75","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/pages\/75","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/types\/page"}],"replies":[{"embeddable":true,"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/comments?post=75"}],"version-history":[{"count":1,"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/pages\/75\/revisions"}],"predecessor-version":[{"id":77,"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/pages\/75\/revisions\/77"}],"wp:attachment":[{"href":"https:\/\/wagenhofer.ch\/me\/wp-json\/wp\/v2\/media?parent=75"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}