Skip to content

Quick Start

Build your first iRacing application in 5 minutes! This guide will walk you through creating a simple app that fetches and displays your racing statistics.

A simple Node.js application that:

  • Connects to the iRacing Data API
  • Fetches your member information
  • Displays your recent race results
  • Shows your career statistics
  1. Create a new project

    Terminal window
    mkdir my-iracing-app
    cd my-iracing-app
    npm init -y
  2. Install the Data Client

    Terminal window
    npm install iracing-data-client
    npm install --save-dev typescript @types/node tsx
  3. Set up environment variables

    Create a .env file in your project root. You’ll need OAuth credentials from iRacing — see OAuth Client Credentials to register.

    IRACING_CLIENT_ID=your-client-id
    IRACING_CLIENT_SECRET=your-client-secret
    IRACING_USERNAME=your-iracing-email
    IRACING_PASSWORD=your-iracing-password
  4. Create your application

    Create a file named app.ts:

    app.ts
    import { IRacingDataClient, IRacingError } from 'iracing-data-client';
    async function main() {
    try {
    // Initialize the 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!,
    },
    });
    // Fetch member info
    console.log('🏁 Connecting to iRacing...\n');
    const memberInfo = await iracing.member.info();
    console.log(`👋 Welcome, ${memberInfo.displayName}!`);
    console.log(`📊 Customer ID: ${memberInfo.custId}`);
    console.log(`📅 Member Since: ${new Date(memberInfo.memberSince).toLocaleDateString()}`);
    // Get recent races
    console.log('\n🏎️ Recent Races:');
    const recentRaces = await iracing.stats.memberRecentRaces({
    custId: memberInfo.custId
    });
    recentRaces.races.slice(0, 5).forEach(race => {
    console.log(`${race.seriesName} at ${race.trackName}`);
    console.log(` Finished P${race.finishPosition} (Started P${race.startPosition})`);
    console.log(` SOF: ${race.strengthOfField}\n`);
    });
    // Get career stats
    console.log('📈 Career Statistics:');
    const careerStats = await iracing.stats.memberCareer({
    custId: memberInfo.custId
    });
    console.log(` • Total Races: ${careerStats.stats[0]?.starts || 0}`);
    console.log(` • Total Wins: ${careerStats.stats[0]?.wins || 0}`);
    console.log(` • Average Incidents: ${careerStats.stats[0]?.avgIncidents || 0}`);
    } catch (error) {
    if (error instanceof IRacingError) {
    console.error('❌ iRacing API Error:', error.message);
    if (error.isMaintenanceMode) {
    console.log('🔧 iRacing is currently in maintenance mode.');
    }
    } else {
    console.error('❌ Unexpected error:', error);
    }
    }
    }
    main();
  5. Run your application

    Terminal window
    npx tsx app.ts

When you run the application, you should see something like:

🏁 Connecting to iRacing...
👋 Welcome, John Doe!
📊 Customer ID: 123456
📅 Member Since: 1/15/2020
🏎️ Recent Races:
• IMSA Sportscar Championship at Road America
Finished P3 (Started P5)
SOF: 2150
• GT3 Fixed at Spa-Francorchamps
Finished P1 (Started P2)
SOF: 1875
📈 Career Statistics:
• Total Races: 542
• Total Wins: 47
• Average Incidents: 3.2

Now that you have a working application, try adding more features:

// Get current season standings
const seasons = await iracing.season.list({
seasonYear: new Date().getFullYear(),
seasonQuarter: Math.ceil((new Date().getMonth() + 1) / 3)
});
const mySeries = seasons.find(s =>
s.seriesName === 'GT3 Fixed'
);
if (mySeries) {
const standings = await iracing.stats.seasonDriverStandings({
seasonId: mySeries.seasonId,
carClassId: mySeries.carClassIds[0]
});
const myStanding = standings.driverStandings.find(
d => d.custId === memberInfo.custId
);
console.log(`Position: ${myStanding?.position}`);
console.log(`Points: ${myStanding?.points}`);
}

Here are more complete example applications:

import { Client, GatewayIntentBits } from 'discord.js';
import { IRacingDataClient } from 'iracing-data-client';
const discord = new Client({
intents: [GatewayIntentBits.Guilds],
});
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!,
},
});
discord.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'driver') {
const custId = interaction.options.getInteger('id')!;
const member = await iracing.member.get({
custIds: [custId],
});
await interaction.reply({
embeds: [{
title: member.members[0].displayName,
fields: [
{ name: 'Last Login', value: member.members[0].lastLogin },
{ name: 'Member Since', value: member.members[0].memberSince }
]
}]
});
}
});
import express from 'express';
import { IRacingDataClient } from 'iracing-data-client';
const app = express();
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!,
},
});
// Cache middleware
const cache = new Map();
const cacheMiddleware = (duration: number) => {
return (req, res, next) => {
const key = req.originalUrl;
const cached = cache.get(key);
if (cached && cached.expires > Date.now()) {
return res.json(cached.data);
}
res.sendResponse = res.json;
res.json = (data) => {
cache.set(key, {
data,
expires: Date.now() + duration * 1000
});
return res.sendResponse(data);
};
next();
};
};
app.get('/api/member/:id',
cacheMiddleware(300), // Cache for 5 minutes
async (req, res) => {
try {
const data = await iracing.member.get({
custIds: [parseInt(req.params.id)]
});
res.json(data);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
);
app.listen(3000, () => {
console.log('API server running on port 3000');
});

✅ Do's

  • Store credentials in environment variables
  • Implement error handling for all API calls
  • Cache responses to reduce API load
  • Use TypeScript for better type safety
  • Handle maintenance mode gracefully

❌ Don'ts

  • Never hardcode credentials in source code
  • Don’t make excessive API calls (respect rate limits)
  • Avoid exposing credentials in client-side code
  • Don’t ignore error responses
  • Never share your authentication tokens
async function safeApiCall<T>(
apiCall: () => Promise<T>
): Promise<T | null> {
try {
return await apiCall();
} catch (error) {
if (error instanceof IRacingError) {
if (error.isMaintenanceMode) {
console.log('iRacing is in maintenance mode');
} else if (error.isRateLimited) {
console.log('Rate limited, waiting...');
await new Promise(r => setTimeout(r, 60000));
return safeApiCall(apiCall);
}
}
console.error('API call failed:', error);
return null;
}
}
// Usage
const data = await safeApiCall(() =>
iracing.member.info()
);
async function getMembersBatch(custIds: number[]) {
const batchSize = 50; // iRacing limit
const results = [];
for (let i = 0; i < custIds.length; i += batchSize) {
const batch = custIds.slice(i, i + batchSize);
const data = await iracing.member.get({
custIds: batch
});
results.push(...data.members);
// Respect rate limits
if (i + batchSize < custIds.length) {
await new Promise(r => setTimeout(r, 100));
}
}
return results;
}

Congratulations! You’ve built your first iRacing application. Here’s what to explore next: