Referencia
Convenciones y errores
Las reglas que se cumplen en todos los endpoints, para que solo tengas que aprenderlas una vez.
Paginación
Los endpoints de listado aceptan page (empezando en 1) y pageSize (de 1 a 100, por defecto 30) como parámetros de query y envuelven los resultados en un envoltorio:
{
"items": [ … ],
"pageNumber": 2,
"totalPages": 9,
"totalCount": 174,
"hasPreviousPage": true,
"hasNextPage": true
}Los enums son cadenas en camelCase
Los valores enum son siempre cadenas en camelCase, nunca enteros: "pounds", "kilometers", "oneRepMax", "chest", "normal". Trata los valores enum desconocidos como compatibles hacia delante: pueden aparecer nuevos sin cambio de versión.
Unidades
| Medida | Unidad |
|---|---|
| Peso (series, récords) | Libras |
| Distancia | Metros |
| Duración de serie / récord | Segundos |
| Duración del entrenamiento | Minutos (durationMinutes) |
| Medidas corporales | El sistema de unidades del weightUnit de la entrada (pulgadas con libras, centímetros con kilogramos) |
Las unidades de visualización preferidas del usuario están en /api/v1/user: convierte en el borde de tu app, no en el almacenamiento.
Identificadores
- Datos de usuario (entrenamientos, plantillas, splits, medidas): GUIDs estables
- Datos de catálogo (ejercicios, equipamiento): ids enteros estables compartidos con la app de Gravl
- El usuario: un id de cadena opaco, estable en todos los endpoints y tokens
Fechas
Todas las marcas de tiempo son UTC, ISO 8601 (2026-07-12T17:03:00Z). Los filtros por rango de fechas (startDate, endDate) son inclusivos.
Rate limits
| Superficie | Límite | Clave |
|---|---|---|
/api/v1/* | 100 peticiones / 15 min | app + usuario |
/oauth/* | 10 peticiones / min | dirección IP |
Superar un límite devuelve 429 con una cabecera Retry-After cuando está disponible. Aplica backoff exponencial y no hagas polling: la mayoría de los datos personales cambian a velocidad humana.
Errores
| Estado | Cuándo | Cuerpo |
|---|---|---|
| 401 | Token ausente, malformado, expirado o revocado; app suspendida | Vacío (desafío WWW-Authenticate) |
| 403 | Token válido, falta el scope | Problem details con el nombre del scope que falta |
| 404 | El recurso no existe, o pertenece a otro usuario | Problem details |
| 429 | Rate limit superado | Vacío; cabecera Retry-After |
| 400 | Fallo de validación (tamaño de página incorrecto, parámetros malformados) | Problem details con errores por campo |
{
"status": 403,
"title": "Missing scope",
"detail": "This authorization is missing the 'measurements:read' scope."
}Fíjate en la regla del 404 para datos ajenos: pedir por id el entrenamiento de otro usuario devuelve 404, no 403. La API nunca confirma que existan datos que no puedes ver.