Unofficial TypeScript SDK for the Intervals.icu API. Fully typed, zero runtime dependencies, generated from the official OpenAPI spec.
Not an official SDK. This project is not affiliated with, endorsed by, or supported by Intervals.icu. It is a community project built on the public API that Intervals.icu generously provides. Please be considerate with your request volume (see rate limits).
- Complete coverage: all 149 operations of the Intervals.icu API v1 (activities, wellness, calendar events, workout library, sport settings, gear, power/pace/HR curves, and more).
- Fully typed: request paths, query params, bodies, and responses, straight from the spec.
- Zero runtime dependencies: the fetch-based client is bundled into the package.
- Runs anywhere fetch does: Node.js >= 18, Bun, Deno, edge runtimes. (Browsers work too, but never ship your API key to a browser.)
- Honest generation pipeline: the spec snapshot, the normalization script, and the generator config are all in this repo. Regenerate at any time; CI fails when
src/genis out of sync, and a weekly job opens a pull request when the upstream spec drifts.
npm install intervals-icu-sdk
# or
bun add intervals-icu-sdk
# or
pnpm add intervals-icu-sdkGet your personal API key from Intervals.icu: Settings (your profile) > Developer. Your athlete id is visible in the URL when you are logged in (for example /athlete/i1234567/...), and "0" always means "the athlete who owns the API key".
import { configureIntervals, getAthlete, listActivities, listWellnessRecords } from "intervals-icu-sdk";
configureIntervals(); // reads INTERVALS_API_KEY from the environment
// or explicitly: configureIntervals({ apiKey: "your-key" });
// Who am I?
const me = await getAthlete({ path: { id: "0" } });
console.log(me.data?.name);
// Last week of wellness (resting HR, HRV, sleep, fitness/fatigue, ...)
const wellness = await listWellnessRecords({
path: { id: "0" },
query: { oldest: "2026-07-01", newest: "2026-07-31" },
});
wellness.data?.forEach((d) => console.log(d.id, d.restingHR, d.hrv, d.ctl));
// Recent activities
const activities = await listActivities({
path: { id: "0" },
query: { oldest: "2026-07-01", limit: 10 },
});Every function returns { data, error, request, response } instead of throwing:
const { data, error, response } = await getAthlete({ path: { id: "0" } });
if (error) {
console.error(`HTTP ${response?.status}`, error);
} else {
console.log(data.name);
}Prefer exceptions? Pass throwOnError:
const { data } = await getAthlete({ path: { id: "0" }, throwOnError: true });
// data is non-optional here; failures throwMore runnable examples live in examples/.
Intervals.icu supports two schemes, and the SDK handles both:
| Scheme | Who it is for | How |
|---|---|---|
API key (HTTP basic, username API_KEY) |
personal scripts and tools | configureIntervals({ apiKey }) or INTERVALS_API_KEY |
| OAuth bearer token | registered multi-user apps | configureIntervals({ accessToken }) or INTERVALS_ACCESS_TOKEN |
See the Intervals.icu API forum thread for how keys and OAuth apps work.
configureIntervals configures a shared default client. For multiple accounts, custom fetch, interceptors, or full isolation, create your own client and pass it per call:
import { createClient } from "intervals-icu-sdk";
import { getAthlete } from "intervals-icu-sdk";
const client = createClient({
baseUrl: "https://intervals.icu",
auth: (auth) => (auth.scheme === "basic" ? `API_KEY:${myKey}` : undefined),
});
const me = await getAthlete({ client, path: { id: "0" } });The client also exposes interceptors.request / interceptors.response for logging, retries, and similar concerns.
Some sandboxed environments (cloud agent containers, locked-down CI) force all egress through an HTTPS proxy with a custom CA, where a plain fetch fails with ECONNRESET. configureIntervals accepts a custom fetch for exactly this (and for instrumentation in general):
import { configureIntervals } from "intervals-icu-sdk";
const PROXY = process.env.HTTPS_PROXY ?? process.env.https_proxy;
const CA = process.env.NODE_EXTRA_CA_CERTS ?? process.env.SSL_CERT_FILE ?? process.env.CURL_CA_BUNDLE;
configureIntervals({
fetch: PROXY
? async (input, init) =>
fetch(input, {
...init,
// Bun supports per-request proxy and TLS options on fetch
proxy: PROXY,
...(CA ? { tls: { ca: await Bun.file(CA).text() } } : {}),
} as RequestInit)
: undefined,
});On Node, wrap fetch with undici's EnvHttpProxyAgent instead: fetch(input, { ...init, dispatcher: new EnvHttpProxyAgent() }).
Function names mirror the API's operation ids, so the official API docs double as SDK docs. A taste of what is available:
| Area | Ops | Example functions |
|---|---|---|
| Activities | 52 | listActivities, getActivity, getActivityStreams, getIntervals, uploadActivity, findBestEfforts |
| Workout library | 19 | listWorkouts, createWorkout, listFolders, applyPlan |
| Calendar events | 16 | listEvents, createEvent, createMultipleEvents, markEventAsDone, downloadEventWorkout |
| Athletes | 10 | getAthlete, updateAthlete, getAthleteSummary, getAthleteTrainingPlan |
| Sport settings | 10 | listSportSettings, getSportSettings, updateSportSettings |
| Chats | 11 | listChats, sendMessage, listActivityMessages, updateChatBlocked |
| Gear | 9 | listGear, createGear, createReminder, replaceGear |
| Custom items | 7 | listCustomItems, createCustomItem |
| Wellness | 6 | listWellnessRecords, updateWellness, updateWellnessBulk, uploadWellness |
| Routes | 4 | listAthleteRoutes, getAthleteRoute |
| Weather | 3 | getForecast, updateWeatherConfig |
| Shared events | 1 | getSharedEvent |
Notes on intentional choices:
- JSON only: the API offers CSV twins of some list endpoints via a
{ext}path suffix. The SDK strips those toggles and always speaks JSON. For CSV exports, call the endpoint directly with.csv(curl works great). - Workout downloads keep their
extparameter because there it selects the actual file format:.zwo,.mrc,.erg, or.fit.
Intervals.icu is run by a small team and offers this API for free. Published limits (see the forum thread): about 5,000 requests per day per API key, 2,500 per rolling 15 minutes, and 10 requests per second per IP. Cache what you can and back off on HTTP 429.
The whole pipeline is reproducible from this repo:
bun install
bun run spec # fetch the latest upstream spec + normalize (fails loudly on drift)
bun run generate # regenerate src/gen from the normalized spec
bun run typecheck # tsc --noEmit
bun run test # unit tests with a stubbed fetch (no network)
bun run build # dual ESM/CJS bundle + type declarationsspec/normalize.ts documents every transformation applied to the upstream spec (partial-segment {ext} params, multipart fixes, wildcard media types, operationId renames). A scheduled CI job re-fetches the spec weekly and fails when Intervals.icu ships API changes, so the SDK stays honest about staleness.
Semantic versioning. While the package is 0.x, minor versions may contain breaking changes; they will always be called out in the changelog.
Issues and PRs are welcome. See CONTRIBUTING.md for the development setup and guidelines.
- Announcement and discussion thread for this SDK on the Intervals.icu forum
- Intervals.icu API docs and the API forum thread
- mvilanova/intervals-mcp-server: MCP server for using Intervals.icu data with AI assistants
- Community client libraries in other styles: paladini/node-intervals-icu, yerzhansa/intervals-icu-api
- Intervals.icu, built by David Tinker, for an outstanding training platform and a clean, well-documented public API. If you find this SDK useful, consider supporting Intervals.icu with a subscription.
- @hey-api/openapi-ts for the code generation toolchain.
MIT. The Intervals.icu name belongs to Intervals.icu; this project just points at their public API.