Skip to content

Fetching Member Data

This guide demonstrates various ways to fetch and work with member data using the iRacing Data Client SDK.

Fetch information about the authenticated member:

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!,
},
});
async function getCurrentMember() {
const info = await iracing.member.info();
console.log('Name:', info.displayName);
console.log('Customer ID:', info.custId);
console.log('Member Since:', info.memberSince);
console.log('Club:', info.clubName);
// Check licenses
info.licenses.forEach(license => {
const sr = (license.safetyRating / 100).toFixed(2);
console.log(`${license.categoryName}: Class ${license.licenseLevel} (SR: ${sr})`);
});
}

Fetch data for specific members by customer ID:

async function getMembersByIds(custIds: number[]) {
const response = await iracing.member.get({
custIds,
includeLicenses: true
});
return response.members.map(member => ({
id: member.custId,
name: member.displayName,
irating: member.licenses[0]?.irating || 0,
safetyRating: member.licenses[0]?.safetyRating || 0
}));
}
// Get multiple members
const members = await getMembersByIds([123456, 789012, 345678]);
members.forEach(m => {
console.log(`${m.name}: iR ${m.irating}`);
});

Build a comprehensive member profile:

interface CompleteMemberProfile {
basic: MemberInfo;
stats: MemberCareerStats;
recentRaces: RecentRace[];
awards: Award[];
bestLaps: BestLap[];
}
async function getCompleteProfile(custId: number): Promise<CompleteMemberProfile> {
// Fetch all data in parallel for efficiency
const [basic, career, recent, awards, bests] = await Promise.all([
iracing.member.get({ custIds: [custId] }),
iracing.stats.memberCareer({ custId }),
iracing.stats.memberRecentRaces({ custId }),
iracing.member.awards({ custId }),
iracing.stats.memberBests({ custId, carId: 0 })
]);
return {
basic: basic.members[0],
stats: career.stats[0],
recentRaces: recent.races,
awards: awards.awards,
bestLaps: bests.stats
};
}
// Usage
const profile = await getCompleteProfile(123456);
console.log(`${profile.basic.displayName}'s Profile:`);
console.log(`Wins: ${profile.stats.wins}`);
console.log(`Recent races: ${profile.recentRaces.length}`);
console.log(`Awards earned: ${profile.awards.length}`);

Get comprehensive career statistics:

async function getCareerStats(custId: number) {
const career = await iracing.stats.memberCareer({ custId });
career.stats.forEach(stat => {
console.log(`\n${stat.categoryName}:`);
console.log(` Starts: ${stat.starts}`);
console.log(` Wins: ${stat.wins} (${((stat.wins / stat.starts) * 100).toFixed(1)}%)`);
console.log(` Top 5s: ${stat.top5s}`);
console.log(` Avg Start: ${stat.avgStartPosition}`);
console.log(` Avg Finish: ${stat.avgFinishPosition}`);
console.log(` Avg Incidents: ${stat.avgIncPerRace.toFixed(2)}`);
console.log(` Total Laps: ${stat.laps}`);
console.log(` Laps Led: ${stat.lapsLed} (${((stat.lapsLed / stat.laps) * 100).toFixed(1)}%)`);
});
}

Analyze recent race performance:

async function analyzeRecentPerformance(custId: number, limit = 10) {
const recent = await iracing.stats.memberRecentRaces({ custId });
const races = recent.races.slice(0, limit);
// Calculate statistics
const stats = races.reduce((acc, race) => {
acc.totalPositionsGained += race.startPosition - race.finishPosition;
acc.totalIncidents += race.incidents;
acc.wins += race.finishPosition === 1 ? 1 : 0;
acc.top5s += race.finishPosition <= 5 ? 1 : 0;
acc.dnfs += race.reasonOut !== 'Running' ? 1 : 0;
return acc;
}, {
totalPositionsGained: 0,
totalIncidents: 0,
wins: 0,
top5s: 0,
dnfs: 0
});
console.log(`Last ${limit} Races Analysis:`);
console.log(` Average positions gained: ${(stats.totalPositionsGained / limit).toFixed(1)}`);
console.log(` Average incidents: ${(stats.totalIncidents / limit).toFixed(2)}`);
console.log(` Win rate: ${((stats.wins / limit) * 100).toFixed(1)}%`);
console.log(` Top 5 rate: ${((stats.top5s / limit) * 100).toFixed(1)}%`);
console.log(` DNF rate: ${((stats.dnfs / limit) * 100).toFixed(1)}%`);
return stats;
}

Track iRating changes over time:

async function trackIRatingProgress(custId: number, categoryId: number) {
// Get chart data for iRating
const chartData = await iracing.member.chartData({
custId,
categoryId,
chartType: 1 // iRating chart
});
if (chartData.data.length < 2) {
console.log('Not enough data for tracking');
return;
}
// Analyze trends
const recent = chartData.data.slice(-30); // Last 30 data points
const startRating = recent[0].value;
const currentRating = recent[recent.length - 1].value;
const change = currentRating - startRating;
// Find peak and valley
const peak = Math.max(...recent.map(d => d.value));
const valley = Math.min(...recent.map(d => d.value));
console.log('iRating Analysis (Last 30 races):');
console.log(` Starting: ${startRating}`);
console.log(` Current: ${currentRating}`);
console.log(` Change: ${change > 0 ? '+' : ''}${change}`);
console.log(` Peak: ${peak}`);
console.log(` Valley: ${valley}`);
console.log(` Volatility: ${peak - valley}`);
// Calculate trend
const midpoint = Math.floor(recent.length / 2);
const firstHalf = recent.slice(0, midpoint).reduce((sum, d) => sum + d.value, 0) / midpoint;
const secondHalf = recent.slice(midpoint).reduce((sum, d) => sum + d.value, 0) / (recent.length - midpoint);
const trend = secondHalf > firstHalf ? 'upward' : 'downward';
console.log(` Trend: ${trend}`);
}

Find members by name or partial match:

async function searchMembers(searchTerm: string) {
const results = await iracing.lookup.drivers({
searchTerm,
leagueId: 0 // 0 for global search
});
return results.map(driver => ({
id: driver.custId,
name: driver.displayName,
helmet: driver.helmet,
club: driver.clubName
}));
}
// Search for drivers
const drivers = await searchMembers('John');
console.log(`Found ${drivers.length} drivers matching "John"`);

Get all members of a league:

async function getLeagueMembers(leagueId: number) {
const roster = await iracing.league.roster({
leagueId,
includeLicenses: true
});
// Sort by iRating
const sorted = roster.roster.sort((a, b) =>
(b.licenses[0]?.irating || 0) - (a.licenses[0]?.irating || 0)
);
return sorted.map((member, index) => ({
rank: index + 1,
name: member.displayName,
irating: member.licenses[0]?.irating || 0,
safetyRating: member.licenses[0]?.safetyRating || 0,
admin: member.isLeagueAdmin,
owner: member.isLeagueOwner
}));
}

Process multiple members efficiently:

class MemberBatchProcessor {
private cache = new Map<number, any>();
async processBatch(custIds: number[]) {
const results = [];
const uncached = [];
// Check cache first
for (const id of custIds) {
if (this.cache.has(id)) {
results.push(this.cache.get(id));
} else {
uncached.push(id);
}
}
// Fetch uncached in batches of 50 (API limit)
while (uncached.length > 0) {
const batch = uncached.splice(0, 50);
const response = await iracing.member.get({
custIds: batch,
includeLicenses: true
});
// Cache and collect results
response.members.forEach(member => {
this.cache.set(member.custId, member);
results.push(member);
});
// Rate limit protection
if (uncached.length > 0) {
await new Promise(r => setTimeout(r, 100));
}
}
return results;
}
clearCache() {
this.cache.clear();
}
}
// Usage
const processor = new MemberBatchProcessor();
const members = await processor.processBatch([
123456, 789012, 345678, /* ... more IDs ... */
]);

Handle member-specific errors:

async function safeMemberFetch(custId: number) {
try {
const response = await iracing.member.get({
custIds: [custId]
});
if (response.members.length === 0) {
console.error(`Member ${custId} not found`);
return null;
}
return response.members[0];
} catch (error) {
if (error instanceof IRacingError) {
if (error.status === 404) {
console.error(`Member ${custId} does not exist`);
} else if (error.isUnauthorized) {
console.error('Authentication required');
} else {
console.error(`API error: ${error.message}`);
}
}
return null;
}
}