The Treasury FiscalData API: Pull the U.S. National Debt as JSON (No Key)

Last month I was building a dashboard that needed the actual interest rate the U.S. government pays on its debt. Not a headline number from a news site, not a scraped table — the real figure, updated monthly, that I could pull programmatically and trust. I went looking for an API and braced myself for the usual: sign up, get a key, hit a rate limit at 500 calls, upgrade to a paid tier.

Then I found the Treasury FiscalData API. No key. No signup. No rate limit worth mentioning. Just clean JSON straight from the U.S. Department of the Treasury, covering everything from the daily national debt to the average interest rate on every class of Treasury security. I’ve been using it for weeks now and it’s become my default source for macro data. Here’s how it works and why I trust it more than most paid feeds.

Why a government API beats the finance data vendors here

If you’ve ever priced out Bloomberg or even a mid-tier data vendor, you know macro data gets expensive fast. And the free tiers (Alpha Vantage, some of the FRED wrappers) either throttle you hard or wrap the numbers in their own formatting layer. The FiscalData API is the primary source — it’s the Treasury publishing its own books. When I pull the total public debt, I’m reading the same figure the Bureau of the Fiscal Service uses internally.

The base URL is https://api.fiscaldata.treasury.gov/services/api/fiscal_service, and every dataset hangs off that. No auth header. Here’s the debt outstanding as of last week, pulled live:

import urllib.request, json

BASE = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"

def get(endpoint, params):
    q = "&".join(f"{k}={v}" for k, v in params.items())
    req = urllib.request.Request(f"{BASE}{endpoint}?{q}",
                                 headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(req, timeout=25) as r:
        return json.load(r)

debt = get("/v2/accounting/od/debt_to_penny", {
    "fields": "record_date,tot_pub_debt_out_amt",
    "sort": "-record_date",
    "page[size]": "1",
})
row = debt["data"][0]
print(f"As of {row['record_date']}: "
      f"${float(row['tot_pub_debt_out_amt'])/1e12:.2f} trillion")

Run that and you get: As of 2026-07-06: $39.39 trillion. That’s the actual number, to the penny, in the tot_pub_debt_out_amt field — the endpoint is literally called debt to the penny. I ran this exact script before writing this paragraph.

The query language does the filtering for you

What sold me was that I don’t have to pull a whole dataset and filter client-side. The API takes fields, filter, sort, and pagination params, so I fetch exactly the rows I want. Say I need the average interest rate the Treasury pays, broken out by security type, for the most recent month:

rates = get("/v2/accounting/od/avg_interest_rates", {
    "fields": "record_date,security_desc,avg_interest_rate_amt",
    "filter": "record_date:eq:2026-06-30",
    "sort": "-avg_interest_rate_amt",
})
for r in rates["data"][:5]:
    print(f"{r['security_desc']:35} {r['avg_interest_rate_amt']}%")

Output, straight from the June 2026 books:

Domestic Series                     7.577%
United States Savings Inflation...  4.418%
Treasury Bills                      3.706%
Treasury Floating Rate Notes (FRN)  3.512%
Treasury Bonds                      3.430%

That T-Bill line — 3.706% — is the weighted average rate across every outstanding bill. If you’re modeling the government’s interest burden or trying to sanity-check where short rates actually sit, this is the ground truth. The filter syntax is field:operator:value, and the operators you’ll use most are eq, gt, gte, lt, lte, and in. Chain them with commas for an AND.

The datasets I actually reach for

There are over a hundred datasets, which is honestly the intimidating part. After a few weeks, these are the four I keep going back to:

  • Debt to the Penny (/v2/accounting/od/debt_to_penny) — daily total public debt, split into public vs intragovernmental holdings. Updated every business day.
  • Average Interest Rates (/v2/accounting/od/avg_interest_rates) — monthly, by security type. The one I used above.
  • Treasury Reporting Rates of Exchange (/v1/accounting/od/rates_of_exchange) — the official USD conversion rates for every foreign currency, quarterly. If you do any cross-border accounting, this is the rate auditors expect.
  • Monthly Treasury Statement — federal receipts and outlays, the government’s income statement. Good for tracking the deficit trend without waiting for a news writeup.

One gotcha that cost me twenty minutes: the pagination params use square brackets — page[size] and page[number] — and if you’re building the URL by hand in some HTTP clients you’ll need to URL-encode them as page%5Bsize%5D. In Python’s requests or with a params dict it’s handled for you, but raw curl on certain shells will choke on the brackets. Default page size is 100; max is 10,000.

A pattern I use: cache the daily pull

Because the debt figure only changes on business days, I don’t hammer the API on every dashboard load. I pull once, cache the JSON, and refresh on a cron. Here’s the shape of it:

import json, os, datetime

CACHE = "/tmp/treasury_debt.json"

def cached_debt():
    today = datetime.date.today().isoformat()
    if os.path.exists(CACHE):
        data = json.load(open(CACHE))
        if data.get("fetched") == today:
            return data["value"]
    row = get("/v2/accounting/od/debt_to_penny", {
        "fields": "tot_pub_debt_out_amt",
        "sort": "-record_date",
        "page[size]": "1",
    })["data"][0]
    value = float(row["tot_pub_debt_out_amt"])
    json.dump({"fetched": today, "value": value}, open(CACHE, "w"))
    return value

That’s it. One network call a day, and the API has been up every single time I’ve hit it. No 429s, no key rotation, no vendor emails about my “usage tier.”

Where it fits with the other free finance APIs

FiscalData isn’t a stock quote API — it won’t give you AAPL’s last price. It’s macro and government fiscal data. I pair it with a couple of other keyless sources depending on what I’m building. For company financials and filings, I use the SEC EDGAR XBRL API, which hands you any public company’s numbers as JSON with no key either. And when I need full-text search across filings, EDGAR’s efts.sec.gov endpoint covers that. Between Treasury FiscalData for macro and SEC EDGAR for corporate, you can build a genuinely capable market data layer without paying for a single API key.

If you’re going deeper on building signals from this kind of raw data, I put together a longer algorithmic trading engineering guide that walks through turning these feeds into something you can actually trade on.

If you want to run this on your own hardware

I run my data-pull crons on a small always-on box rather than paying for a cloud VM — a keyless API plus a $100 mini PC gets you a surprisingly capable homelab data node that never sleeps. If you’re setting one up, I’ve been happy with a Beelink mini PC for exactly this kind of lightweight always-on job, and a Samsung T7 external SSD to hold the cached datasets and historical pulls. Full disclosure: those are Amazon affiliate links, so I earn a small cut if you buy through them — I only recommend gear I actually run.

The whole point of an API like this is that the interesting work happens on your side — the modeling, the charting, the alerts. The data itself should be boring, reliable, and free. FiscalData is all three, and after a few weeks of leaning on it I’ve stopped reaching for the paid vendors for anything macro.

Start with the debt-to-the-penny endpoint, get one number printing, then explore the dataset catalog from there. The pattern is identical across all of them.


Join https://t.me/alphasignal822 for free market intelligence.

📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Also by us: StartCaaS — AI Company OS · Hype2You — AI Tech Trends