# 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