APIdeveloperintegrations

How to Build Your Own Fitness Dashboard with the Pairform API

A developer's guide to accessing your running data programmatically. Use the Pairform API to build custom dashboards, automations, and AI integrations.

Pairform Team··6 min read

If you're a developer who runs, you've probably wanted to build something with your fitness data. Maybe a custom dashboard that shows exactly what you care about. Maybe an automation that adjusts your calendar based on recovery. Maybe a Slack bot that posts your weekly training summary.

The problem is getting the data. Strava's API is heavily rate-limited and doesn't include recovery data. WHOOP's API is restricted. Garmin's API requires a developer partnership. Cobbling together data from 3-4 different APIs with different auth flows, rate limits, and data formats is a project in itself.

Pairform solves the data layer so you can focus on building.

What the Pairform API provides

The API gives you unified access to data from all connected sources through a single authenticated endpoint. Here's what's available:

Snapshot

GET /api/snapshot — Your current training status in one call.

Returns:

  • Current CTL, ATL, TSB with human-readable form description
  • This week's stats (distance, duration, workouts, elevation)
  • Latest body metrics (weight, body fat)
  • Recovery data (HRV, resting HR, sleep, readiness score)
  • Recent nutrition data

This is the endpoint most AI integrations use — it provides enough context for coaching conversations without multiple API calls.

Workouts

GET /api/workouts/detail — Detailed workout data.

Parameters:

  • days — Number of days to look back (default: 30)
  • limit — Max workouts to return

Returns workout objects with:

  • Distance, duration, pace, elevation
  • Heart rate data (avg, max, zone distribution)
  • TSS (training stress score)
  • RPE and notes (if logged)
  • Training effects (aerobic/anaerobic)
  • Planned vs. actual metrics (if training plan is connected)

Goals

GET /api/goals/enriched — Active goals with progress and trend data.

Returns goals with:

  • Current value and target
  • 8-week trend data
  • Completion percentage
  • Source metrics

Metrics

GET /api/metrics/detail — Daily metrics over time.

Parameters:

  • metric — Which metric (weight, hrv, resting_hr, sleep, etc.)
  • days — Lookback period

Returns time-series data for any tracked metric.

Calendar

GET /api/calendar — Workout calendar with planned and completed workouts.

Race Predictions

Part of the snapshot response — estimated race times for 5K, 10K, half marathon, and marathon based on current fitness.

Authentication

Two authentication methods:

API Key (simplest)

Generate an API key from your Pairform profile. Include it in the Authorization header:

Authorization: Bearer pk_your_api_key_here

API keys have full read access to your data. Generate, revoke, and manage keys from your profile page.

OAuth (for multi-user apps)

If you're building an app that other Pairform users will connect to, use the OAuth 2.0 flow:

  1. Register your app at your Pairform profile
  2. Redirect users to /api/oauth/authorize with your client ID
  3. User approves, you receive an authorization code
  4. Exchange for access token at /api/oauth/token

MCP (for AI integrations)

If you're building an AI agent or connecting to ChatGPT/Claude, MCP (Model Context Protocol) is the easiest path. Pairform exposes an MCP server that AI models can call directly.

The MCP server provides tools for:

  • Getting the athlete's snapshot
  • Querying workouts with filters
  • Checking training load and recovery
  • Accessing goals and race predictions
  • Getting coaching analytics

No custom API integration needed — just connect Pairform as an MCP server and the AI can query naturally.

Example: Weekly training summary bot

Here's a simple example — a script that fetches your weekly training data and formats a summary:

const API_KEY = process.env.PAIRFORM_API_KEY;
const BASE_URL = "https://pairform.io";

async function getWeeklySummary() {
  const snapshot = await fetch(`${BASE_URL}/api/snapshot`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  }).then((r) => r.json());

  const { weekStats, trainingLoad, recovery } = snapshot;

  return `
## Weekly Training Summary

**Runs**: ${weekStats.workoutCount} workouts, ${weekStats.totalDistance} miles
**Training Load**: CTL ${trainingLoad.ctl} | ATL ${trainingLoad.atl} | TSB ${trainingLoad.tsb} (${trainingLoad.formDescription})
**Recovery**: ${recovery.readinessScore}% readiness, HRV ${recovery.hrv}ms
**Sleep**: ${recovery.sleepHours}h avg
  `.trim();
}

Post this to Slack, email it to yourself, or display it on a Raspberry Pi dashboard — the data is clean JSON, so the integration is straightforward.

Example: Recovery-based calendar adjustment

A more advanced use case — automatically adjusting your calendar based on recovery:

async function shouldTrainHard(): Promise<boolean> {
  const snapshot = await fetch(`${BASE_URL}/api/snapshot`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  }).then((r) => r.json());

  const { trainingLoad, recovery } = snapshot;

  // Green light: fresh and recovered
  if (trainingLoad.tsb > 0 && recovery.readinessScore > 65) {
    return true;
  }

  // Red light: fatigued and not recovered
  if (trainingLoad.tsb < -15 || recovery.readinessScore < 40) {
    return false;
  }

  // Yellow: borderline, check HRV trend
  return recovery.hrvTrend === "rising" || recovery.hrvTrend === "stable";
}

Hook this into your calendar automation — if shouldTrainHard() returns false, automatically swap today's interval session for an easy run.

Example: Custom AI coaching agent

Build a custom coaching agent using the Claude API and Pairform data:

import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

async function askCoach(question: string) {
  // Fetch context from Pairform
  const snapshot = await fetch(`${BASE_URL}/api/snapshot`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  }).then((r) => r.json());

  const workouts = await fetch(
    `${BASE_URL}/api/workouts/detail?days=14`,
    { headers: { Authorization: `Bearer ${API_KEY}` } }
  ).then((r) => r.json());

  const response = await anthropic.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 1024,
    system: `You are an AI running coach. Here is the athlete's current data:
${JSON.stringify(snapshot, null, 2)}

Recent workouts:
${JSON.stringify(workouts, null, 2)}`,
    messages: [{ role: "user", content: question }],
  });

  return response.content[0].text;
}

// Usage
const advice = await askCoach("Should I do intervals tomorrow?");

This gives you a custom AI coach that runs anywhere — a CLI tool, a Discord bot, a mobile app — with full access to your training data.

Rate limits and best practices

  • Rate limit: 100 requests per minute per API key
  • Caching: Snapshot data updates every few minutes. Don't poll more frequently than every 5 minutes.
  • Pagination: Workout endpoints support limit and offset for large datasets
  • Error handling: API returns standard HTTP status codes. 401 = bad API key, 429 = rate limited, 500 = server error.

Getting started

  1. Create a free Pairform account and connect your data sources
  2. Generate an API key from your profile
  3. Hit the snapshot endpoint to see your data structure
  4. Build something — the data is clean JSON, the auth is a bearer token, the endpoints are RESTful

The goal of Pairform's API is to make your fitness data as accessible as possible. No rate limit games, no restricted developer programs, no data hostage situations. Your data, your API key, your integrations.


Ready to build? Create a free Pairform account, connect your devices, and generate an API key.