Auto.dev

Examples

Practical code examples and integration patterns for Auto.dev APIs

Examples & Integration Patterns

Explore real-world examples and integration patterns for Auto.dev APIs. These examples demonstrate best practices, error handling, and how to combine multiple APIs for powerful automotive applications.

TypeScript SDK Examples

Basic VIN Decode with Type Safety

Here's a complete TypeScript example showing proper error handling and type safety:

// Auto.dev VIN Decode SDK Example
interface ApiError {
  code: string
  message: string
  details?: string
}

interface VinDecodeSuccess {
  success: true
  data: {
    vin: string
    year: number
    make: string
    model: string
    trim: string
    engine: string
    transmission: string
    drivetrain: string
    fuelType: string
    doors: number
    seats: number
    confidence: number
  }
}

interface VinDecodeError {
  success: false
  error: ApiError
}

type VinDecodeResponse = VinDecodeSuccess | VinDecodeError

class AutoDevClient {
  constructor(private apiKey: string) {}

  async decodeVin(vin: string): Promise<VinDecodeResponse> {
    const response = await fetch(`https://api.auto.dev/vin/decode/${vin}`, {
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
      },
    })

    return response.json()
  }
}

// Usage with proper type checking
const client = new AutoDevClient('YOUR_API_KEY')
const result = await client.decodeVin('1HGBH41JXMN109186')

if (result.success) {
  // TypeScript knows this is VinDecodeSuccess
  console.log(`Vehicle: ${result.data.year} ${result.data.make} ${result.data.model}`)

  // Type-safe property access
  const engine = result.data.engine

  // Confidence score validation
  if (result.data.confidence > 0.95) {
    console.log('High confidence decode')
  }
} else {
  // TypeScript knows this is VinDecodeError
  console.error(`Error ${result.error.code}: ${result.error.message}`)
}

Complete Vehicle Profile Builder

Combine multiple APIs to build a comprehensive vehicle profile:

interface VehicleProfile {
  basic: VinDecodeResponse
  specifications?: SpecificationsResponse
  listings?: VehicleListingsResponse
  photos?: VehiclePhotosResponse
  recalls?: VehicleRecallsResponse
}

class VehicleProfileBuilder {
  constructor(private client: AutoDevClient) {}

  async buildProfile(vin: string): Promise<VehicleProfile> {
    const profile: VehicleProfile = {
      basic: await this.client.decodeVin(vin),
    }

    // Only fetch additional data if VIN decode was successful
    if (profile.basic.success) {
      const { year, make, model } = profile.basic.data

      // Fetch specifications (Growth/Scale plans)
      try {
        profile.specifications = await this.client.getSpecifications({ year, make, model })
      } catch (error) {
        console.warn('Specifications not available:', error)
      }

      // Fetch current listings
      try {
        profile.listings = await this.client.getListings({ year, make, model })
      } catch (error) {
        console.warn('Listings not available:', error)
      }

      // Fetch vehicle photos
      try {
        profile.photos = await this.client.getPhotos(vin)
      } catch (error) {
        console.warn('Photos not available:', error)
      }

      // Check for recalls (Growth/Scale plans)
      try {
        profile.recalls = await this.client.getRecalls({ year, make, model })
      } catch (error) {
        console.warn('Recalls not available:', error)
      }
    }

    return profile
  }
}

// Usage
const builder = new VehicleProfileBuilder(client)
const profile = await builder.buildProfile('1HGBH41JXMN109186')

if (profile.basic.success) {
  console.log('Vehicle Profile:', {
    vehicle: `${profile.basic.data.year} ${profile.basic.data.make} ${profile.basic.data.model}`,
    hasSpecs: !!profile.specifications?.success,
    listingCount: profile.listings?.success ? profile.listings.data.length : 0,
    photoCount: profile.photos?.success ? profile.photos.data.retail.length : 0,
    recallCount: profile.recalls?.success ? profile.recalls.data.length : 0,
  })
}

Real-World Use Cases

Dealership Inventory Management

Automatically populate vehicle details when adding inventory:

class DealershipInventory {
  constructor(private autodev: AutoDevClient, private inventory: InventoryDB) {}

  async addVehicle(vin: string, askingPrice: number): Promise<void> {
    // Decode VIN for basic info
    const vinData = await this.autodev.decodeVin(vin)
    if (!vinData.success) {
      throw new Error(`Invalid VIN: ${vinData.error.message}`)
    }

    // Get current market listings for pricing intelligence
    const listings = await this.autodev.getListings({
      year: vinData.data.year,
      make: vinData.data.make,
      model: vinData.data.model,
      trim: vinData.data.trim,
    })

    let marketAnalysis = null
    if (listings.success) {
      const prices = listings.data.map((l) => l.retailListing.price)
      marketAnalysis = {
        averagePrice: prices.reduce((a, b) => a + b, 0) / prices.length,
        minPrice: Math.min(...prices),
        maxPrice: Math.max(...prices),
        totalListings: prices.length,
      }
    }

    // Get specifications for detailed description
    const specs = await this.autodev.getSpecifications({
      year: vinData.data.year,
      make: vinData.data.make,
      model: vinData.data.model,
    })

    // Save to inventory with enriched data
    await this.inventory.save({
      vin,
      askingPrice,
      ...vinData.data,
      marketAnalysis,
      specifications: specs.success ? specs.data : null,
      addedAt: new Date(),
      status: 'available',
    })
  }
}

Insurance Quote Calculator

Calculate insurance premiums using vehicle data:

class InsuranceCalculator {
  constructor(private autodev: AutoDevClient) {}

  async calculatePremium(vin: string, driverAge: number, location: string): Promise<number> {
    // Get vehicle details
    const vehicle = await this.autodev.decodeVin(vin)
    if (!vehicle.success) throw new Error('Invalid VIN')

    // Get safety ratings
    const recalls = await this.autodev.getRecalls({
      year: vehicle.data.year,
      make: vehicle.data.make,
      model: vehicle.data.model,
    })

    // Base premium calculation
    let premium = this.getBasePremium(vehicle.data.year, vehicle.data.make)

    // Safety adjustments
    if (vehicle.data.safety?.nhtsa_overall >= 4) {
      premium *= 0.9 // 10% discount for high safety rating
    }

    // Recall penalty
    if (recalls.success && recalls.data.length > 0) {
      premium *= 1.05 // 5% increase for active recalls
    }

    // Age and location adjustments
    premium *= this.getAgeMultiplier(driverAge)
    premium *= this.getLocationMultiplier(location)

    return Math.round(premium * 100) / 100
  }

  private getBasePremium(year: number, make: string): number {
    const age = new Date().getFullYear() - year
    const makeMultipliers: Record<string, number> = {
      Honda: 0.8,
      Toyota: 0.8,
      BMW: 1.3,
      'Mercedes-Benz': 1.4,
      Porsche: 1.8,
    }

    return (1000 - age * 50) * (makeMultipliers[make] || 1.0)
  }

  private getAgeMultiplier(age: number): number {
    if (age < 25) return 1.5
    if (age < 65) return 1.0
    return 1.2
  }

  private getLocationMultiplier(location: string): number {
    // Simplified location-based multiplier
    const locationMultipliers: Record<string, number> = {
      urban: 1.2,
      suburban: 1.0,
      rural: 0.8,
    }
    return locationMultipliers[location] || 1.0
  }
}

Fleet Management Dashboard

Track and analyze fleet vehicles:

interface FleetVehicle {
  id: string
  vin: string
  assignedDriver?: string
  mileage: number
  lastService: Date
  profile?: VehicleProfile
}

class FleetManager {
  constructor(private autodev: AutoDevClient) {}

  async analyzeFleet(vehicles: FleetVehicle[]): Promise<FleetAnalysis> {
    const analysis: FleetAnalysis = {
      totalVehicles: vehicles.length,
      averageAge: 0,
      maintenanceAlerts: [],
      recallAlerts: [],
      costAnalysis: null,
    }

    // Enrich each vehicle with Auto.dev data
    for (const vehicle of vehicles) {
      vehicle.profile = await this.buildVehicleProfile(vehicle.vin)

      if (vehicle.profile.basic.success) {
        const age = new Date().getFullYear() - vehicle.profile.basic.data.year
        analysis.averageAge += age

        // Check for recalls
        if (vehicle.profile.recalls?.success && vehicle.profile.recalls.data.length > 0) {
          analysis.recallAlerts.push({
            vehicleId: vehicle.id,
            vin: vehicle.vin,
            recalls: vehicle.profile.recalls.data.length,
          })
        }

        // Maintenance predictions based on mileage and age
        if (vehicle.mileage > 75000 || age > 8) {
          analysis.maintenanceAlerts.push({
            vehicleId: vehicle.id,
            reason: vehicle.mileage > 75000 ? 'High Mileage' : 'Vehicle Age',
            priority: vehicle.mileage > 100000 ? 'High' : 'Medium',
          })
        }
      }
    }

    analysis.averageAge = analysis.averageAge / vehicles.length

    return analysis
  }

  private async buildVehicleProfile(vin: string): Promise<VehicleProfile> {
    // Use the VehicleProfileBuilder from earlier example
    const builder = new VehicleProfileBuilder(this.autodev)
    return builder.buildProfile(vin)
  }
}

interface FleetAnalysis {
  totalVehicles: number
  averageAge: number
  maintenanceAlerts: Array<{
    vehicleId: string
    reason: string
    priority: 'Low' | 'Medium' | 'High'
  }>
  recallAlerts: Array<{
    vehicleId: string
    vin: string
    recalls: number
  }>
  costAnalysis: any
}

Integration Patterns

Rate Limiting and Retry Logic

Handle API rate limits gracefully:

class RateLimitedClient {
  private requestQueue: Array<() => Promise<any>> = []
  private isProcessing = false
  private lastRequestTime = 0
  private minInterval = 100 // Minimum time between requests in ms

  constructor(private autodev: AutoDevClient) {}

  async queueRequest<T>(requestFn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.requestQueue.push(async () => {
        try {
          const result = await this.executeWithRetry(requestFn)
          resolve(result)
        } catch (error) {
          reject(error)
        }
      })

      if (!this.isProcessing) {
        this.processQueue()
      }
    })
  }

  private async processQueue(): Promise<void> {
    this.isProcessing = true

    while (this.requestQueue.length > 0) {
      const request = this.requestQueue.shift()!

      // Ensure minimum interval between requests
      const timeSinceLastRequest = Date.now() - this.lastRequestTime
      if (timeSinceLastRequest < this.minInterval) {
        await this.sleep(this.minInterval - timeSinceLastRequest)
      }

      await request()
      this.lastRequestTime = Date.now()
    }

    this.isProcessing = false
  }

  private async executeWithRetry<T>(requestFn: () => Promise<T>, maxRetries = 3, baseDelay = 1000): Promise<T> {
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        return await requestFn()
      } catch (error: any) {
        if (attempt === maxRetries) throw error

        // Exponential backoff for rate limit errors
        if (error.status === 429) {
          const delay = baseDelay * Math.pow(2, attempt)
          await this.sleep(delay)
          continue
        }

        throw error
      }
    }

    throw new Error('Max retries exceeded')
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms))
  }
}

Caching Strategy

Implement intelligent caching for frequently accessed data:

interface CacheEntry<T> {
  data: T
  timestamp: number
  ttl: number
}

class CachedAutoDevClient {
  private cache = new Map<string, CacheEntry<any>>()
  private readonly DEFAULT_TTL = 1000 * 60 * 60 // 1 hour

  constructor(private autodev: AutoDevClient) {}

  async decodeVin(vin: string): Promise<VinDecodeResponse> {
    const cacheKey = `vin:${vin}`
    const cached = this.getFromCache<VinDecodeResponse>(cacheKey)

    if (cached) {
      return cached
    }

    const result = await this.autodev.decodeVin(vin)

    // Only cache successful responses
    if (result.success) {
      this.setCache(cacheKey, result, this.DEFAULT_TTL)
    }

    return result
  }

  async getSpecifications(params: { year: number; make: string; model: string }): Promise<any> {
    const cacheKey = `specs:${params.year}:${params.make}:${params.model}`
    const cached = this.getFromCache(cacheKey)

    if (cached) {
      return cached
    }

    const result = await this.autodev.getSpecifications(params)

    if (result.success) {
      // Specs change less frequently, longer TTL
      this.setCache(cacheKey, result, this.DEFAULT_TTL * 24) // 24 hours
    }

    return result
  }

  private getFromCache<T>(key: string): T | null {
    const entry = this.cache.get(key)
    if (!entry) return null

    if (Date.now() - entry.timestamp > entry.ttl) {
      this.cache.delete(key)
      return null
    }

    return entry.data
  }

  private setCache<T>(key: string, data: T, ttl: number): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now(),
      ttl,
    })
  }

  clearCache(): void {
    this.cache.clear()
  }
}

Error Handling Best Practices

Comprehensive Error Handler

enum AutoDevErrorCode {
  INVALID_VIN = 'INVALID_VIN',
  VIN_NOT_FOUND = 'VIN_NOT_FOUND',
  RATE_LIMIT = 'RATE_LIMIT',
  UNAUTHORIZED = 'UNAUTHORIZED',
  PLAN_LIMIT = 'PLAN_LIMIT',
  NETWORK_ERROR = 'NETWORK_ERROR',
}

class AutoDevError extends Error {
  constructor(public code: AutoDevErrorCode, message: string, public statusCode?: number, public retryable = false) {
    super(message)
    this.name = 'AutoDevError'
  }
}

class ErrorHandler {
  static handleApiError(error: any): AutoDevError {
    if (error.status === 401) {
      return new AutoDevError(AutoDevErrorCode.UNAUTHORIZED, 'Invalid API key or authentication failed', 401)
    }

    if (error.status === 429) {
      return new AutoDevError(
        AutoDevErrorCode.RATE_LIMIT,
        'Rate limit exceeded',
        429,
        true, // Retryable
      )
    }

    if (error.status === 403) {
      return new AutoDevError(AutoDevErrorCode.PLAN_LIMIT, 'API not available on current plan', 403)
    }

    if (!navigator.onLine) {
      return new AutoDevError(AutoDevErrorCode.NETWORK_ERROR, 'Network connection unavailable', undefined, true)
    }

    // Generic error
    return new AutoDevError(AutoDevErrorCode.NETWORK_ERROR, error.message || 'Unknown API error', error.status)
  }

  static async withErrorHandling<T>(operation: () => Promise<T>, fallback?: T): Promise<T | null> {
    try {
      return await operation()
    } catch (error) {
      const autoDevError = this.handleApiError(error)

      console.error(`Auto.dev API Error [${autoDevError.code}]:`, autoDevError.message)

      // Return fallback value if provided
      if (fallback !== undefined) {
        return fallback
      }

      // Re-throw if no fallback
      throw autoDevError
    }
  }
}

// Usage
const safeVinDecode = await ErrorHandler.withErrorHandling(
  () => client.decodeVin('INVALID_VIN'),
  null, // Fallback to null on error
)

if (safeVinDecode?.success) {
  console.log('VIN decoded successfully')
} else {
  console.log('VIN decode failed, using fallback')
}

These examples demonstrate production-ready patterns for integrating Auto.dev APIs into real applications with proper error handling, caching, rate limiting, and type safety.