finance-query v3.2.0

US Treasury FiscalData#

info · Feature flag required

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

FiscalData is the US Treasury's own publishing platform for federal debt, interest rates, and the Daily Treasury Statement. It is keyless — no registration, no API key, no environment variable.

FRED mirrors part of this data with a lag and needs a key; FiscalData is the primary source.

Setup#

rust · no_run feature=fiscaldata
use finance_query::{Capability, Provider, Providers};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let providers = Providers::builder()
        .route(Capability::ECONOMIC, [Provider::FiscalData])
        .build()
        .await?;

    let debt = providers.economic("DEBT_TO_PENNY").series().await?;
    println!("{}", debt.title.unwrap_or_default());  // "Total Public Debt Outstanding"

    if let Some(latest) = debt.observations.last() {
        println!("{}: {:?}", latest.date, latest.value);
    }
    Ok(())
}

Curated Series#

FiscalData organises its data as ~50 datasets with dozens of columns each, so the adapter names the common single-value series directly:

Series idDatasetColumnUnitsFrequency
DEBT_TO_PENNYv2/accounting/od/debt_to_pennytot_pub_debt_out_amtUS DollarsDaily
DEBT_HELD_BY_PUBLICv2/accounting/od/debt_to_pennydebt_held_public_amtUS DollarsDaily
INTRAGOVERNMENTAL_HOLDINGSv2/accounting/od/debt_to_pennyintragov_hold_amtUS DollarsDaily
AVG_INTEREST_RATEv2/accounting/od/avg_interest_ratesavg_interest_rate_amtPercentMonthly
AVG_INTEREST_RATE_MARKETABLEv2/accounting/od/avg_interest_ratesavg_interest_rate_amtPercentMonthly
OPERATING_CASH_BALANCEv1/accounting/dts/operating_cash_balanceopen_today_balMillions of US DollarsDaily

Series ids are matched case-insensitively.

The two AVG_INTEREST_RATE* entries read the same column with different row filters — Total Interest-bearing Debt and Total Marketable respectively — because that dataset stacks one series per security type.

warning · Units differ per dataset

FiscalData's own metadata reports only CURRENCY / PERCENTAGE, which cannot distinguish dollars from millions of dollars. The curated table above states the real unit; Daily Treasury Statement figures (OPERATING_CASH_BALANCE) are in millions.

Any Other Dataset#

Anything outside the curated list is reachable with the passthrough form "<dataset path>:<value column>":

rust · no_run feature=fiscaldata
use finance_query::{Capability, Provider, Providers};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let providers = Providers::builder()
        .route(Capability::ECONOMIC, [Provider::FiscalData])
        .build()
        .await?;

    let series = providers
        .economic("v2/accounting/od/debt_to_penny:debt_held_public_amt")
        .series()
        .await?;
    Ok(())
}

Dataset paths and column names come from the FiscalData dataset catalogue. Passthrough series report units from the column's declared type and leave frequency as None — the adapter will not guess.

Passing anything that is neither a curated id nor a dataset:column pair returns FinanceError::InvalidParameter listing the curated catalogue.

Treasury Auctions#

Auction records and the forward schedule hang off the EconomicCatalog handle rather than a series id, since neither is a single value per date.

rust · no_run feature=fiscaldata
use finance_query::{Capability, Provider, Providers, TreasuryAuctionQuery};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let providers = Providers::builder()
        .route(Capability::ECONOMIC, [Provider::FiscalData])
        .build()
        .await?;

    let query = TreasuryAuctionQuery::new()
        .security_type("Note")
        .dates(Some("2025-01-01"), Some("2025-03-31"))
        .limit(50);

    for auction in providers.economic_catalog().treasury_auctions(&query).await? {
        println!(
            "{} {} {}: bid-to-cover {:?}, high yield {:?}",
            auction.auction_date,
            auction.security_term,
            auction.cusip,
            auction.bid_to_cover_ratio,
            auction.high_yield,
        );
    }
    Ok(())
}
rust · no_run feature=fiscaldata
use finance_query::{Capability, Provider, Providers};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let providers = Providers::builder()
        .route(Capability::ECONOMIC, [Provider::FiscalData])
        .build()
        .await?;

    for auction in providers.economic_catalog().upcoming_auctions().await? {
        println!(
            "{}: {} {} (announced {})",
            auction.auction_date,
            auction.security_term,
            auction.security_type,
            auction.announcement_date,
        );
    }
    Ok(())
}

Auctions come back newest first; the schedule comes back soonest first.

Every filter on TreasuryAuctionQuery is optional, and limit caps how many auctions are fetched from Treasury (default 100, maximum 1,000). security_type, security_term, from, and to are rejected with FinanceError::InvalidParameter if they carry a , or :, which FiscalData's filter grammar reads as clause separators; from and to must be YYYY-MM-DD.

warning · Result fields are empty until an auction settles

auctions_query carries a row from the moment Treasury announces an auction, so the most recent rows have terms (offering_amt, maturity_date) but no results yet. Bills price off high_discnt_rate and high_investment_rate and leave high_yield unset; notes and bonds do the reverse.

upcoming_auctions() returns only the newest published schedule. The underlying dataset appends each week's snapshot without retiring the last, so its older rows describe auctions that have already been held.

Response Shape#

Results come back as the provider-neutral EconomicSeries:

FieldValue from FiscalData
series_idThe string you passed in
titleThe column's human label from the response metadata
unitsFrom the curated table, or the column type for passthrough series
frequencyFrom the curated table; None for passthrough series
observationsChronological (oldest first), keyed by record_date

FiscalData encodes every column as a string, including numbers, and marks a missing figure with the literal string "null" — both are normalised, so value is a real Option<f64>.

Pagination#

Series are fetched at the API's maximum page size (10,000 rows) and pagination is followed automatically, capped at 5 pages. A dataset larger than that logs a warning rather than silently returning a truncated series.

Auction queries ask for a single page sized to limit, so limit bounds the request itself rather than trimming a larger response.

Rate Limits#

FiscalData publishes no documented quota. The client paces itself at 5 requests/second, and a 429 surfaces as FinanceError::RateLimited.

Next Steps#

built with cargo soothfast docs build source