A fully typed TypeScript wrapper for the Yahoo Fantasy Sports API, rebuilt around a composable request builder.
This project has been fully refactored around a single core concept: build Yahoo Fantasy API paths compositionally, with strong TypeScript guidance.
- Unified request surface via
client.request() - Type-aware chainable builder for resources, collections, and sub-resources
- Cleaner OAuth split:
- OAuth 1.0 (2-legged) for public mode
- OAuth 2.0 for user-authenticated mode
- First-class path composition support (
buildPath()) - Improved response typing routed by query shape
- Better internal separation of concerns (client, request graph, response routes, parsers)
- Keep Yahoo URL semantics visible instead of hiding them behind opaque helpers
- Make composition predictable and debuggable
- Provide useful typing without fighting real-world Yahoo response variability
npm install yfs-api
# or
yarn add yfs-api
# or
pnpm add yfs-api
# or
bun add yfs-apiUse this for public data like game metadata and game-level player search.
import { YahooFantasyClient } from "yfs-api";
const client = new YahooFantasyClient({
clientId: process.env.YAHOO_CLIENT_ID!,
clientSecret: process.env.YAHOO_CLIENT_SECRET!,
publicMode: true,
});
const game = await client.request().game("nhl").execute();
const players = await client
.request()
.game("nhl")
.players()
.search("mcdavid")
.count(10)
.execute();
console.log(game.game.name);
console.log(players.players?.length ?? 0);Use this for user-specific data like your teams, rosters, standings, and league data.
import { YahooFantasyClient } from "yfs-api";
const client = new YahooFantasyClient({
clientId: process.env.YAHOO_CLIENT_ID!,
clientSecret: process.env.YAHOO_CLIENT_SECRET!,
redirectUri: "oob", // or your callback URL
});
const authUrl = client.getAuthUrl();
console.log("Authorize here:", authUrl);
await client.authenticate(authorizationCode);
const myTeams = await client
.request()
.users()
.useLogin()
.games()
.teams()
.execute();
console.log(myTeams.users.length);import { YahooFantasyClient } from "yfs-api";
import * as fs from "node:fs/promises";
const tokenStorage = {
save: async (tokens) => {
await fs.writeFile(".tokens.json", JSON.stringify(tokens, null, 2));
},
load: async () => {
try {
const data = await fs.readFile(".tokens.json", "utf8");
return JSON.parse(data);
} catch {
return null;
}
},
clear: async () => {
await fs.rm(".tokens.json", { force: true });
},
};
const client = new YahooFantasyClient(
{
clientId: process.env.YAHOO_CLIENT_ID!,
clientSecret: process.env.YAHOO_CLIENT_SECRET!,
redirectUri: "oob",
},
tokenStorage,
);
await client.loadTokens();
if (!client.isAuthenticated()) {
const authUrl = client.getAuthUrl();
console.log("Visit:", authUrl);
await client.authenticate(code);
}The request builder models Yahoo path composition directly:
const result = await client
.request()
.users()
.useLogin()
.games()
.gameKeys("nfl")
.leagues()
.execute();Generated path:
/users;use_login=1/games;game_keys=nfl/leagues
game(key)league(key)team(key)player(key)users()games()
- Collections:
leagues(),teams(),players(),transactions(),drafts(),games() - Sub-resources:
settings(),standings(),scoreboard(),roster(),matchups(),stats(),ownership(),percentOwned(),draftAnalysis(),statCategories(),positionTypes(),gameWeeks() - Params:
out(),position(),status(),sort(),count(),start(),search(),week(),date(),gameKeys(),leagueKeys(),teamKeys(),playerKeys(),param(),params() - Execution:
execute(),post(),put(),delete(),buildPath()
const path = client
.request()
.league("423.l.12345")
.players()
.position("C")
.status("FA")
.count(25)
.buildPath();
console.log(path);
// /league/423.l.12345/players;position=C;status=FA;count=25- Enable with
publicMode: true - No
redirectUrirequired - No user authorization step
- Good for public resources and metadata
- Requires
redirectUri - Use
getAuthUrl()thenauthenticate(code) - Access token refresh supported automatically during requests
- Optional token storage via
TokenStorage
- NHL: most heavily exercised in integration tests
- NFL: supported, fewer real-world validation cycles
- NBA: supported, fewer real-world validation cycles
- MLB: supported, fewer real-world validation cycles
- Integration Test Setup
- OAuth 2.0 Implementation
- URL Pattern Guide
- Auth Flow Helper
- Yahoo API Reference Notes
- Public endpoints (OAuth 1.0)
- OAuth 2.0 auth flow
- Client endpoint walk-through
- Composable builder usage
- Token storage helper
- Node.js >= 18
- Bun >= 1.0.0
# Install deps
bun install
# Type check
npm run type-check
# Unit tests
npm test
# Integration tests
npm run test:integration
# Lint / format
npm run lint
npm run format
# Build
npm run buildyfs-api/
├── src/
│ ├── client/ # OAuth clients, HTTP transport, top-level client
│ ├── request/ # Composable request builder
│ ├── types/ # Resource, response, and query typing graph
│ └── utils/ # Parsing and validation utilities
├── tests/
│ ├── unit/
│ ├── integration/
│ └── fixtures/
├── examples/
├── docs/
└── scripts/
If you used older, non-builder style helpers, migrate to the builder-first API:
- Prefer
client.request().<resource>()...for all reads/writes - Use
buildPath()to verify generated Yahoo paths during migration - Replace ad-hoc query string assembly with chain methods and
param()/params() - Keep response handling aligned to normalized camelCase payload shapes
- Transaction write flows are available but still less field-tested than read flows
- Some sport-specific typing edges may still surface in less common endpoints
- Yahoo response shape inconsistencies can still require occasional defensive checks
Contributions are welcome.
- Follow existing style and architecture patterns
- Add or update tests for behavioral changes
- Update docs/examples with API changes
- Validate against real Yahoo API behavior when possible
MIT License - see LICENSE file for details
Built by jbru for the fantasy sports community 🏒⚾🏈🏀
See CHANGELOG.md.
Last updated: 2026-03-12