finance-query v3.0.0

OpenFIGI (identifier mapping)#

info · Feature flag required

toml
finance-query = { version = "...", features = ["openfigi"] }

OpenFIGI resolves a CUSIP, ISIN, SEDOL, or FIGI to the instruments carrying it — the missing step for any dataset that identifies holdings by CUSIP rather than ticker, 13F filings being the obvious case. No API key is required.

Where It Lives#

openfigi is a crate-level module, not a routed provider:

rust · no_run feature=openfigi
use finance_query::openfigi;

fn main() {}

Identifier resolution is not tied to a symbol handle and maps onto no Capability, so it sits alongside edgar and fred at the crate root rather than behind Providers::builder().

Resolving One Identifier#

rust · no_run feature=openfigi
use finance_query::openfigi;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    for listing in openfigi::resolve_cusip("037833100").await? {
        println!(
            "{:?} on {:?} ({:?})",
            listing.ticker, listing.exchange_code, listing.security_type
        );
    }
    Ok(())
}

resolve_isin and resolve_sedol are the same shape, and resolve(SecurityIdKind::…, id) takes the kind explicitly — including SecurityIdKind::Figi to find a FIGI's siblings and SecurityIdKind::Ticker to go the other way.

note · One identifier, many instruments

A CUSIP or ISIN identifies a security, which trades on many venues, so resolution returns a list. Entries sharing a composite_figi are the same security on different venues; share_class_figi groups share classes across countries. Filter on exchange_code == "US" for the country composite.

An identifier that is well-formed but matches nothing returns an empty list. Only a malformed identifier is an error.

Resolving Many#

OpenFIGI accepts 10 identifiers per request without a key. resolve_many batches automatically:

rust · no_run feature=openfigi
use finance_query::openfigi::{self, SecurityIdKind};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let cusips = ["037833100", "594918104", "02079K305"];
    let results = openfigi::resolve_many(SecurityIdKind::Cusip, &cusips).await?;

    for (cusip, listings) in cusips.iter().zip(&results) {
        match listings.iter().find(|l| l.exchange_code.as_deref() == Some("US")) {
            Some(l) => println!("{cusip} -> {:?}", l.ticker),
            None => println!("{cusip} -> no US listing"),
        }
    }
    Ok(())
}

The result is positional: element i answers ids[i], with an empty list where nothing matched. The adapter validates that OpenFIGI returned exactly as many results as jobs sent, and errors rather than risk pairing answers to the wrong identifiers.

SecurityMapping Fields#

FieldDescription
figiThis instrument's Financial Instrument Global Identifier
tickerTicker symbol as the venue lists it
nameSecurity name, usually the issuer
exchange_code"US" for the country composite, otherwise a venue code
composite_figiFIGI of the country-level composite this rolls up to
share_class_figiFIGI shared by every listing of this share class worldwide
security_typee.g. "Common Stock", "ETP"
market_sectore.g. "Equity", "Corp", "Govt"

Rate Limits#

Keyless: 25 requests per minute, 10 identifiers per request. The client paces itself to stay inside that.

A free key from openfigi.com/api raises both limits substantially. Export it and it is picked up automatically, per request, with no other change:

bash
export OPENFIGI_API_KEY="your-key"

Next Steps#

built with cargo soothfast docs build source