the-odds-api 0.2.0

A Rust SDK for The Odds API - sports odds and scores
Documentation
# the-odds-api-rs

A Rust SDK for [The Odds API](https://the-odds-api.com/), providing access to sports betting odds, scores, and related data from bookmakers worldwide.

## Features

- Full coverage of The Odds API v4 endpoints
- Strongly-typed request parameters and responses
- Builder pattern for flexible request configuration
- Async/await support with `reqwest`
- API usage tracking via response headers
- Comprehensive error handling

## Installation

```bash
cargo add the_odds_api
```

Or add to your `Cargo.toml`:

```toml
[dependencies]
the_odds_api = "0.1"
tokio = { version = "1", features = ["full"] }
```

## Quick Start

```rust
use the_odds_api::{TheOddsApiClient, Region, Market};

#[tokio::main]
async fn main() -> the_odds_api::Result<()> {
    let client = TheOddsApiClient::new("your-api-key");

    // Get all in-season sports
    let sports = client.get_sports().await?;
    println!("Found {} sports", sports.data.len());

    // Get NFL odds from US bookmakers
    let odds = client
        .get_odds("americanfootball_nfl")
        .regions(&[Region::Us])
        .markets(&[Market::H2h, Market::Spreads])
        .send()
        .await?;

    // Check API usage
    println!("Requests remaining: {:?}", odds.usage.requests_remaining);

    Ok(())
}
```

## API Endpoints

### Sports

Get all available sports. **Free** - does not count against quota.

```rust
// Get in-season sports only
let sports = client.get_sports().await?;

// Get all sports including out-of-season
let all_sports = client.get_all_sports().await?;
```

### Events

Get events for a sport without odds. **Free** - does not count against quota.

```rust
let events = client
    .get_events("americanfootball_nfl")
    .commence_time_from(start_time)
    .commence_time_to(end_time)
    .send()
    .await?;
```

**Options:**
- `date_format(DateFormat)` - Response date format (ISO or Unix)
- `event_ids(ids)` - Filter by specific event IDs
- `commence_time_from(DateTime)` - Filter events starting after this time
- `commence_time_to(DateTime)` - Filter events starting before this time

### Odds

Get odds for a sport. **Quota cost:** markets x regions

```rust
let odds = client
    .get_odds("americanfootball_nfl")
    .regions(&[Region::Us, Region::Uk])
    .markets(&[Market::H2h, Market::Spreads, Market::Totals])
    .odds_format(OddsFormat::American)
    .bookmakers(["draftkings", "fanduel"])
    .include_links(true)
    .send()
    .await?;
```

**Options:**
- `regions(&[Region])` - **Required.** Bookmaker regions (Us, Us2, Uk, Au, Eu)
- `markets(&[Market])` - Market types (H2h, Spreads, Totals, Outrights)
- `odds_format(OddsFormat)` - Decimal or American
- `date_format(DateFormat)` - ISO or Unix
- `event_ids(ids)` - Filter by event IDs
- `bookmakers(keys)` - Filter by bookmaker keys
- `commence_time_from/to(DateTime)` - Time range filter
- `include_links(bool)` - Include deep links to bookmaker pages
- `include_sids(bool)` - Include site IDs
- `include_bet_limits(bool)` - Include betting limits

### Scores

Get live and completed scores. **Quota cost:** 1 (or 2 with `days_from`)

```rust
// Live/upcoming scores
let scores = client
    .get_scores("americanfootball_nfl")
    .send()
    .await?;

// Include completed games from past 3 days
let scores = client
    .get_scores("americanfootball_nfl")
    .days_from(3)
    .send()
    .await?;
```

**Options:**
- `days_from(u8)` - Include games from past 1-3 days (costs 2 instead of 1)
- `date_format(DateFormat)` - ISO or Unix
- `event_ids(ids)` - Filter by event IDs

### Event Odds

Get detailed odds for a single event, including player props. **Quota cost:** markets x regions

```rust
let event_odds = client
    .get_event_odds("americanfootball_nfl", "event_id_here")
    .regions(&[Region::Us])
    .markets(&[Market::H2h])
    .custom_market("player_pass_tds")  // Player props
    .include_multipliers(true)  // DFS multipliers
    .send()
    .await?;
```

**Options:**
- `regions(&[Region])` - **Required.** Bookmaker regions
- `markets(&[Market])` - Standard market types
- `custom_market(key)` - Add player prop or alternate markets by key
- `odds_format(OddsFormat)` - Decimal or American
- `bookmakers(keys)` - Filter by bookmaker keys
- `include_links(bool)` - Deep links
- `include_sids(bool)` - Site IDs
- `include_multipliers(bool)` - DFS multipliers

### Event Markets

Discover available markets for an event. **Quota cost:** 1

```rust
let markets = client
    .get_event_markets("americanfootball_nfl", "event_id_here")
    .regions(&[Region::Us])
    .send()
    .await?;

// Returns list of available market keys per bookmaker
for bookmaker in markets.data.bookmakers {
    println!("{}: {:?}", bookmaker.title, bookmaker.markets);
}
```

### Participants

Get all teams/players for a sport. **Quota cost:** 1

```rust
let participants = client
    .get_participants("americanfootball_nfl")
    .await?;
```

### Historical Odds

Get odds snapshot at a specific point in time. **Quota cost:** 10 x markets x regions

```rust
use chrono::{TimeZone, Utc};

let historical = client
    .get_historical_odds("americanfootball_nfl")
    .date(Utc.with_ymd_and_hms(2024, 1, 15, 12, 0, 0).unwrap())
    .regions(&[Region::Us])
    .markets(&[Market::H2h])
    .send()
    .await?;

// Navigate through time
println!("Snapshot: {}", historical.data.timestamp);
println!("Previous: {:?}", historical.data.previous_timestamp);
println!("Next: {:?}", historical.data.next_timestamp);
```

### Historical Events

Get events that existed at a specific point in time. **Quota cost:** 1

```rust
let historical_events = client
    .get_historical_events("americanfootball_nfl")
    .date(snapshot_time)
    .send()
    .await?;
```

### Historical Event Odds

Get historical odds for a specific event. **Quota cost:** markets x regions

```rust
let historical_event = client
    .get_historical_event_odds("americanfootball_nfl", "event_id")
    .date(snapshot_time)
    .regions(&[Region::Us])
    .markets(&[Market::Spreads])
    .send()
    .await?;
```

## Types

### Regions

```rust
pub enum Region {
    Us,   // United States
    Us2,  // United States (secondary)
    Uk,   // United Kingdom
    Au,   // Australia
    Eu,   // Europe
}
```

### Markets

```rust
pub enum Market {
    H2h,       // Head to head (moneyline)
    Spreads,   // Point spreads
    Totals,    // Over/under
    Outrights, // Futures
    H2hLay,    // Draw no bet
    Custom(String), // Player props, alternates
}
```

### Odds Format

```rust
pub enum OddsFormat {
    Decimal,  // 2.50
    American, // +150, -200
}
```

## Configuration

### Custom Client

```rust
use the_odds_api::TheOddsApiClient;

// Use IPv6 endpoint
let client = TheOddsApiClient::builder("your-api-key")
    .use_ipv6()
    .build();

// Custom base URL
let client = TheOddsApiClient::builder("your-api-key")
    .base_url("https://custom-endpoint.example.com")
    .build();

// Custom reqwest client
let http_client = reqwest::Client::builder()
    .timeout(std::time::Duration::from_secs(30))
    .build()?;

let client = TheOddsApiClient::builder("your-api-key")
    .client(http_client)
    .build();
```

## API Usage Tracking

Every response includes usage information from the API headers:

```rust
let response = client.get_odds("americanfootball_nfl")
    .regions(&[Region::Us])
    .send()
    .await?;

println!("Requests remaining: {:?}", response.usage.requests_remaining);
println!("Requests used: {:?}", response.usage.requests_used);
println!("Last request cost: {:?}", response.usage.requests_last);
```

## Error Handling

```rust
use the_odds_api::Error;

match client.get_odds("invalid_sport").regions(&[Region::Us]).send().await {
    Ok(odds) => { /* handle success */ }
    Err(Error::Unauthorized) => {
        println!("Invalid API key");
    }
    Err(Error::RateLimited { requests_remaining }) => {
        println!("Rate limited. Remaining: {:?}", requests_remaining);
    }
    Err(Error::Api { status, message }) => {
        println!("API error {}: {}", status, message);
    }
    Err(Error::MissingParameter(param)) => {
        println!("Missing required parameter: {}", param);
    }
    Err(e) => {
        println!("Other error: {}", e);
    }
}
```

## License

MIT