Skip to content

Client API

The client provides the core functionality for connecting to the iRacing Data API, handling OAuth2 authentication, and managing requests.

The main entry point for using the Data Client.

import { IRacingDataClient } from 'iracing-data-client';
const iracing = new IRacingDataClient(options);
interface IRacingClientOptions {
auth: AuthConfig;
fetchFn?: FetchLike;
validateParams?: boolean;
validateSemanticParams?: boolean;
stores?: HttpClientStores;
}

Type: AuthConfig (required)

Authentication configuration. A discriminated union of two OAuth2 flows:

  • PasswordLimitedAuth — for scripts, CLIs, and backend services
  • AuthorizationCodeAuth — for web apps acting on behalf of other users

See Authentication Configuration below for full details.

const iracing = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
username: process.env.IRACING_USERNAME!,
password: process.env.IRACING_PASSWORD!,
},
});

The auth option accepts a discriminated union (AuthConfig) of two OAuth2 flows.

For scripts, CLI tools, and backend services running on behalf of the registered client owner. This is the most common flow.

interface PasswordLimitedAuth {
type: 'password-limited';
clientId: string;
clientSecret: string;
username: string;
password: string;
tokens?: {
accessToken: string;
refreshToken?: string;
expiresAt?: number;
};
onTokenRefresh?: OnTokenRefresh;
}

The client authenticates automatically on the first API call. You can optionally provide pre-obtained tokens to skip the initial token request, and an onTokenRefresh callback to persist tokens for reuse across sessions.

const iracing = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
username: process.env.IRACING_USERNAME!,
password: process.env.IRACING_PASSWORD!,
onTokenRefresh: async (tokens) => {
await fs.writeFile('tokens.json', JSON.stringify(tokens));
},
},
});

For web and desktop applications that access data on behalf of other users. Requires completing the OAuth flow externally before creating the client.

interface AuthorizationCodeAuth {
type: 'authorization-code';
clientId: string;
clientSecret: string;
tokens: {
accessToken: string;
refreshToken?: string;
expiresAt?: number;
};
onTokenRefresh?: OnTokenRefresh;
}

Use the exported helper functions to complete the flow:

import {
IRacingDataClient,
buildAuthorizationUrl,
exchangeAuthorizationCode,
} from 'iracing-data-client';
// Step 1: Build the authorization URL and redirect the user
const { url, state, pkce } = await buildAuthorizationUrl({
clientId: process.env.IRACING_CLIENT_ID!,
redirectUri: 'https://myapp.com/callback',
usePKCE: true,
});
// Step 2: Exchange the authorization code for tokens (in your callback handler)
const tokens = await exchangeAuthorizationCode({
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
code: callbackCode,
redirectUri: 'https://myapp.com/callback',
codeVerifier: pkce?.verifier,
});
// Step 3: Create the client with the obtained tokens
const iracing = new IRacingDataClient({
auth: {
type: 'authorization-code',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
tokens: {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
},
onTokenRefresh: (newTokens) => {
// Persist refreshed tokens for the user's session
},
},
});

The SDK instance provides access to all service classes:

const iracing = new IRacingDataClient(options);
// Access services as properties
iracing.car // CarService
iracing.carclass // CarclassService
iracing.constants // ConstantsService
iracing.driverStatsByCategory // DriverStatsByCategoryService
iracing.hosted // HostedService
iracing.league // LeagueService
iracing.lookup // LookupService
iracing.member // MemberService
iracing.results // ResultsService
iracing.season // SeasonService
iracing.series // SeriesService
iracing.stats // StatsService
iracing.team // TeamService
iracing.timeAttack // TimeAttackService
iracing.track // TrackService

The underlying client that handles HTTP requests. You typically don’t interact with this directly.

Automatic OAuth2 Authentication

Handles the complete OAuth2 flow including token acquisition, Bearer header injection, and automatic token refresh.

S3 Link Following

Automatically follows presigned S3 URLs returned by the API for accessing data.

Pluggable Stores

Supports optional caching, deduplication, and rate limiting via @http-client-toolkit/core stores.

Parameter Transformation

Automatically converts between camelCase (SDK) and snake_case (API) formats.

The client provides these internal methods (used by services):

Makes a GET request to the API.

async get<T>(
url: string,
options?: {
params?: Record<string, unknown>;
schema?: ZodSchema;
}
): Promise<T>

Returns true if the client has valid, non-expired tokens.

isAuthenticated(): boolean

Clears stored tokens, requiring re-authentication on the next request.

clearTokens(): void

Error thrown for iRacing Data API errors (non-2xx HTTP responses).

class IRacingError extends Error {
status?: number; // HTTP status code
statusText?: string; // HTTP status text
responseData?: unknown; // Response body
// Computed helper properties
get isMaintenanceMode(): boolean; // True if 503 maintenance
get isRateLimited(): boolean; // True if 429 rate limit
get isUnauthorized(): boolean; // True if 401 unauthorized
}

Error thrown for OAuth-related failures (token requests, invalid credentials).

class OAuthError extends Error {
code: OAuthErrorCode | string;
description?: string;
uri?: string;
get isInvalidGrant(): boolean;
get isInvalidClient(): boolean;
get isRateLimited(): boolean;
get isUnauthorizedClient(): boolean;
}

Error thrown when token refresh fails and no recovery is possible. Extends OAuthError.

class TokenRefreshError extends OAuthError {
cause?: Error;
}
import { IRacingError } from 'iracing-data-client';
try {
const data = await iracing.member.info();
} catch (error) {
if (error instanceof IRacingError) {
console.error(`API Error: ${error.message}`);
console.error(`Status: ${error.status}`);
}
}

The SDK exports all TypeScript types for responses and parameters:

import type {
// Auth types
AuthConfig,
PasswordLimitedAuth,
AuthorizationCodeAuth,
TokenResponse,
OnTokenRefresh,
FetchLike,
// Client types
IRacingClientOptions,
HttpClientStores,
// Response types
MemberInfoResponse,
CarGetResponse,
TrackAssetsResponse,
// Parameter types
MemberGetParams,
ResultsSearchSeriesParams,
StatsSeasonDriverStandingsParams,
} from 'iracing-data-client';
// Classes — use value imports for instanceof checks
import {
IRacingError,
OAuthError,
TokenRefreshError,
buildAuthorizationUrl,
exchangeAuthorizationCode,
} from 'iracing-data-client';
import { IRacingDataClient } from 'iracing-data-client';
const iracing = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
username: process.env.IRACING_USERNAME!,
password: process.env.IRACING_PASSWORD!,
},
});
import * as fs from 'node:fs/promises';
import { IRacingDataClient } from 'iracing-data-client';
const iracing = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
username: process.env.IRACING_USERNAME!,
password: process.env.IRACING_PASSWORD!,
onTokenRefresh: async (tokens) => {
await fs.writeFile('tokens.json', JSON.stringify(tokens));
},
},
});
import * as fs from 'node:fs/promises';
import { IRacingDataClient } from 'iracing-data-client';
const savedTokens = JSON.parse(await fs.readFile('tokens.json', 'utf-8'));
const iracing = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.IRACING_CLIENT_ID!,
clientSecret: process.env.IRACING_CLIENT_SECRET!,
username: process.env.IRACING_USERNAME!,
password: process.env.IRACING_PASSWORD!,
tokens: {
accessToken: savedTokens.access_token,
refreshToken: savedTokens.refresh_token,
},
onTokenRefresh: async (tokens) => {
await fs.writeFile('tokens.json', JSON.stringify(tokens));
},
},
});
const iracing = new IRacingDataClient({
auth: { /* ... */ },
validateParams: false,
validateSemanticParams: false,
});
const mainAccount = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.MAIN_CLIENT_ID!,
clientSecret: process.env.MAIN_CLIENT_SECRET!,
username: process.env.MAIN_USERNAME!,
password: process.env.MAIN_PASSWORD!,
},
});
const altAccount = new IRacingDataClient({
auth: {
type: 'password-limited',
clientId: process.env.ALT_CLIENT_ID!,
clientSecret: process.env.ALT_CLIENT_SECRET!,
username: process.env.ALT_USERNAME!,
password: process.env.ALT_PASSWORD!,
},
});

When a CacheStore is provided via the stores option, the client caches S3 responses with TTL derived from the presigned URL expiry:

  • Cached responses are returned immediately without making a new request
  • Cache TTL is automatically calculated from the S3 link’s expires field
  • Without a stores.cache, no caching is performed

See the Caching Guide for setup and store implementations.

Best practices:

  • Provide a RateLimitStore via stores.rateLimit for automatic throttling
  • Implement exponential backoff for retries
  • Batch requests where the API supports it

The client reuses the same authenticated session across all requests:

const iracing = new IRacingDataClient(options);
// All these requests share the same OAuth tokens
await iracing.member.info();
await iracing.car.get();
await iracing.track.assets();