| Crates.io | eeyf |
| lib.rs | eeyf |
| version | 0.1.0 |
| created_at | 2025-10-06 18:58:25.745388+00 |
| updated_at | 2025-10-06 18:58:25.745388+00 |
| description | Eric Evans' Yahoo Finance API - A rate-limited, reliable Rust adapter for Yahoo Finance API |
| homepage | |
| repository | https://github.com/ciresnave/eeyf |
| max_upload_size | |
| id | 1870646 |
| size | 1,034,930 |
A rate-limited, reliable Rust adapter for the Yahoo Finance API designed for enterprise use.
⚠️ IMPORTANT: This library implements proper rate limiting to respect Yahoo Finance's API limits and prevent your IP from being blocked. Unlike other libraries, EEYF includes built-in protection against API abuse.
yahoo_finance_api codeThis project started out as a fork of the yahoo! finance API crate. I would like to formally thank Mark Beinker and all of the many contributors to that crate as this crate wouldn't have been possible without their hard work as its base. On that note, if any of the maintainers of that crate would like to copy any/all code from this crate back to that one, feel free! I only split it so I could work without needing approval to push changes. I hope you approve of the ones I make.
I would also like to thank Yahoo! as without them we wouldn't have their wonderful Yahoo! Finance website that this crate retrieves data from.
This project is licensed under Apache 2.0 or MIT license (see files LICENSE-Apache2.0 and LICENSE-MIT).
EEYF provides a comprehensive FFI (Foreign Function Interface) layer for creating language bindings. Language bindings are maintained in separate repositories for better ecosystem integration:
See docs/FFI_GUIDE.md for complete instructions on creating bindings for any language. The guide includes:
Community members can create and maintain language bindings. High-quality bindings that follow the FFI guide will be listed here:
eeyf-python): Coming soon - PyPI package with ctypes/cffi bindingseeyf-node): Coming soon - npm package with TypeScript supporteeyf-go): Coming soon - Go modules with CGO bindingseeyf-ruby): Coming soon - RubyGems with FFI gemWant to create a binding? Follow the FFI Integration Guide and open an issue to have your repository listed here.
Language bindings are maintained separately to:
This follows the proven patterns used by major projects like SQLite, protobuf, and tree-sitter.
All core Yahoo Finance API functionality is 100% operational and production-ready:
Three advanced data processing modules are temporarily disabled in v0.1.0 pending refactoring:
timeseries - Time series utilities (resampling, timezone handling)transform - Data transformation (OHLC aggregation, technical indicators)validate - Data validation (integrity checks, anomaly detection)Status: These modules are under refactoring to work with both f64 and rust_decimal::Decimal types.
Workaround: To use these features now, enable the decimal feature:
[dependencies]
eeyf = { version = "0.1", features = ["decimal"] }
Timeline: These modules will be re-enabled in v0.1.1 or v0.2.0 after refactoring is complete.
See CHANGELOG.md for detailed information about changes between versions.
For production use, always enable rate limiting to prevent API violations:
use eeyf::YahooConnector;
use tokio_test;
fn main() {
// Create connector with default rate limiting (1800 requests/hour)
let provider = YahooConnector::with_rate_limiting().unwrap();
// Get latest quote - automatically rate limited
let response = tokio_test::block_on(
provider.get_latest_quotes("AAPL", "1d")
).unwrap();
let quote = response.last_quote().unwrap();
println!("Apple's latest price: ${}", quote.close);
}
use eeyf::{YahooConnector, RateLimitConfig};
use std::time::Duration;
fn main() {
let config = RateLimitConfig {
requests_per_hour: 1000, // Custom hourly limit
burst_limit: 5, // Allow 5 rapid requests
min_interval: Duration::from_millis(200), // 200ms between requests
};
let provider = YahooConnector::with_custom_rate_limiting(config).unwrap();
// Your requests are now automatically rate-limited
}
All request functions return async futures and are designed for modern async/await workflows. They need to be called from within an async function with .await or via functions like block_on. All examples use the tokio runtime and tests use the tokio-test crate.
use eeyf as yahoo;
use time::OffsetDateTime;
use tokio_test;
fn main() {
let provider = yahoo::YahooConnector::with_rate_limiting().unwrap();
// get the latest quotes in 1 minute intervals
let response = tokio_test::block_on(provider.get_latest_quotes("AAPL", "1d")).unwrap();
// extract the latest valid quote summary
// including timestamp,open,close,high,low,volume
let quote = response.last_quote().unwrap();
let time: OffsetDateTime =
OffsetDateTime::from_unix_timestamp(quote.timestamp).unwrap();
println!("At {} quote price of Apple was {}", time, quote.close);
}
use eeyf as yahoo;
use time::{macros::datetime, OffsetDateTime};
use tokio_test;
fn main() {
let provider = yahoo::YahooConnector::with_rate_limiting().unwrap();
let start = datetime!(2020-1-1 0:00:00.00 UTC);
let end = datetime!(2020-1-31 23:59:59.99 UTC);
// returns historic quotes with daily interval
let resp = tokio_test::block_on(provider.get_quote_history("AAPL", start, end)).unwrap();
let quotes = resp.quotes().unwrap();
println!("Apple's quotes in January: {:?}", quotes);
}
Another method to retrieve a range of quotes is by requesting the quotes for a given period and lookup frequency. Here is an example retrieving the daily quotes for the last month:
use eeyf as yahoo;
use tokio_test;
fn main() {
let provider = yahoo::YahooConnector::with_rate_limiting().unwrap();
let response = tokio_test::block_on(provider.get_quote_range("AAPL", "1d", "1mo")).unwrap();
let quotes = response.quotes().unwrap();
println!("Apple's quotes of the last month: {:?}", quotes);
}
Note: See the Time Period Labels and Valid Parameter Combinations sections for what can and can't be used in the above code
use eeyf as yahoo;
use tokio_test;
fn main() {
let provider = yahoo::YahooConnector::with_rate_limiting().unwrap();
let resp = tokio_test::block_on(provider.search_ticker("Apple")).unwrap();
let mut apple_found = false;
println!("All tickers found while searching for 'Apple':");
for item in resp.quotes
{
println!("{}", item.symbol)
}
}
Some fields like longname are optional and will be replaced by default values if missing (e.g. empty string). If you do not like this behavior, use search_ticker_opt instead which contains Option<String> fields, returning None if the field found missing in the response.
if let Some(status) = provider.rate_limit_status() {
println!("Used: {}/{} requests this hour", status.hourly_used, status.hourly_limit);
println!("Remaining: {} requests", status.hourly_remaining());
println!("Usage: {:.1}%", status.hourly_percent_used());
if status.is_near_limit() {
println!("⚠️ Warning: Approaching rate limit!");
}
}
Yahoo Finance has API limits that unprotected libraries don't respect:
Without rate limiting, you risk:
EEYF uses conservative defaults (90% of Yahoo's limits):
These settings are safe for production use while maximizing throughput.
EEYF is designed as a drop-in replacement. Simply change your dependency:
# Before
# yahoo_finance_api = "4.1.0"
# After
eeyf = "0.1.0"
Then update your imports and add rate limiting:
// Before (UNSAFE - no rate limiting)
use yahoo_finance_api as yahoo;
let provider = yahoo::YahooConnector::new().unwrap();
// After (SAFE - with protection)
use eeyf as yahoo; // Drop-in replacement
let provider = yahoo::YahooConnector::with_rate_limiting().unwrap();
let symbols = vec!["AAPL", "GOOGL", "MSFT", "TSLA"];
for symbol in symbols {
// Each request automatically waits for rate limit clearance
let response = provider.get_latest_quotes(symbol, "1d").await?;
println!("{}: ${}", symbol, response.last_quote()?.close);
}
EEYF provides detailed error information:
match provider.get_latest_quotes("INVALID", "1d").await {
Err(eeyf::YahooError::TooManyRequests(details)) => {
println!("Rate limited: {}", details);
// Maybe implement exponential backoff
},
Err(eeyf::YahooError::ConnectionFailed(err)) => {
println!("Network error: {}", err);
// Maybe retry with backoff
},
Err(other) => {
println!("Other error: {}", other);
},
Ok(response) => {
// Success
}
}
rate_limit_status()Time periods are given as strings, combined from the number of periods (except for "ytd" and "max") and a string label specifying a single period. The following period labels are supported:
| label | description |
|---|---|
| m | minute |
| h | hour |
| d | day |
| wk | week |
| mo | month |
| y | year |
| ytd | year-to-date |
| max | maximum |
| range | interval |
|---|---|
| 1d | 1m, 2m, 5m, 15m, 30m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo |
| 1mo | 2m, 3m, 5m, 15m, 30m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo |
| 3mo | 1h, 1d, 1wk, 1mo, 3mo |
| 6mo | 1h, 1d, 1wk, 1mo, 3mo |
| 1y | 1h, 1d, 1wk, 1mo, 3mo |
| 2y | 1h, 1d, 1wk, 1mo, 3mo |
| 5y | 1d, 1wk, 1mo, 3mo |
| 10y | 1d, 1wk, 1mo, 3mo |
| ytd | 1m, 2m, 5m, 15m, 30m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo |
| max | 1m, 2m, 5m, 15m, 30m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo |