A high-performance asynchronous Rust client library for the OpenFIGI API, providing type-safe access to financial instrument identification and mapping services.
OpenFIGI is Bloomberg's open symbology initiative that provides standardized identification for financial instruments across asset classes and markets worldwide.
- Features
- Getting Started
- Configuration
- API Usage Examples
- Error Handling
- Documentation
- Contributing
- License
- Acknowledgments
- Support
This library is designed with a focus on ergonomics, correctness, and production readiness.
- βοΈ Ergonomic: Provide a simple, intuitive, and fluent builder API.
- π Type-safe API: Strongly-typed request and response models prevent invalid data.
- β‘ Fully Asynchronous: Built on
tokioandreqwestfor high-concurrency applications. - π§ Extensible via Middleware: Integrates with
reqwest-middlewarefor custom logic like retries, logging, and tracing. - π Ergonomic Error Handling: Provides distinct error types for network issues, API errors, and invalid requests.
- π Environment integration: Automatically detects API keys from environment variables.
- π Built-in Rate Limit Handling: The client is aware of API rate limits and provides informative errors when they are exceeded.
- π Batch operations: First-class support for bulk operations to minimize network round-trips (up to 100 with API key, 5 without).
First, add the crate to your project's dependencies:
cargo add openfigi-rsuse openfigi_rs::client::OpenFIGIClient;
use openfigi_rs::model::enums::IdType;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Create a client. It will use the OPENFIGI_API_KEY env var if available.
let client = OpenFIGIClient::new();
// Map an ISIN to its corresponding FIGI
let mapping_results = client
.mapping(IdType::ID_ISIN, "US4592001014") // IBM
.send()
.await?;
// The result is a vector of responses. Let's inspect the first one.
let data = mapping_results.data();
println!("FIGI: {}", data[0].figi);
println!("Name: {}", data[0].display_name());
// You can also pretty-print the full debug output
// println!("{:#?}", mapping_results);
Ok(())
}The client can be configured with an API key to access higher rate limits.
The client automatically detects the OPENFIGI_API_KEY environment variable.
export OPENFIGI_API_KEY="your-secret-key"You can also provide the key explicitly using the builder pattern.
# use openfigi_rs::client::OpenFIGIClient;
# #[tokio::main]
# async fn main() -> anyhow::Result<()> {
#
let client = OpenFIGIClient::builder()
.api_key("your-secret-key")
.build()?;
# Ok(())
# }For production environments, you'll want to configure timeouts and retry policies. This library is built on reqwest and reqwest-middleware, making customization easy.
use openfigi_rs::client::OpenFIGIClient;
use reqwest_middleware::ClientBuilder;
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. Create a base reqwest client with timeouts
let http_client = reqwest::Client::builder()
.timeout(Duration::from_secs(15))
.connect_timeout(Duration::from_secs(5))
.build()?;
// 2. Configure a retry policy
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
// 3. Build the middleware client
let middleware_client = ClientBuilder::new(http_client)
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
.build();
// 4. Build the OpenFIGI client with the custom middleware client
let client = OpenFIGIClient::builder()
.middleware_client(middleware_client)
.api_key("your-secret-key")
.build()?;
Ok(())
}| Limitation | Without API Key | With API Key |
|---|---|---|
| Request Rate | 25 per minute | 250 per minute (25 per 6s) |
| Jobs per Request | 10 jobs | 100 jobs |
The client supports all three OpenFIGI API v3 endpoints.
| Endpoint | Purpose | Batch Support |
|---|---|---|
| /mapping | Map third-party identifiers to FIGIs. | β |
| /search | Perform a text-based search for instruments. | β |
| /filter | Search for instruments using specific criteria. | β |
Convert third-party identifiers to FIGIs:
use openfigi_rs::client::OpenFIGIClient;
use openfigi_rs::model::enums::{IdType, Currency, ExchCode};
use openfigi_rs::model::request::MappingRequest;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = OpenFIGIClient::new();
// Single mapping request with optional parameters
let single_result = client
.mapping(IdType::ID_ISIN, "US4592001014")
.currency(Currency::USD)
.exch_code(ExchCode::US)
.send()
.await?;
// Bulk mapping request for multiple identifiers using a prebuilt vector of requests
let requests = vec![
MappingRequest::new(IdType::ID_ISIN, "US4592001014"),
MappingRequest::new(IdType::TICKER, "AAPL"),
];
let bulk_results = client
.bulk_mapping()
.add_requests(requests)
.send()
.await?;
// Bulk mapping request with inline closures
let result = client
.bulk_mapping()
.add_request_with(|j| {
// Simple mapping request
j.id_type(IdType::ID_ISIN)
.id_value("US4592001014")
})?
.add_request_with(|j| {
// Complex mapping request with filters
j.id_type(IdType::TICKER)
.id_value("IBM")
.currency(Currency::USD)
.exch_code(ExchCode::US)
})?
.send()
.await?;
Ok(())
}Text-based instrument search:
use openfigi_rs::client::OpenFIGIClient;
use openfigi_rs::model::enums::Currency;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = OpenFIGIClient::new();
let results = client
.search("apple")
.currency(Currency::USD)
.send()
.await?;
Ok(())
}Filter instruments by criteria:
use openfigi_rs::client::OpenFIGIClient;
use openfigi_rs::model::enums::SecurityType;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = OpenFIGIClient::new();
let results = client
.filter()
.query("technology")
.security_type(SecurityType::CommonStock)
.send()
.await?;
Ok(())
}The library provides a comprehensive OpenFIGIError enum. A common task is handling responses in a bulk request where some jobs may succeed and others may fail.
The API returns a 200 OK with a body containing either a data array or an error message for each job.
use openfigi_rs::client::OpenFIGIClient;
use openfigi_rs::error::OpenFIGIError;
use openfigi_rs::model::{enums::IdType, request::MappingRequest};
async fn handle_mapping() -> anyhow::Result<()> {
let client = OpenFIGIClient::new();
let requests = vec![
MappingRequest::new(IdType::ID_ISIN, "US4592001014"), // Valid
MappingRequest::new(IdType::ID_ISIN, "INVALID_ISIN"), // Invalid
];
match client.bulk_mapping().add_requests(requests).send().await {
Ok(mapping_results) => {
// Handle successful results
for (_index, data) in mapping_results.successes() {
println!("SUCCESS: Found {} instruments.", data.data().len());
}
// Handle failed results
for (_index, error) in mapping_results.failures() {
println!("API ERROR: {}", error);
}
}
// Handle network errors, timeouts, etc.
Err(e) => {
eprintln!("An unexpected network error occurred: {}", e);
}
}
Ok(())
}- API Documentation - Complete technical documentation for this crate.
- OpenFIGI API Documentation - The upstream API documentation from OpenFIGI.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines on how to submit patches, report issues, and suggest features.
This project is licensed under the MIT License. See the LICENSE file for details.
- OpenFIGI for providing the public API.
- Bloomberg for the OpenFIGI initiative.
- OMG for providing documentation.
- The Rust community for creating amazing libraries like
reqwest,reqwest-middleware,serde, andtokio.
For help with this library, please use the following resources:
- π Documentation: Check the API reference for detailed information.
- π Issues: For bugs and feature requests, please use the GitHub Issue Tracker.
- π¬ Discussions: For questions and general discussion, please use the GitHub Discussions.
Disclaimer: This library is an independent project and is not officially affiliated with, endorsed by, or sponsored by Bloomberg L.P. or the OpenFIGI project.