Activity Library API

The Open Activity Library exposes a public REST API for browsing and searching exercises. Read endpoints require no authentication — anonymous callers get 120 requests an hour, and a free read-only key raises that to 5,000 a day. Write endpoints require an Authorization: Bearer <token> header; admin endpoints additionally require the caller to be an admin account.

Want everything at once? The full library is free on GitHub as YAML (CC BY-SA 4.0) — no key, no rate limit.

Authentication

Authenticated endpoints identify you from the bearer token — the acting user is never read from the request body or a custom header. Library write endpoints accept a Firebase ID token for the signed-in user. Library reads need nothing at all; send a library key (ows_lib_…, see Keys) for a higher limit. Personal API tokens (ows_…, generated on your Account page) authenticate the Remote Agent API and the MCP connector instead — see Connect your AI.

curl -X POST https://your-domain.com/api/library/activities/propose \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"draft": {"name": "Bulgarian Split Squat", "primaryMuscles": ["quadriceps"]}}'

Base URL

https://your-domain.com

All endpoints are relative to the application root. Replace with your deployment URL.

Keys

Anonymous requests get 120 an hour; a free key gets 5,000 a day. Keys are instant and read-only, and if you want the whole library rather than an API, the bulk data is free on GitHub.

TierLimitHow
Anonymous120 requests / hour per IPNothing — just call the API
Signed-in app user2,000 requests / dayThe app sends your Firebase ID token
Free key5,000 requests / dayAuthorization: Bearer ows_lib_… — get one below
CommercialOn requestComing when someone asks

Using a key

curl "https://your-domain.com/api/library/activities?q=squat" \
  -H "Authorization: Bearer ows_lib_…"

When you hit the limit

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1788373440
X-Library-Tier: anonymous

{
  "error": "rate_limited",
  "tier": "anonymous",
  "limit": 120,
  "remaining": 0,
  "resetAt": "2026-09-02T18:24:00.000Z",
  "message": "Anonymous requests are limited to 120 per hour. Sign in or create a free key for 5,000 a day.",
  "getKey": {
    "docs": "https://your-domain.com/docs/api#keys",
    "signIn": "https://your-domain.com/login?returnTo=%2Fdocs%2Fapi%23keys",
    "create": "POST https://your-domain.com/api/library/keys",
    "agents": {
      "register": "https://your-domain.com/api/oauth/register",
      "scope": "ows:library",
      "docs": "https://your-domain.com/docs/connect-ai"
    }
  },
  "bulk": "https://github.com/openworkoutsystem/open-activity-library"
}

Agents: the 429 tells you where to register; request the ows:library scope.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-Library-Tier, so you can pace yourself before a 429.

Public Endpoints

GET/api/library/activities

List and search exercises in the library. Public — no authentication required.

Query Parameters

qstringSearch query (name, description, muscles)
musclestringFilter by primary muscle group
equipmentstringFilter by equipment type
tagstringFilter by tag
sourcestringFilter by source: "user", "ai", or "migration"
limitnumberMax results (default 50, max 200)
offsetnumberPagination offset

Response

{
  "activities": [
    {
      "id": "lib-1710500000-abc123",
      "name": "Barbell Back Squat",
      "description": "...",
      "primaryMuscles": ["quadriceps", "glutes"],
      "equipment": ["barbell", "squat rack"],
      "source": "user",
      "provenance": {
        "confidence": 0.85,
        "reviewCount": 3,
        "contributors": [...]
      },
      "validationStatus": "validated"
    }
  ],
  "total": 1247
}
GET/api/library/activities/:id

Get a single exercise by ID with full details including provenance and feedback summary.

Response

{
  "id": "lib-1710500000-abc123",
  "name": "Barbell Back Squat",
  "description": "...",
  "primaryMuscles": ["quadriceps", "glutes"],
  "secondaryMuscles": ["hamstrings", "core"],
  "equipment": ["barbell", "squat rack"],
  "trackableMetrics": [...],
  "defaults": { "sets": 3, "reps": 8 },
  "provenance": {
    "confidence": 0.85,
    "contributors": [...],
    "citations": [...],
    "reviewCount": 3
  },
  "feedbackSummary": {
    "thumbsUp": 12,
    "thumbsDown": 1,
    "totalFeedback": 13
  },
  "validationStatus": "validated"
}
GET/api/library/stats

Library-wide statistics: total exercises, breakdown by source/status, average confidence.

Response

{
  "total": 1247,
  "bySource": { "user": 340, "ai": 780, "migration": 127 },
  "byStatus": { "active": 1100, "pending_review": 47, "archived": 100 },
  "averageConfidence": 0.72,
  "validatedCount": 980,
  "pendingValidation": 167,
  "staleCount": 100
}
GET/api/library/schema

Returns the YAML schema definition for exercises. Useful for building tools or validators.

Response

{
  "schema": "# Open Activity Library Exercise Schema\n..."
}

Authenticated Endpoints

POST/api/library/activities/proposeRequires Authorization: Bearer <Firebase ID token>; submitter is the verified caller

Propose a new exercise. Goes through Tier 2 AI review (expert model with research).

Request Body

draftobjectFull exercise object (name, description, primaryMuscles, etc.)
contextstringOptional note for the reviewer (sources, why it belongs in the library)

Response

{
  "decision": "approve",
  "reasoning": "Well-structured exercise with accurate anatomy...",
  "exerciseId": "lib-1710500000-xyz789",
  "validationStatus": "pending_validation"
}
POST/api/library/activities/:id/suggestRequires Authorization: Bearer <Firebase ID token>; submitter is the verified caller

Suggest an edit to an existing exercise. Tier 1 for minor edits (descriptions, tags), Tier 2 for structural changes (muscles, equipment).

Request Body

proposedChangesobjectObject with changed field values
changedFieldsstring[]List of changed field names
contextstringOptional context for the change

Response

{
  "decision": "approve",
  "reasoning": "Tag additions are accurate and follow conventions.",
  "tier": 1
}
POST/api/library/activities/:id/feedbackRequires Authorization: Bearer <Firebase ID token>; feedback is recorded for the verified caller

Submit feedback (thumbs up/down) on an exercise. Used for Tier 3 crowdsourced validation.

Request Body

rating"up" | "down"Thumbs up or down
feedbackTypestringCurrently always "validation"

Response

{ "success": true }
GET/api/library/activities/:id/feedbackRequires Authorization: Bearer <Firebase ID token>

Get the verified caller's feedback for an exercise. Check if you already submitted.

Response

{
  "feedback": {
    "rating": "up",
    "feedbackType": "validation",
    "createdAt": "2026-03-15T..."
  }
}
POST/api/library/keysRequires Authorization: Bearer <Firebase ID token>; guests get 403 sign_in_required

Create a read-only library key (up to 5 per account). The plaintext key is returned once and never again — store it. No approval step; keys are instant.

Request Body

labelstringOptional, up to 40 characters — shown on your Account page

Response

{
  "key": "ows_lib_…",
  "id": "k_7f3a…",
  "prefix": "ows_lib_7f3a",
  "label": "My script",
  "createdAt": "2026-09-02T17:04:00.000Z",
  "tier": "free",
  "dailyLimit": 5000
}

// 409 when you already have 5 keys
{ "error": "key_limit", "limit": 5 }
GET/api/library/keysRequires Authorization: Bearer <Firebase ID token>

List your library keys with today's usage. Prefixes only — the plaintext key is never returned after creation.

Response

{
  "keys": [
    {
      "id": "k_7f3a…",
      "prefix": "ows_lib_7f3a",
      "label": "My script",
      "createdAt": "2026-09-02T17:04:00.000Z",
      "lastUsedAt": null,
      "usageToday": 0,
      "dailyLimit": 5000
    }
  ]
}
DELETE/api/library/keys/:idRequires Authorization: Bearer <Firebase ID token>

Revoke a library key. Takes effect immediately.

Response

{ "ok": true }

Admin Endpoints

POST/api/library/reviewAdmin only — Authorization: Bearer <Firebase ID token> of an admin account

Admin endpoint. Manually trigger AI review on an exercise.

Request Body

exerciseIdstringExercise to review
changedFieldsstring[]Fields that changed
proposedChangesobjectNew field values
tiernumberForce tier 1 or 2

Response

{
  "decision": "approve",
  "reasoning": "...",
  "tier": 2
}
POST/api/library/syncAdmin only — Authorization: Bearer <Firebase ID token> of an admin account

Admin endpoint. Sync library to GitHub repository as YAML files.

Request Body

dryRunbooleanIf true, returns file list without pushing

Response

{
  "fileCount": 1247,
  "commitSha": "abc123...",
  "dryRun": false
}
POST/api/library/recalculateAdmin only — Authorization: Bearer <Firebase ID token> of an admin account

Admin endpoint. Recalculate confidence scores for all exercises, flag stale ones.

Request Body

dryRunbooleanDefaults to true. Set false to apply changes.
staleThresholdDaysnumberDays before marking stale (default 180)

Response

{
  "dryRun": true,
  "total": 1247,
  "recalculated": 45,
  "flaggedStale": 12,
  "flaggedArchive": 3,
  "changes": [...]
}