Reference
Conventions & errors
The rules that hold across every endpoint, so you only have to learn them once.
Pagination
List endpoints take page (1-based) and pageSize (1–100, default 30) as query parameters and wrap results in one envelope:
{
"items": [ … ],
"pageNumber": 2,
"totalPages": 9,
"totalCount": 174,
"hasPreviousPage": true,
"hasNextPage": true
}Enums are camelCase strings
Enum values are always strings in camelCase — never integers: "pounds", "kilometers", "oneRepMax", "chest", "normal". Treat unknown enum values as forward-compatible: new ones may appear without a version bump.
Units
| Measure | Unit |
|---|---|
| Weight (sets, records) | Pounds |
| Distance | Meters |
| Set / record duration | Seconds |
| Workout duration | Minutes (durationMinutes) |
| Body measurements | The unit system in the entry’s weightUnit (inches with pounds, centimeters with kilograms) |
The user’s preferred display units are on /api/v1/user — convert at the edge of your app, not in storage.
Identifiers
- User data (workouts, templates, splits, measurements) — stable GUIDs
- Catalog data (exercises, equipment) — stable integer ids shared with the Gravl app
- The user — an opaque string id, stable across all endpoints and tokens
Dates
All timestamps are UTC, ISO 8601 (2026-07-12T17:03:00Z). Date-range filters (startDate, endDate) are inclusive.
Rate limits
| Surface | Limit | Keyed by |
|---|---|---|
/api/v1/* | 100 requests / 15 min | app + user |
/oauth/* | 10 requests / min | IP address |
Exceeding a limit returns 429 with a Retry-After header when available. Back off exponentially; don’t poll — most personal data changes at human speed.
Errors
| Status | When | Body |
|---|---|---|
| 401 | Missing, malformed, expired or revoked token; suspended app | Empty (WWW-Authenticate challenge) |
| 403 | Valid token, missing scope | Problem details with the missing scope named |
| 404 | Resource doesn’t exist — or belongs to another user | Problem details |
| 429 | Rate limited | Empty; Retry-After header |
| 400 | Validation failure (bad page size, malformed parameters) | Problem details with field errors |
{
"status": 403,
"title": "Missing scope",
"detail": "This authorization is missing the 'measurements:read' scope."
}Note the 404-for-foreign-data rule: requesting another user’s workout by id returns 404, not 403 — the API never confirms that data you can’t see exists.