Crate openfigi_rs

Source
Expand description

Β§OpenFIGI Rust Client

Crates.io Documentation License: MIT Build Status Downloads

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.

Β§πŸ“– Table of Contents

§✨ Features

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 tokio and reqwest for high-concurrency applications.
  • πŸ”§ Extensible via Middleware: Integrates with reqwest-middleware for 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).

Β§πŸš€ Getting Started

First, add the crate to your project’s dependencies:

cargo add openfigi-rs

Β§Basic Usage

use 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(())
}

Β§πŸ”§ Configuration

Β§API Key

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"
Β§2. Manual Configuration

You can also provide the key explicitly using the builder pattern.

let client = OpenFIGIClient::builder()
    .api_key("your-secret-key")
    .build()?;

Β§Custom HTTP Client & Middleware

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(())
}

Β§Rate Limits

LimitationWithout API KeyWith API Key
Request Rate25 per minute250 per minute (25 per 6s)
Jobs per Request10 jobs100 jobs

Β§πŸ“š API Usage Examples

The client supports all three OpenFIGI API v3 endpoints.

EndpointPurposeBatch Support
/mappingMap third-party identifiers to FIGIs.βœ“
/searchPerform a text-based search for instruments.βœ—
/filterSearch for instruments using specific criteria.βœ—

Β§Mapping Endpoint

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(())
}

Β§Search Endpoint

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 Endpoint

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(())
}

§🚨 Error Handling

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(())
}

Β§πŸ“– Documentation

§🀝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines on how to submit patches, report issues, and suggest features.

Β§πŸ“„ License

This project is licensed under the MIT License. See the LICENSE file for details.

Β§πŸ™ Acknowledgments

  • 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, and tokio.

Β§πŸ“ž Support

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.

ModulesΒ§

client
HTTP client for OpenFIGI API operations HTTP client for interacting with the OpenFIGI API.
client_builder
Client builder with fluent configuration API for custom HTTP settings Builder pattern for configuring OpenFIGI API clients.
endpoint
API endpoint implementations for mapping, search, and filter operations
error
Comprehensive error types with OpenFIGI-specific context and inspection methods Error handling types for OpenFIGI API operations.
model
Strongly typed request and response data models for all API operations Data models for OpenFIGI API requests and responses.

MacrosΒ§

impl_filter_builder
Macro to implement standard filter builder methods for OpenFIGI API request builders.

ConstantsΒ§

DEFAULT_ENDPOINT_FILTER
The default endpoint path for filter requests.
DEFAULT_ENDPOINT_MAPPING
The default endpoint path for mapping requests.
DEFAULT_ENDPOINT_SEARCH
The default endpoint path for search requests.
VERSION
Library version

StaticsΒ§

DEFAULT_BASE_URL
The default base URL for the OpenFIGI API v3.