Shape API

Shape ships with a REST API so you can plug your training into any tool, AI agent, or automation — or export everything. No lock-in.

The full machine-readable reference is at /llms.txt — point any LLM or agent at it and it can plan, create, and manage your workouts for you.

Authentication

Every request needs your API token in the Authorization header. Tokens start with shape_ and can be created from API access.

Authorization: Bearer shape_...

Base URL

https://shapecalendar.com/api/v1

Endpoints

GET/activities

List activities in a date range. Returns { activities, total, limit, offset }.

Query parameters (all optional)

  • fromISO date

    Start of the range (e.g. 2026-03-01).

  • toISO date

    End of the range.

  • sportTypestring

    Filter by type: run, bike, swim, hike, yoga, nordicski, strength, other.

  • completedboolean

    true or false.

  • includePairedboolean

    true to include planned activities that have a paired completed activity (hidden by default).

  • limitnumber

    Default 50, max 200.

  • offsetnumber

    Pagination offset. Default 0.

GET /activities?from=2026-03-01&to=2026-03-31
GET/activities/:id

Get a single activity by ID. Returns { activity }.

POST/activities

Create an activity. Returns { activity } with status 201.

Required fields

  • dateISO date

    e.g. 2026-03-28 or 2026-03-28T08:00:00Z.

  • titlestring

    Short workout title.

  • sportTypestring

    One of run, bike, swim, hike, yoga, nordicski, strength, other.

Optional fields

  • descriptionstring
  • distancenumber

    Meters.

  • durationnumber

    Seconds.

  • speednumber

    Meters per second.

  • heartRatenumber

    Beats per minute.

  • powernumber

    Watts.

  • loadnumber

    Training stress score.

  • elevationGainnumber

    Meters.

  • completedboolean
  • targetstring

    One of distance, time, pace, steps.

  • stepsarray

    Structured workout steps (see below). Set target to steps when using this.

PATCH/activities/:id

Update an activity. Send only the fields you want to change — same fields as create, all optional. Returns { activity }.

DELETE/activities/:id

Delete an activity. Returns { deleted: true }.

POST/activities/batch

Create up to 100 activities in one request. Returns { activities } with status 201.

{ "activities": [{ date, title, sportType, ... }, ...] }
PATCH/activities/batch

Update up to 100 activities in one request. Each object must include id plus the fields to change. Returns { updated }.

{ "activities": [{ "id": "id1", "completed": true }, ...] }
DELETE/activities/batch

Delete up to 100 activities by ID. Returns { deleted }.

{ "ids": ["id1", "id2", ...] }

Activity object

{
  "id": "abc-123",
  "date": "2026-03-28",
  "title": "Easy morning run",
  "description": "Recovery run",
  "sportType": "run",
  "distance": 5000,
  "duration": 1800,
  "speed": 2.78,
  "heartRate": 140,
  "power": null,
  "load": 45,
  "elevationGain": 50,
  "completed": true,
  "target": "distance",
  "source": "strava",
  "externalId": "12345",
  "createdAt": 1711612800000,
  "updatedAt": 1711612800000
}

Structured workout steps

Activities can include structured steps for interval training. Set target to steps and provide a steps array.

Step types

warmup, cooldown, interval, recovery, repeat

End conditions

lap.button, time, distance, iterations, heart.rate, power, open

Target types

no.target, heart.rate, pace, power, speed, cadence

Step object

{
  "stepType": "interval",
  "displayName": "400m fast",
  "description": null,
  "endCondition": "distance",
  "endConditionValue": 400,
  "targetType": "pace",
  "targetValueOne": 210,
  "targetValueTwo": 240,
  "zoneNumber": null,
  "secondaryTargetType": null,
  "secondaryTargetValueOne": null,
  "secondaryTargetValueTwo": null,
  "secondaryZoneNumber": null
}

Repeat group

{
  "stepType": "repeat",
  "numberOfIterations": 5,
  "skipLastRestStep": false,
  "workoutSteps": [
    { "stepType": "interval", "displayName": "400m fast", "endCondition": "distance", "endConditionValue": 400, "targetType": "pace", "targetValueOne": 210, "targetValueTwo": 240 },
    { "stepType": "recovery", "displayName": "90s jog", "endCondition": "time", "endConditionValue": 90, "targetType": "no.target" }
  ]
}

Example: 5×400m interval workout

{
  "date": "2026-03-30",
  "title": "400m Repeats",
  "sportType": "run",
  "distance": 4000,
  "duration": 2400,
  "target": "steps",
  "steps": [
    { "stepType": "warmup", "displayName": "Warmup jog", "endCondition": "time", "endConditionValue": 600, "targetType": "no.target" },
    {
      "stepType": "repeat",
      "numberOfIterations": 5,
      "workoutSteps": [
        { "stepType": "interval", "displayName": "400m fast", "endCondition": "distance", "endConditionValue": 400, "targetType": "pace", "targetValueOne": 210, "targetValueTwo": 240 },
        { "stepType": "recovery", "displayName": "90s jog", "endCondition": "time", "endConditionValue": 90, "targetType": "no.target" }
      ]
    },
    { "stepType": "cooldown", "displayName": "Cooldown jog", "endCondition": "time", "endConditionValue": 600, "targetType": "no.target" }
  ]
}

Sport types

run, bike, swim, hike, yoga, nordicski, strength, other

Errors

Errors always return { "error": "message" }.

  • 400 — validation error (bad request body or query params)
  • 401 — missing or invalid token
  • 404 — activity not found
  • 429 — rate limit exceeded

Tips for LLMs

  • Always include a date range when listing activities to avoid fetching everything.
  • Use PATCH to update single fields (e.g. mark completed) rather than sending the full object.
  • Use batch endpoints when creating or deleting multiple activities at once.
  • Distance is always in meters, duration in seconds.
  • Activities with source strava, garmin, or wahoo were imported from external services.
  • The completed field distinguishes planned workouts (false) from completed ones (true).
  • When a planned activity is completed, both exist linked via completedActivityId / plannedActivityId. By default the list endpoint hides the paired planned one — use includePaired=true to see both.

Creating workouts — conventions

  • Keep titles short. Don’t include distance/duration in the title: “Easy 5k run” → “Easy run”, “bike 100km” → “Bike”.
  • Set target based on the workout goal: “5k run” → distance, “20min ride” → time, “5k in 20min” → pace, structured intervals → steps.
  • Only include steps for workouts with a specific interval structure (e.g. “5×400m at 5k pace with 60s rest”). Don’t create steps for simple descriptions like “5k run” or “20min easy bike ride”.
  • Never include steps for strength training — use the description field instead.
  • load is Training Stress Score (TSS): (duration_seconds / 3600) × relative_effort × 100, where relative effort ranges from 0.25 (very easy) to 1.50 (very hard). Example: 30min easy run = (1800/3600) × 0.5 × 100 = 25 TSS.

Example response

GET/api/v1/activities?from=2026-04-15&to=2026-04-21
[
  {
    "id": "act_8hX2",
    "date": "2026-04-18",
    "title": "5×1km at threshold",
    "sportType": "run",
    "distance": 10000,
    "duration": 3600,
    "load": 142,
    "completed": true,
    "steps": []
  }
]

Rate limits

30 requests per minute and 200 per hour. Exceeding either returns a 429. Use batch endpoints to stay under the cap when creating or deleting many activities.