API documentation
Use the X-API-Key header as the canonical and preferred credential transport. Clients that cannot set custom headers can use the api_key query parameter as a fallback. Base URL is https://api.bullionapi.dev. Usage increments after API-key and quota checks but before endpoint validation.
Query credentials may appear in browser history, copied URLs, referrers, intermediary infrastructure, and logs. Use the X-API-Key header whenever your client supports custom headers. Query authentication does not enable cross-origin browser fetch; CORS remains separate. If both transports are supplied, they must match; differing values return 401 conflicting_credentials.
Quick start
Get live prices in 30 seconds. If you don’t have an API key yet, get one here.
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/price?currency=USD"Metal prices
Returns the spot price of one metal in the requested base currency as a single <metal>_price series: the latest cached price, or daily history when you pass start and end. Each metal has its own route: /v1/gold/price, /v1/silver/price, /v1/palladium/price, /v1/copper/price, and /v1/platinum/price. This is an authenticated database reader and never calls an upstream provider.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | ISO 4217 currency code. Defaults to USD. See supported currencies below. |
unit | string | Optional output mass unit: gram/g, kilogram/kg, or troy_oz/toz. Defaults to troy_oz; invalid values return 400. |
start | YYYY-MM-DD | Optional first inclusive UTC day. Omit start and end for the latest price only; start alone runs to today. |
end | YYYY-MM-DD | Optional last inclusive UTC day. Requires start; the range may span at most five years. |
api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. |
Spreadsheet and no-code example
For a client that cannot set custom headers, request the URL below. Treat the URL as a secret and prefer the header transport whenever your client supports it.
GET https://api.bullionapi.dev/v1/gold/price?currency=USD&api_key=bullion_your_keyResponse
{
"status": "success",
"metric": "price",
"metal": "gold",
"currency": "USD",
"unit": "troy_oz",
"start": null,
"end": null,
"series": [
{
"id": "gold_price",
"label": "Gold spot price",
"unit": "troy_oz",
"frequency": "hourly",
"method": "latest",
"observations": [{ "date": "2026-08-24T22:00:00.000Z", "value": "4527.86" }]
}
]
}Cached for 1 hour and refreshed by the scheduled data pipeline. Without a range the series holds one observation (frequency: hourly, method: latest) whose date is the full UTC observation timestamp. Every value is a decimal string in the requested unit, rounded to eight decimal places; request unit=gram or unit=kilogram for converted values. Unsupported currencies return 400 invalid_currency, unsupported units 400 invalid_unit, and a missing cached price 404 no_data.
For a configured projected currency the observation time is the hourly canonical USD metal observation used in the calculation, and the series adds a conversion object with the separate daily Bullion API conversion snapshot. It is omitted for USD, EUR, and GBP. The conversion time is not a provider end-of-day observation. stale is true when a snapshot is more than seven UTC calendar days older than the price it converts. Once the latest snapshot is more than seven UTC calendar days old, the latest price is withheld and the request returns 404 no_data until a new snapshot is accepted. Internal UniRateAPI observations are used only to derive informational metal prices and are never returned by this endpoint. Conversion input attribution: Exchange Rates By UniRateAPI.
"conversion": {
"snapshot_at": "2026-08-24T23:10:00.000Z",
"stale": false,
"attribution": "Exchange Rates By UniRateAPI",
"attribution_url": "https://unirateapi.com/"
}The legacy GET /v1/latest and GET /v1/metals/{metal} routes were removed on 26 September 2026. Like any unknown /v1 path, they now return 404 not_found with the JSON error envelope. Request each metal from its /v1/{metal}/price route instead.
Historical prices
Pass start and end to any /v1/{metal}/price route for one final close per UTC weekday, up to five years per request.
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/price?currency=USD&start=2025-07-08&end=2026-07-08"Response
{
"status": "success",
"metric": "price",
"metal": "gold",
"currency": "USD",
"unit": "troy_oz",
"start": "2025-07-08",
"end": "2026-07-08",
"series": [
{
"id": "gold_price",
"label": "Gold spot price",
"unit": "troy_oz",
"frequency": "daily",
"method": "weekday_close",
"observations": [
{ "date": "2025-07-08", "value": "3320.1" },
{ "date": "2025-07-09", "value": "3342.55" }
]
}
]
}Each observation is the final cached price of a UTC weekday, dated YYYY-MM-DD (frequency: daily, method: weekday_close). Saturday and Sunday UTC observations, including the Sunday-evening market reopen, are excluded. Only cached days are included; non-gold history contains hourly refresh observations and is not backfilled from the gold-owned historical provider. A range with no weekday close returns 404 no_data. end without start returns 400 missing_params; malformed dates return 400 invalid_date; reversed ranges or ranges over five years return 400 invalid_range. Scheduled jobs populate the cache; API requests never call upstream providers.
Metal price change
Returns one metal's absolute and percentage change between the final cached observation of two UTC days. Each metal has its own route, for example /v1/gold/change or /v1/silver/change. The endpoint reads the database cache and never calls an upstream provider.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | ISO 4217 base currency. Defaults to USD. |
unit | string | Optional output mass unit: gram/g, kilogram/kg, or troy_oz/toz. Defaults to troy_oz. |
start | YYYY-MM-DD | First UTC day to compare. end defaults to today and may be at most 365 days later. |
end | YYYY-MM-DD | Last UTC day to compare. Requires start. |
period | string | Rolling UTC window ending today: 1d (1 day), 1w (7 days), 1m (30 days), or 1y (365 days). Use instead of start and end. |
api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/silver/change?currency=USD&period=1w"Response
{
"status": "success",
"metric": "change",
"metal": "silver",
"currency": "USD",
"unit": "troy_oz",
"start": { "date": "2026-08-03", "value": "45" },
"end": { "date": "2026-08-10", "value": "46" },
"change": "1",
"change_percent": "2.2222"
}The final observation in each UTC day is selected deterministically, weekends included. All values are decimal strings: change is in the requested unit and change_percent is relative to the start price, rounded to four decimal places. Gram and kilogram start and end values are rounded to eight decimal places before change is taken. Use either start/end or one period; mixing them returns conflicting_params, and supplying neither returns missing_params. Invalid dates return invalid_date; reversed or overlong ranges return invalid_range; unsupported periods return invalid_period. Missing cached data returns no_change_data; a zero start price returns invalid_change_baseline.
Gold karat prices per gram
Returns cached gold prices per gram for common karats. Gold is the only supported metal and grams are the only response unit.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | Three-letter ISO 4217 code, case-insensitive. Defaults to USD; the currency must have cached gold data. |
date | YYYY-MM-DD | Optional exact UTC day. The final cached gold observation in that day is used. |
api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/carat?currency=USD"Response
{
"status": "success",
"currency": "USD",
"unit": "gram",
"as_of": "2026-08-10T14:30:00.000Z",
"karats": {
"24k": "145.57407936",
"23k": "139.50849272",
"22k": "133.44290608",
"21.6k": "131.01667142",
"21k": "127.37731944",
"18k": "109.18055952",
"16k": "97.04938624",
"14k": "84.91821296",
"12k": "72.78703968",
"10k": "60.65586640",
"9k": "54.59027976",
"8k": "48.52469312",
"6k": "36.39351984"
}
}Without date, the current cached gold row is used. With date, the final observation in that UTC day is used. Each value is a decimal string, calculated from the exact karat ratio and rounded half-up to eight decimal places. Malformed currency or date values return invalid_currency or invalid_date; a valid but unseeded currency or day returns no_data. Cache read failures return data_unavailable. Requests never bridge currencies or call upstream providers.
Gold-to-silver ratio
Returns the gold-to-silver ratio from the local price cache. Omit start and end for the latest stored ratio, or supply both for final observations by UTC day. Missing days are omitted; the endpoint never calls an upstream provider.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | ISO 4217 base currency. Defaults to USD. |
start | YYYY-MM-DD | Optional first inclusive UTC day. Use with end. |
end | YYYY-MM-DD | Optional last inclusive UTC day. Maximum range is five years. |
api_key | string | Fallback credential; prefer the X-API-Key header. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold-silver-ratio?currency=USD"Response
{
"status": "success",
"currency": "USD",
"start_date": null,
"end_date": null,
"points": [{ "date": "2026-09-24", "value": "68.40312345" }]
}Values are decimal strings rounded half-up to eight places; they were JSON numbers before 2026-10-07. The calculation reads only the database and is cached in memory for five minutes per currency and range. A latest request without both stored metal rows, or whose gold and silver observations are more than six hours apart, returns no_data; historical requests return an empty points array when no overlapping observations exist.
Gold moving average
Returns the simple moving average of final daily gold closes. Each value uses up to window valid stored closes including the current close; values before enough closes exist are partial warm-up averages.
Query parameters
| Name | Type | Description |
|---|---|---|
window | integer | Required. 50 or 200 stored daily closes; anything else returns invalid_window. |
currency | string | ISO 4217 base currency. Defaults to USD. |
start | YYYY-MM-DD | Required first inclusive UTC day. |
end | YYYY-MM-DD | Required last inclusive UTC day. Maximum range is five years. |
api_key | string | Fallback credential; prefer the X-API-Key header. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/moving-average?window=50¤cy=USD&start=2026-01-01&end=2026-12-31"Response
{
"status": "success",
"currency": "USD",
"window": 50,
"unit": "toz",
"start_date": "2026-01-01",
"end_date": "2026-12-31",
"points": [{ "date": "2026-01-01", "value": "5100.00000000" }]
}Uses the final stored gold observation for each UTC weekday; weekend rows are excluded and missing days are omitted. Values are decimal strings rounded half-up to eight places. The derived series is cached in memory for five minutes per currency and range, and the endpoint never calls an upstream provider. Omitting either date returns missing_params.
Gold volatility
Returns annualised sample volatility of 30 daily gold returns as a fraction, so "0.15000000" is 15%. Days before 30 returns exist are omitted, so a short range can return an empty points array.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | ISO 4217 base currency. Defaults to USD. |
start | YYYY-MM-DD | Required first inclusive UTC day. |
end | YYYY-MM-DD | Required last inclusive UTC day. Maximum range is five years. |
api_key | string | Fallback credential; prefer the X-API-Key header. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/volatility?currency=USD&start=2026-01-01&end=2026-12-31"Response
{
"status": "success",
"currency": "USD",
"window": 30,
"start_date": "2026-01-01",
"end_date": "2026-12-31",
"points": [{ "date": "2026-01-01", "value": "0.15000000" }]
}Uses the final stored gold observation for each UTC weekday; weekend rows are excluded and missing days are omitted. Values are decimal strings rounded half-up to eight places; they were JSON numbers before 2026-10-07. The calculation is cached in memory for five minutes per currency and range, and the endpoint never calls an upstream provider. Omitting either date returns missing_params.
Gold drawdown
Returns how far each trading-day gold close sits below its running peak over up to 1,260 trading-day closes (about five years), as a fraction: "-0.25000000" is 25% below the peak. Values before enough observations exist are partial warm-up drawdowns.
Query parameters
| Name | Type | Description |
|---|---|---|
currency | string | ISO 4217 base currency. Defaults to USD. |
start | YYYY-MM-DD | Required first inclusive UTC day. |
end | YYYY-MM-DD | Required last inclusive UTC day. Maximum range is five years. |
api_key | string | Fallback credential; prefer the X-API-Key header. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/gold/drawdown?currency=USD&start=2026-01-01&end=2026-12-31"Response
{
"status": "success",
"currency": "USD",
"lookback_observations": 1260,
"start_date": "2026-01-01",
"end_date": "2026-12-31",
"points": [{ "date": "2026-01-01", "value": "-0.05000000" }]
}Uses the final stored gold observation for each UTC weekday; weekend rows are excluded and missing days are omitted. Values are decimal strings rounded half-up to eight places; they were JSON numbers before 2026-10-07. The calculation is cached in memory for five minutes per currency and range, and the endpoint never calls an upstream provider. Omitting either date returns missing_params.
Policy rates, inflation and bond yields
One endpoint per metric with the country in the path. US, GB (alias uk) and EA (alias eu) are covered; an unknown code returns 400 invalid_country and any other valid country code returns 404 no_data. Without a date range each series returns its latest observation. The bare /v1/policy-rates, /v1/inflation and /v1/bond-yields return the latest value for every covered country, in the order US, GB, EA, as one quota-counted request; they ignore start and end.
Path and query parameters
| Name | Type | Description |
|---|---|---|
country | path | ISO 3166-1 alpha-2 code or EA, case-insensitive; uk and eu are aliases. |
start | date | First inclusive date (YYYY-MM-DD). Alone, runs to today. |
end | date | Last inclusive date (YYYY-MM-DD). Requires start. |
tenor | string | Bond yields only: 2y, 5y, 10y or 30y. |
basis | string | Bond yields: nominal or real. Inflation: year_over_year, breakeven, implied or expectation. |
api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/bond-yields/us?tenor=10y&start=2026-05-01&end=2026-06-30"curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/policy-rates"Response
{
"status": "success",
"metric": "bond_yields",
"country": "US",
"country_name": "United States",
"start": "2026-05-01",
"end": "2026-06-30",
"series": [
{
"id": "treasury_nominal_yield_10y",
"label": "10-year Treasury yield (nominal)",
"basis": "nominal",
"tenor": "10y",
"unit": "percent",
"frequency": "monthly",
"method": "monthly_average",
"source": {
"id": "us_treasury",
"series_id": "daily_treasury_yield_curve.BC_10YEAR",
"attribution": "U.S. Department of the Treasury"
},
"observations": [
{ "date": "2026-05-31", "value": "4.41" },
{ "date": "2026-06-30", "value": "4.25" }
]
}
]
}Every series has its id, label, unit, frequency, method, source attribution with the upstream series id, and observations with decimal-string values; bond yields and inflation add their basis and, where relevant, tenor. Unknown filter values return invalid_series_filter. Policy rates are daily; yields and inflation are monthly. UK gilt curves include nominal and real zero-coupon spot yields plus RPI-linked implied inflation at 2Y, 5Y, 10Y, and 30Y tenors, using official month-end archive dates with the month_end method. The API reads the database cache; protected cron collection is the only upstream caller. Every plan receives the same data contract and differs only in monthly quota and usage rights.
Methodology: Treasury nominal and real yields cover nominal 2Y, 5Y, 10Y, and 30Y plus real 5Y, 10Y, and 30Y series; each is an arithmetic average of valid daily observations in complete UTC calendar months. Treasury 5Y, 10Y, and 30Y breakevens are daily nominal-minus-real differences calculated only on common dates, then averaged by month. Empty, missing, and N/A Treasury values are absent rather than treated as zero. The BLS CPI-U NSA series CUUR0000SA0 is exposed as year-over-year percent; when BLS does not publish an annual-percent field, Bullion API calculates the twelve-month change from the same month in the prior year. UK Bank Rate uses the latest valid BoE IUDBEDR observation for each date in the refresh range; UK CPI uses the latest numeric monthly ONS D7G7 annual-rate value, stored at month end. UK curve values are direct BoE spot observations and BoE-derived RPI-linked implied inflation values; source dates are preserved, including final business days. Neither UK curve values nor UK Bank Rate/CPI are averaged.
Euro-area data includes ECB deposit facility, main refinancing, and marginal lending rates; AAA nominal 2Y, 5Y, 10Y, and 30Y spot yields; metadata-verified all-items HICP year-over-year inflation as ecb_hicp_yoy; and Consumer Expectations Survey weighted-median 12-month and five-year inflation expectations. CES values use survey_expectationand are not market-implied. ECB AAA yields are arithmetic averages of valid observations from complete UTC calendar months. HICP uses HICP.M.U2.N.000000.4D0.ANR and the provider validates its geography, adjustment, all-items, unit, and annual-rate metadata before accepting rows.
Sources: Federal Reserve Bank of New York reference rates, U.S. Department of the Treasury Daily Interest Rate XML Feed, Bank of England Database and BoE yield curve archives, plus Office for National Statistics D7G7 CPI series, and U.S. Bureau of Labor Statistics Public Data API, and the European Central Bank Data Portal. BLS disclaimer: BLS.gov cannot vouch for the data or analyses derived from these data after retrieval.
Money supply and central-bank balance sheets
Read cached, registered monetary series by country or from the collection route. Add inclusive start and end dates to a country route; omit both for the latest observation in each series. Collections return each covered country’s latest observations. Country coverage is registry-backed; a valid country without registered observations returns 404 no_data.
Path and query parameters
| Name | Type | Description |
|---|---|---|
country | path | ISO 3166-1 alpha-2 code. The response echoes the canonical upper-case code. |
start | date | First inclusive date (YYYY-MM-DD). Omit start and end for the latest observation of each series. |
end | date | Last inclusive date (YYYY-MM-DD). Requires start. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/money-supply/ca?start=2026-06-01&end=2026-07-31"Response
Abbreviated example. Each returned series also includes its full registered monetary metadata.
The Canada example is a static, not live, response using the Bank of Canada’s published July 2026 V41552786 value of 2,853,114 million dollars, normalized exactly to 2853114000000 CAD. See the Bank of Canada Valet observation.
{
"status": "success",
"metric": "money_supply",
"country": "CA",
"country_name": "Canada",
"start": "2026-07-01",
"end": "2026-07-31",
"series": [
{
"id": "boc_m2_gross_monthly",
"label": "M2 (gross), unadjusted",
"unit": "cad",
"frequency": "monthly",
"method": "month_end",
"stock_semantics": "period_end",
"methodology_history": [
{
"effective_from": "1968-01-31",
"effective_through": "1994-01-31",
"method": "monthly_average",
"stock_semantics": "period_average",
"description": "Monthly average of Wednesday observations through January 1994."
},
{
"effective_from": "1994-02-28",
"effective_through": "2020-09-30",
"method": "monthly_average",
"stock_semantics": "period_average",
"description": "Monthly average of daily observations from February 1994 through September 2020."
},
{
"effective_from": "2020-10-31",
"effective_through": null,
"method": "month_end",
"stock_semantics": "period_end",
"description": "Month-end methodology for M2 beginning with October 2020 data."
}
],
"source": {
"id": "boc",
"series_id": "V41552786",
"attribution": "Source: Bank of Canada.",
"notice": "Attribute the Bank of Canada as the source and indicate changes; do not imply Bank endorsement. Changes made: monthly source dates are represented as calendar month-end periods, and source decimal values are normalized exactly to base Canadian dollars. For content included in a paid service or document for sale, tell prospective purchasers before distribution or sale that it came from the Bank of Canada and is available on the Bank of Canada's website free of charge. Third-party content is excluded unless separately licensed."
},
"observations": [
{
"date": "2026-07-31",
"value": "2853114000000",
"method": "month_end",
"stock_semantics": "period_end",
"methodology_description": "Month-end methodology for M2 beginning with October 2020 data."
}
]
}
]
}Monetary observation values are exact decimal strings. The response’s source includes the upstream series id, attribution, and applicable reuse notice. The Bank of Canada M2 series use Valet vectors V41552786 and V41552796, published in millions of dollars; source month-start dates are represented as calendar month-end periods, and values are normalized exactly to base CAD. Bank of Canada total assets use weekly Wednesday vector V36610, also converted exactly from millions of dollars to CAD. Valet v cells must be non-empty decimal strings; JSON numbers and empty cells fail refresh validation, and empty cells are not interpreted as withdrawals. Accepted values are normalized exactly and returned as decimal strings. For Canada M2, top-level method and stock_semantics describe the latest methodology; each observation carries its period-resolved method and stock semantics, and methodology_history lists inclusive effective periods. Both M2 vectors use monthly_average /period_average with Wednesday observations through January 1994 and daily observations from February 1994 through September 2020, then month_end /period_end from October 2020. Third-party M2++ components are excluded. The API reads the database cache; protected cron collection is the only upstream caller.
Source: Bank of Canada Valet Web Services. The source data is available on the Bank’s website free of charge. Attribute the Bank as the source and indicate changes without implying endorsement. See the Bank of Canada terms; third-party content is excluded unless separately licensed.
Government debt, borrowing, and debt interest
One endpoint per metric: /v1/government-debt, /v1/government-borrowing, and /v1/debt-interest. With a country in the path each returns that country's series; the bare path returns the latest observation of every series for each country covered by that endpoint's metric, in registry order. Coverage is metric-specific and comes from registered, servable series; the country parameter below lists each metric's country codes in collection order. Each series declares its government scope, accounting basis, unit, frequency, period_type, method, and a plain-language definition with its sign convention; unlike measures are never renamed into a common definition, so compare series only when scope and basis match. Values are decimal strings; date is the period end, and flows such as borrowing and interest add period_start. Recognized countries without that metric return 404 no_data.
Path and query parameters
| Name | Type | Description |
|---|---|---|
country | path | Case-insensitive country code or path alias. Registered, servable coverage by metric: debt: us, gb (or uk), au, is, ea (or eu), sg, at, be, bg, cy, cz, de, dk, ee, es, fi, fr, gr, hr, hu, ie, it, lt, lu, lv, mt, nl, pl, pt, ro, se, si, sk, no, ca, ch, qa, jp, pe; borrowing: us, gb (or uk), is, ea (or eu), at, be, bg, cy, cz, de, dk, ee, es, fi, fr, gr, hr, hu, ie, it, lt, lu, lv, mt, nl, pl, pt, ro, se, si, sk, no, ca, ch; debt interest: gb (or uk), is, ea (or eu), at, be, bg, cy, cz, de, dk, ee, es, fi, fr, gr, hr, hu, ie, it, lt, lu, lv, mt, nl, pl, pt, ro, se, si, sk, no, ca. An unrecognized code returns 400 invalid_country; a recognized code with no series for this endpoint returns 404 no_data. |
start | YYYY-MM-DD | Optional inclusive first period end. Omit start and end for the latest observation of each series. |
end | YYYY-MM-DD | Optional inclusive last period end; requires start and defaults to today. A range with no observations returns 404 no_data. |
api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/government-borrowing/uk?start=2026-01-01&end=2026-08-31"Response
{
"status": "success",
"metric": "government_borrowing",
"country": "GB",
"country_name": "United Kingdom",
"start": "2026-01-01",
"end": "2026-08-31",
"series": [
{
"id": "ons_net_borrowing_monthly",
"label": "Public sector net borrowing (month)",
"scope": "public_sector",
"basis": "net_borrowing",
"unit": "gbp_millions",
"frequency": "monthly",
"period_type": "month",
"method": "reported",
"definition": "UK public sector net borrowing excluding public sector banks ... A positive value is borrowing (a deficit); a negative value is a surplus. ...",
"source": { "id": "ons_psf", "series_id": "PUSF.DZLS", "attribution": "Source: Office for National Statistics, Public Sector Finances." },
"observations": [
{ "date": "2026-07-31", "value": "2040", "period_start": "2026-07-01" },
{ "date": "2026-08-31", "value": "18268", "period_start": "2026-08-01" }
]
}
]
}United States series are U.S. Treasury Fiscal Service data distributed by FRED, in millions of U.S. dollars. Debt: total public debt outstanding and debt held by the public at each quarter end, plus a quarterly Bullion API debt-to-GDP ratio that divides quarter-end debt by U.S. Bureau of Economic Analysis nominal GDP. Borrowing: the Monthly Treasury Statement budget surplus (+) or deficit (-) for the month, and a Bullion API fiscal year-to-date sum of it from 1 October. US debt interest is not currently available. This product uses the Bureau of Economic Analysis (BEA) Data API but is not endorsed or certified by BEA. United Kingdom series come from the Office for National Statistics Public Sector Finances: public sector net debt, net debt as a percentage of GDP, public sector net borrowing for the month (positive is borrowing), a Bullion API fiscal year-to-date sum of that borrowing from 1 April, and central government debt interest payable; amounts are in millions of pounds. ONS's debt ratio uses GDP for the 12 months centred on the month, which includes projected GDP, so recent ratios are revised. Euro-area and EU member-state series come from Eurostat for general government (Maastricht basis), for the EA21 euro-area aggregate and each of the 27 EU member states, whose series have the same definitions as the aggregate: consolidated gross debt in millions of euro and as a Eurostat-published percentage of GDP at each year end and quarter end, net lending (+) or net borrowing (-) for each calendar year and quarter (negative is net borrowing), and interest expenditure for each calendar year and quarter. Annual and quarterly figures are separate series, quarterly flows are not seasonally adjusted, and recent quarters are provisional and revised later. Norway series come from Statistics Norway (SSB) for the national-accounts general-government sector, in millions of Norwegian kroner: gross public debt at nominal value (annual at 31 December and quarterly), net lending (+) or net borrowing (-) (annual and quarterly, not seasonally adjusted) and annual interest expense only, because Statistics Norway publishes no quarterly interest item. Statistics Norway publishes no debt-to-GDP ratio, so the Norway ratios are Bullion API calculations, marked derived_ratio with their formula and inputs in derivation. Canada series come from Statistics Canada for the national-accounts general-government sector, quarterly only, in millions of Canadian dollars: gross debt at the quarter end, gross debt to GDP as a percentage published by Statistics Canada (its gross debt is defined more widely than the debt series), and net lending (+) or net borrowing (-) and interest on debt for each calendar quarter, unadjusted. Switzerland series come from the Swiss Federal Finance Administration (GFS model, whole-state general government), annual accounts years only, in millions of Swiss francs: gross debt excluding financial derivatives (IMF definition), the Maastricht-style debt and the debt ratio the Administration publishes for it (in percent), and net lending (+) or net borrowing (-); the two debt measures are different and neither is verified as comparable with Eurostat's, and there is no Swiss interest series. Iceland series come from Statistics Iceland (THJ05181 and THJ05121), annual general-government figures: gross debt including pension/insurance liabilities and payables (not Maastricht debt), the published debt-to-GDP ratio, financial balance (surplus positive, deficit negative), and interest payments. Amounts are millions of ISK at current prices; full PX decimal precision is preserved, not rounded CSV/JSON display precision. Qatar series come from the Ministry of Finance and National Planning Council open-data datasets: national central-government public debt in millions of Qatari riyals through 2025, and a Bullion API debt-to-GDP calculation from matched-year nominal GDP only through 2024. No 2025 GDP or ratio is inferred; this is not general-government or Maastricht debt. Forecasts and projections are never returned. Every plan can call these endpoints, and the collection counts as one request. The API reads the database cache; protected cron collection is the only upstream caller.
Sources: U.S. Department of the Treasury, Bureau of the Fiscal Service data via FRED, Federal Reserve Bank of St. Louis. This product uses the FRED® API but is not endorsed or certified by the Federal Reserve Bank of St. Louis. Bullion API is not endorsed by the U.S. Department of the Treasury. GDP for the US debt-to-GDP ratio: U.S. Bureau of Economic Analysis Data API. This product uses the Bureau of Economic Analysis (BEA) Data API but is not endorsed or certified by BEA. UK data: Office for National Statistics, Public Sector Finances. Euro-area and EU member-state data: Eurostat, government finance statistics. Norway data: Source: Statistics Norway (SSB), www.ssb.no, licensed under CC BY 4.0; changes were made (Bullion API calculates the debt-to-GDP ratios), and Statistics Norway does not endorse Bullion API. Canada data: Source: Statistics Canada, tables 36-10-0467-01, 38-10-0237-01 and 36-10-0477-01, reference date shown on each observation. Reproduced and distributed on an "as is" basis with the permission of Statistics Canada (Statistics Canada Open Licence). This does not constitute an endorsement by Statistics Canada of this product. Switzerland data: Source: Swiss Federal Finance Administration (EFV/FFA), "Main aggregates and forecasts with FS- and GFS-Model", published as open government data under the opendata.swiss "Open use" terms (commercial use permitted, source recommended); provided without verification of the derived series and not endorsed by the Federal Finance Administration. Qatar data: Source: Qatar Ministry of Finance, dataset MOF-OD-C0-00004 (public debt); Qatar National Planning Council, dataset NPC-OD-C0-01288 (gross domestic product). Both datasets identify CC BY 4.0; changes were made because Bullion API calculates matched-year debt-to-GDP ratios. As observed 2026-09-30, the Qatar Open Data Portal's generic data-trust-index.ready-for-publication=false flag is not a publisher certification, data withdrawal or deprecation. Iceland data: Source: Statistics Iceland, THJ05181 and THJ05121, under CC BY 4.0. Modifications are not attributed to Statistics Iceland.
Annual central-bank reserves
Annual central-bank reserve values and gold share on every plan. This endpoint is served from Bullion API’s automated cache. The bare /v1/central-bank-reserves returns the latest observation of each series for every covered country.
Parameters
| Name | Type | Description |
|---|---|---|
country | path | ISO 3166-1 alpha-2 code, case-insensitive; uk is accepted for GB. |
start | YYYY-MM-DD | Optional first inclusive date. Omit start and end for the latest observation only. |
end | YYYY-MM-DD | Optional last inclusive date; requires start. Annual observations are dated at year end. |
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/central-bank-reserves/gb?start=2024-01-01&end=2025-12-31"Response
{
"status": "success",
"metric": "central_bank_reserves",
"country": "GB",
"country_name": "United Kingdom",
"start": "2024-01-01",
"end": "2025-12-31",
"series": [
{
"id": "total_reserves_usd",
"label": "Total reserves including gold",
"unit": "usd",
"frequency": "annual",
"method": "reported",
"observations": [
{ "date": "2024-12-31", "value": "180000000000" },
{ "date": "2025-12-31", "value": "200000000000" }
]
},
{
"id": "gold_reserve_value_usd",
"label": "Gold reserve value",
"unit": "usd",
"frequency": "annual",
"method": "derived",
"notes": "Derived by Bullion API from the reported annual reserve values. Total reserves including gold minus total reserves excluding gold. Years whose source values are inconsistent are omitted.",
"observations": [
{ "date": "2024-12-31", "value": "15000000000" },
{ "date": "2025-12-31", "value": "25000000000" }
]
}
]
}Series: total_reserves_usd and reserves_excluding_gold_usd are reported; gold_reserve_value_usd, gold_share_pct and annual_gold_value_change_usd are derived by Bullion API and carry notes. Values are decimal strings. A year whose source values are inconsistent is omitted from the derived series and listed in the reported series’ quality_flags.
Illustrative synthetic values only; this example is not live reserve data. Without start and end, each series returns its own latest observation, so dates can differ across series. Use /v1/countries to discover which countries currently have a queryable reserve dataset; an absent observation is not a zero value. Annual value changes combine holdings changes, gold-price movement, and revisions and do not measure tonnes bought or sold. Reuse rights for public statistics remain separate from API plan terms and are governed by applicable terms.
Countries
Which datasets each country has, with the path to request each one. /v1/countries lists every country with at least one dataset, sorted by code. Coverage reflects the series Bullion API serves, so a dataset is listed only where its endpoint can answer.
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/countries/uk"Response (abridged)
{
"status": "success",
"country": "GB",
"country_name": "United Kingdom",
"aliases": ["uk"],
"datasets": [
{ "metric": "policy_rates", "path": "/v1/policy-rates/gb" },
{ "metric": "inflation", "path": "/v1/inflation/gb" },
{ "metric": "central_bank_reserves", "path": "/v1/central-bank-reserves/gb" }
]
}The example is abridged; the full datasets list includes every metric served for the country. An unknown code returns 400 invalid_country; a valid code with no dataset returns 404 no_data. aliases lists accepted path aliases such as uk and eu.
Central-bank gold holdings
Monthly central-bank gold holdings, including weekly, fortnightly, quarterly and irregular sources on every plan, served from Bullion API’s scheduled cache of reported holdings. GET /v1/central-bank-gold/{country} returns one country’s series; GET /v1/central-bank-gold returns the latest observation of each series for every covered country, ordered by country code, so you can sort it into a leaderboard.
Parameters
| Name | Type | Description |
|---|---|---|
country | path, ISO2 | Case-insensitive country code such as de; uk is accepted for GB. Unknown codes return invalid_country; countries without coverage return no_data. |
start / end | YYYY-MM-DD | Optional inclusive dates, up to 10 years apart. Omit both for the latest observation of each series; start alone runs to today. |
Each series is one measure — holdings_tonnes, holdings_million_fine_troy_oz, holdings_change_tonnes, reported_value_usd_millions, reported_value_eur_millions and the other reported currency values — at one cadence, so weekly, fortnightly, monthly, quarterly and irregular releases are never combined. Values are decimal strings. Monthly, quarterly and irregular observations are dated at the end of their reference month; weekly and fortnightly observations retain their reference date. Every series carries holdings_basis, cadence, a stale flag that stays visible after failed refreshes, a provenance object and a reuse_notice covering the returned observations; a stock-change series also covers the prior observation used in its calculation. A range with no data returns no_data. When weight is reported in fine troy ounces, the tonnes series says Bullion API converted that reported weight; the original ounce series remains available. A quantity derived from value is labelled separately and never presented as reported weight. Use /v1/countries to discover current per-country coverage; a country can have a subset of measures, and a missing observation is not a zero value.
Example
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/central-bank-gold/de?start=2026-05-01&end=2026-06-30"Response
{
"status": "success",
"metric": "central_bank_gold",
"country": "DE",
"country_name": "Germany",
"start": "2026-05-01",
"end": "2026-06-30",
"series": [{
"id": "holdings_tonnes_monthly",
"label": "Gold holdings converted by Bullion API from reported weight (tonnes)",
"measure": "holdings_tonnes",
"unit": "tonnes",
"frequency": "monthly",
"cadence": "monthly",
"method": "fixed_unit_conversion_from_reported_weight",
"holdings_basis": "reported",
"stale": false,
"provenance": {
"name": "Synthetic example",
"methodology": "Synthetic example: monthly gold holdings reported in million fine troy ounces.",
"updated_at": "2026-07-15"
},
"reuse_notice": "Synthetic example: source terms and Bullion API plan terms apply; standard plans do not authorize raw-data redistribution or resale.",
"observations": [
{ "date": "2026-05-31", "value": "1000.00" },
{ "date": "2026-06-30", "value": "1000.00" }
]
}, {
"id": "holdings_change_tonnes_monthly",
"label": "Bullion API-calculated change in reported gold holdings (tonnes; stock change, not purchases or sales)",
"measure": "holdings_change_tonnes",
"unit": "tonnes",
"frequency": "monthly",
"cadence": "monthly",
"method": "stock_change",
"holdings_basis": "reported",
"stale": false,
"provenance": {
"name": "Synthetic example",
"methodology": "Synthetic example: monthly gold holdings reported in million fine troy ounces.",
"updated_at": "2026-07-15"
},
"reuse_notice": "Synthetic example: source terms and Bullion API plan terms apply; standard plans do not authorize raw-data redistribution or resale.",
"observations": [{ "date": "2026-06-30", "value": "0" }]
}]
}Illustrative synthetic values only; this example is not live data. Changes between observations describe changes in reported stock, not purchases or sales; they also reflect valuations and revisions. Value-only measures carry no quantity series rather than invented tonnes, and some valuations use a fixed official price rather than market prices. reported_value_sgd_millions is a combined gold and foreign-exchange reserve value, not a gold-only figure. Reuse rights for public statistics remain separate from API plan terms and are governed by applicable terms.
Multi-metal portfolio settings
Configure one private multi-metal portfolio and its reporting currency. Both methods accept X-API-Key only; the api_key query parameter is not accepted for portfolio routes.
Existing and new keys start with portfolio access set to None. Grant Read for GET, or Read and write for GET and PUT, from the API keys dashboard. Write always includes read. Portfolio requests keep the rolling per-key and source-IP abuse limits but do not increment the monthly market-data quota.
Create or change the reporting currency
Send { "reporting_currency": "GBP" } with PUT. The value must be one of the supported currencies below and can change while the portfolio has no transactions.
Read the portfolio
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio"Empty portfolio response
{
"status": "success",
"portfolio": {
"id": "d799789a-e1af-4c41-a79a-611e18f0df31",
"metals": ["gold", "silver", "palladium", "copper", "platinum"],
"reporting_currency": "GBP",
"created_at": "2026-08-14T11:00:00.000Z",
"updated_at": "2026-08-14T11:00:00.000Z"
},
"summary": {
"gold_quantity_grams": "0.000000000000000",
"metals": [],
"cost_basis": "0.00000000",
"realised_pnl": "0.00000000",
"valuation": {
"status": "unavailable",
"spot_price": null,
"current_value": null,
"unrealised_pnl": null,
"price_timestamp": null
}
}
}Decimal summary values remain strings, including zero. Valuation comes from the latest stored matching metal spot price in the reporting currency and is current when that price is at most six hours old, stale when older, and unavailable when no matching-currency price exists — there is no cross-currency fallback and portfolio reads never call an upstream provider. When unavailable, spot_price, current_value, unrealised_pnl, and price_timestamp are null rather than zero; holdings and cost basis always remain available. Missing portfolios return portfolio_not_found, and keys without the required action return insufficient_scope.
Transactions accept metal: gold, silver, palladium, copper or platinum. Omitted metal defaults to gold. Sales consume only matching-metal lots, and an existing transaction's metal cannot be edited. The summary's metals array gives each metal's quantity, cost basis, realised gains and valuation. The legacy gold_quantity_grams value contains gold only; a mixed-metal aggregate has no single spot price. Projected currency valuations become unavailable when their conversion snapshot is more than seven UTC calendar days old.
Record an exact purchase
POST /v1/portfolio/transactions with portfolio write access and an opaque Idempotency-Key header. Quantity accepts up to eight fractional digits in grams, kilograms, or troy ounces and is returned in grams at 15 decimal places. Money is returned as eight-place decimal strings using round-half-up.
curl -X POST "https://api.bullionapi.dev/v1/portfolio/transactions" \
-H "X-API-Key: bullion_your_key" \
-H "Idempotency-Key: purchase-2026-08-19-001" \
-H "content-type: application/json" \
--data '{"transaction_date":"2026-08-19","quantity":"1.005","weight_unit":"troy_oz","unit_price":"2500.125","delivery_cost":"12.345678945","fees":"1.005","form":"coin","cgt_status":"exempt"}'A repeated canonical request with the same key replays its original result. A changed request with that key returns 409 idempotency_conflict. Dates must be real calendar dates no later than today; descriptions are optional and limited to 500 characters. Invalid fields return 400 invalid_purchase with field details.
List open lots and allocate a sale explicitly
GET /v1/portfolio/lots lists only your open purchase lots with a positive remaining quantity, ordered by purchase date, creation time, then ID. Each lot carries its stable lot_id, originating purchase_transaction_id, date, form, CGT status, remaining quantity, and remaining cost basis.
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/lots"{
"status": "success",
"lots": [{
"lot_id": "22222222-2222-4222-8222-222222222222",
"purchase_transaction_id": "11111111-1111-4111-8111-111111111111",
"transaction_date": "2026-08-19",
"form": "coin",
"cgt_status": "exempt",
"remaining_quantity_grams": "31.258994184000000",
"remaining_cost_basis": "2525.97630395"
}]
}A sale defaults to FIFO lot selection. To choose the disposed lots yourself, send allocations on the sale body: an array of { "lot_id", "quantity_grams" } objects whose quantities sum exactly to the sale quantity. Every selected lot must belong to your account, match the sale's form and CGT treatment, predate or equal the sale date, and have enough remaining quantity. Any missing, foreign, duplicated, incompatible, over-allocated, or under-allocated lot fails the whole sale without mutation: eligibility and exact-sum violations return 400 invalid_allocation, lots dated after the sale return 400 sale_date_invalid, and exhausted selections return 409 insufficient_holdings. Committed sales report their allocation_method as fifo or explicit and return the committed allocations.
Browse transaction history
GET /v1/portfolio/transactions returns only the authenticated owner's transactions. Results use the stable order transaction_date DESC, created_at DESC, id DESC. The limit defaults to 50 and is capped at 100. Follow the opaque cursor in next_cursorwith cursor for the next page; do not depend on its internal format.
Filter with type, form, and cgt_status, and restrict the period with either calendar_year=2025 (1 January through 31 December inclusive) or uk_tax_year=2025-26 (6 April through 5 April inclusive; the two years in the label must be consecutive). The two period filters are mutually exclusive. Invalid values return invalid_filter, invalid_calendar_year, invalid_tax_year, or conflicting_periods.
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/transactions?limit=50&uk_tax_year=2025-26"{
"status": "success",
"transactions": [{
"metal": "gold",
"type": "purchase",
"version": 1,
"id": "11111111-1111-4111-8111-111111111111",
"transaction_date": "2026-08-19",
"created_at": "2026-08-19T12:00:00.000Z",
"entered_quantity": "1.00500000",
"weight_unit": "troy_oz",
"quantity_grams": "31.258994184000000",
"unit_price": "2500.12500000",
"gross_cost": "2512.62562500",
"delivery_cost": "12.34567895",
"fees": "1.00500000",
"total_cost": "2525.97630395",
"cost_per_gram": "80.80798407",
"form": "coin",
"cgt_status": "exempt",
"description": "One-ounce gold coin",
"lots": [{
"metal": "gold",
"id": "22222222-2222-4222-8222-222222222222",
"open_quantity_grams": "31.258994184000000",
"open_cost_basis": "2525.97630395"
}],
"allocations": [],
"gross_proceeds": "0.00000000",
"net_proceeds": "0.00000000",
"disposed_cost": "0.00000000",
"realised_pnl": "0.00000000"
}],
"next_cursor": null
}GET /v1/portfolio/transactions/{id} returns the same transaction representation, including its open lot and allocation collections. Decimal quantities and monetary values are always strings. Invalid page, filter, or period parameters return invalid_limit, invalid_cursor, invalid_filter, invalid_calendar_year, invalid_tax_year, or conflicting_periods; missing and cross-account identifiers return transaction_not_found. Every response carries an ETag header with the quoted transaction version.
Edit a recorded transaction
PATCH /v1/portfolio/transactions/{id} corrects one owned transaction without corrupting its allocations. Send If-Match: "3" quoting the current version from the ETag header. The body replaces every mutable field — date, quantity, weight unit, unit price, delivery cost, fees, form, CGT status, and description — while type and ownership stay fixed.
curl -X PATCH "https://api.bullionapi.dev/v1/portfolio/transactions/11111111-1111-4111-8111-111111111111" \
-H "X-API-Key: bullion_your_key" \
-H 'If-Match: "1"' \
-H "content-type: application/json" \
--data '{"transaction_date":"2026-08-19","quantity":"1.005","weight_unit":"troy_oz","unit_price":"2510.5","delivery_cost":"12.34","fees":"1.005","form":"coin","cgt_status":"exempt"}'A successful edit increments the version, returns the fresh ETag, and recalculates affected values immediately: purchases refresh their lot totals, and sales release and rebuild only their own allocations using the stored FIFO or explicit method before recomputing disposed cost and realised P&L. A purchase edit must leave enough quantity and basis for every sale that allocated it, cannot move after such a sale, and cannot change form or CGT treatment while those allocations exist. A missing or stale precondition returns 412 stale_transaction: reload the latest record and resubmit deliberately. Foreign identifiers return 404 transaction_not_found, and every rejected edit rolls back completely.
Delete a transaction
DELETE /v1/portfolio/transactions/{id} removes an owned purchase or sale with portfolio write access and rebalances your holdings. Send the transaction's version as an ETag-style If-Match header ("1"); a missing or mismatched version returns 412 stale_transaction without mutating anything. Deleting a sale restores every allocated quantity and cost basis to its originating purchase lot.
curl -X DELETE "https://api.bullionapi.dev/v1/portfolio/transactions/11111111-1111-4111-8111-111111111111" \
-H "X-API-Key: bullion_your_key" \
-H 'If-Match: "1"'{ "status": "success", "deleted_transaction_id": "11111111-1111-4111-8111-111111111111" }A purchase that has already funded a sale cannot be deleted and returns 409 transaction_in_use; delete the dependent sale first. Missing and cross-account identifiers return transaction_not_found.
Tax-year gains
GET /v1/portfolio/gains sums realised profit or loss from your sale transactions inside one period and groups it by the CGT status recorded on each sale. Supply exactly one of calendar_year=2025 (1 January through 31 December inclusive) or uk_tax_year=2025-26 (6 April through 5 April inclusive; the two years in the label must be consecutive) — the two filters are mutually exclusive.
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/gains?uk_tax_year=2025-26"curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/gains?calendar_year=2025"{
"status": "success",
"period": {
"kind": "uk_tax_year",
"label": "2025-26",
"start": "2025-04-06",
"end": "2026-04-05"
},
"buckets": {
"exempt": "1234.50500000",
"not_exempt": "99.99400000",
"unknown": "0.00000000"
},
"total": "1334.49900000",
"metals": [{ "metal": "gold", "total": "1334.49900000" }]
}The response echoes the resolved period and returns buckets.exempt, buckets.not_exempt, and buckets.unknown plus their total, all as exact decimal strings. Omitting both filters returns missing_period; supplying both returns conflicting_periods; malformed labels return invalid_calendar_year or invalid_tax_year. Figures exclude annual allowances, matching and pooling rules, and tax rates. They are not tax advice.
Tax-year gains
GET /v1/portfolio/gains sums realised profit or loss from your sale transactions inside one period and groups it by the CGT status recorded on each sale. Supply exactly one of calendar_year=2025 (1 January through 31 December inclusive) or uk_tax_year=2025-26 (6 April through 5 April inclusive; the two years in the label must be consecutive) — the two filters are mutually exclusive.
curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/gains?uk_tax_year=2025-26"curl -H "X-API-Key: bullion_your_key" \
"https://api.bullionapi.dev/v1/portfolio/gains?calendar_year=2025"{
"status": "success",
"period": {
"kind": "uk_tax_year",
"label": "2025-26",
"start": "2025-04-06",
"end": "2026-04-05"
},
"buckets": {
"exempt": "1234.50500000",
"not_exempt": "99.99400000",
"unknown": "0.00000000"
},
"total": "1334.49900000",
"metals": [{ "metal": "gold", "total": "1334.49900000" }]
}The response echoes the resolved period and returns buckets.exempt, buckets.not_exempt, and buckets.unknown plus their total, all as exact decimal strings. Omitting both filters returns missing_period; supplying both returns conflicting_periods; malformed labels return invalid_calendar_year or invalid_tax_year. Figures exclude annual allowances, matching and pooling rules, and tax rates. They are not tax advice.
Rate limits
Authenticated successful market-data /v1 responses include these headers so you can track usage in real time. X-API-CURRENT and X-API-QUOTA are additive compatibility aliases; the existing x-ratelimit-* names remain supported. Portfolio responses omit these monthly quota headers because portfolio operations do not use the monthly market-data quota. Each API key also permits 60 requests per rolling 60-second window across REST; short-window denials return rate_limitedand do not consume monthly quota.
| Header | Type | Description |
|---|---|---|
| x-ratelimit-limit | integer | Your plan’s monthly cap. |
| x-ratelimit-used | integer | Requests used this month. |
| x-ratelimit-remaining | integer | Requests remaining. |
| x-ratelimit-reset | ISO 8601 | When the quota resets (1st 00:00 UTC). |
| X-API-CURRENT | integer | Compatibility alias for x-ratelimit-used. |
| X-API-QUOTA | integer | Compatibility alias for x-ratelimit-limit. |
Error responses
This is a breaking change: every application-authored JSON HTTP error now uses one nested envelope. Clients must stop reading legacy top-level error, code, and quota fields. The HTTP status remains the source of truth. Better Auth, framework-owned responses keep their own protocol contracts.
Any /v1 path without an API operation returns 404 with code not_found for every method, without authentication or quota use.
401 — Missing API key
HTTP/1.1 401 Unauthorized
content-type: application/json
{
"status": "error",
"error": {
"code": "missing_api_key",
"message": "Missing API key"
}
}401 — Conflicting credentials
HTTP/1.1 401 Unauthorized
content-type: application/json
{
"status": "error",
"error": {
"code": "conflicting_credentials",
"message": "Conflicting API key credentials"
}
}429 — Monthly quota exceeded
HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 30
x-ratelimit-used: 30
x-ratelimit-remaining: 0
x-ratelimit-reset: 2026-08-01T00:00:00.000Z
X-API-CURRENT: 30
X-API-QUOTA: 30
retry-after: 86400
{
"status": "error",
"error": {
"code": "quota_exceeded",
"message": "Your monthly usage limit has been reached. Please upgrade your subscription plan.",
"plan": "free",
"used": 30,
"limit": 30,
"reset_at": "2026-08-01T00:00:00.000Z"
}
}429 — Rolling API-key limit
HTTP/1.1 429 Too Many Requests
retry-after: 42
{
"status": "error",
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}400 — Bad request
HTTP/1.1 400 Bad Request
content-type: application/json
{
"status": "error",
"error": {
"code": "invalid_range",
"message": "The requested range is invalid."
}
}400 — Invalid unit
HTTP/1.1 400 Bad Request
content-type: application/json
{
"status": "error",
"error": {
"code": "invalid_unit",
"message": "The requested output unit is invalid."
}
}Registered error codes
| Code | Status | Description |
|---|---|---|
| config_error | 500 | The API is not configured correctly. |
| billing_state_conflict | 503 | The billing state needs support attention. |
| annual_billing_unavailable | 503 | Annual billing is currently unavailable. |
| billing_interval_change_conflict | 409 | A billing interval change conflicts with the current billing state. |
| billing_interval_change_expired | 409 | The billing interval change preview has expired. |
| billing_interval_change_failed | 402 | The billing interval change payment failed. |
| billing_interval_change_invalid | 400 | The billing interval change is invalid. |
| billing_interval_change_origin_forbidden | 403 | This billing action requires a trusted origin. |
| billing_interval_change_revalidation_conflict | 409 | The subscription could not be verified for an annual-to-monthly change. |
| billing_interval_change_processing | 409 | The billing interval change is already processing. |
| plan_change_conflict | 409 | The plan change conflicts with the current billing state. |
| plan_change_expired | 409 | The plan change preview has expired. Please preview it again. |
| plan_change_failed | 402 | The plan change payment failed. Your plan has not changed. |
| plan_change_invalid | 400 | The plan change is invalid. |
| billing_cancellation_conflict | 409 | The subscription cancellation conflicts with the current billing state. |
| billing_cancellation_failed | 503 | The subscription cancellation could not be applied. |
| billing_cancellation_invalid | 400 | The subscription cancellation is invalid. |
| conflicting_credentials | 401 | The supplied API-key credentials do not match. |
| data_unavailable | 503 | The requested data is not available. |
| email_not_verified | 403 | The API-key owner must verify their email address. |
| invalid_api_key | 401 | The API key is invalid or revoked. |
| invalid_country | 400 | The country code is invalid. |
| invalid_currency | 400 | The currency code is invalid. |
| invalid_date | 400 | The date format is invalid. |
| invalid_range | 400 | The requested range is invalid. |
| invalid_series_filter | 400 | The series filter value is invalid. |
| invalid_unit | 400 | The requested output unit is invalid. |
| invalid_window | 400 | The requested window is invalid. |
| method_not_allowed | 405 | The API does not support this method. |
| missing_api_key | 401 | An API key is required. |
| missing_params | 400 | Required request parameters are missing. |
| no_data | 404 | Cached data is not available. |
| not_found | 404 | No API operation exists at the requested path. |
| plan_required | 403 | The request requires an eligible subscription plan. |
| quota_exceeded | 429 | The monthly request quota has been exceeded. |
| rate_limited | 429 | The API key has exceeded its rolling request window. |
| unauthenticated | 401 | A signed-in session is required. |
| api_key_not_found | 404 | The API key was not found. |
| insufficient_scope | 403 | API key lacks the required portfolio scope |
| invalid_portfolio_permission | 400 | The portfolio permission is invalid. |
| invalid_price_alert | 400 | The price alert request is invalid. |
| invalid_digest_preference | 400 | The digest preference request is invalid. |
| price_alert_exists | 409 | You already have an active price alert for this currency, metal, and target. |
| price_alert_limit | 409 | You can have up to 10 active price alerts. |
| price_alert_not_found | 404 | The price alert was not found. |
| price_alert_conflict | 409 | The alert has changed or its email is being sent. Refresh your alerts and try again. |
| portfolio_not_found | 404 | The account does not have a portfolio. |
| portfolio_currency_locked | 409 | The reporting currency is locked after the first purchase. |
| missing_idempotency_key | 400 | An Idempotency-Key header is required for portfolio mutations. |
| invalid_purchase | 400 | The purchase request is invalid |
| invalid_sale | 400 | The sale request is invalid |
| invalid_allocation | 400 | The selected sale lots are invalid. |
| idempotency_conflict | 409 | The Idempotency-Key was already used with another request. |
| invalid_cursor | 400 | The transaction history cursor is invalid. |
| invalid_limit | 400 | The transaction history page size is invalid. |
| invalid_filter | 400 | The transaction history filter value is invalid. |
| invalid_calendar_year | 400 | The transaction history calendar year is invalid. |
| invalid_tax_year | 400 | The UK tax-year label is invalid. |
| missing_period | 400 | Use either calendar_year or uk_tax_year. |
| conflicting_periods | 400 | Use either calendar_year or uk_tax_year, not both. |
| transaction_not_found | 404 | The transaction was not found. |
| stale_transaction | 412 | Load the transaction again and retry with its latest ETag. |
| transaction_in_use | 409 | Gold from this purchase has been sold and cannot be deleted. |
| sale_date_invalid | 400 | The sale date is invalid. |
| insufficient_holdings | 409 | The portfolio does not have enough gold for this sale. |
| conflicting_params | 400 | The request parameters conflict. |
| invalid_period | 400 | The period is invalid. |
| no_change_data | 404 | Cached metal data is missing for one or both requested dates. |
| invalid_change_baseline | 422 | The starting price must be non-zero. |
| health_degraded | 503 | The database is unreachable or cached data is stale. |
| runtime_unavailable | 503 | The service is temporarily unavailable. |
| unknown_metal | 400 | The requested metal is invalid. |
| internal_error | 500 | The daily image data could not be loaded. |
| render_error | 500 | The daily image could not be rendered. |
| invalid_json | 400 | Invalid JSON |
| invalid_email | 400 | Invalid email |
| invalid_request | 400 | Invalid one-click unsubscribe request |
| invalid_token | 400 | Invalid or expired unsubscribe token |
| expired_token | 400 | Invalid or expired unsubscribe token |
Supported metals and currencies
The supported metal slugs are gold, silver, palladium, copper, and platinum. All metal prices use troy ounces by default. The authenticated GET /v1/symbols route returns the metal descriptions and the currently serviceable currencies.
Reviewed currencies in this deployment’s static configuration: USD, EUR, GBP. Static documentation does not check materialised price rows. Authenticated GET /v1/symbols is authoritative for currently serviceable runtime currency choices. Projected currencies require all five metal prices to have materialised with FX provenance. USD is the default.
Currency conversion input attribution: Exchange Rates By UniRateAPI. These are Bullion API conversion snapshots, not provider end-of-day rates.
Prices are informational only. They are not live FX rates, executable prices, transaction benchmarks or binding quotes, and must not be used for trading or settlement. See the price methodology for how native and projected prices are produced.
Price methodology
Native prices: USD, EUR and GBP prices are metal observations from the scheduled price pipeline. Metal observations refresh hourly and API responses are served from the cache.
Projected prices: every other currency listed by GET /v1/symbols is derived by converting the hourly canonical USD metal observation with the most recent accepted Bullion API daily FX snapshot. Snapshots are captured on weekdays at about 23:10 UTC, with a retry if that capture fails. Weekends, and any weekday when both captures fail, keep using the last accepted snapshot, which keeps its original date. A snapshot more than seven UTC calendar days older than the price it converts is treated as stale. Once the latest snapshot is more than seven UTC calendar days old, latest projected prices are withheld until a new snapshot is accepted, so an old conversion is never shown as current.
The snapshot time is when Bullion API captured the snapshot. It is not a provider end-of-day reference rate. The FX source does not supply a verified upstream observation time, so none is stored or returned, and Bullion API never invents one. Projected-currency history holds at most one daily point per metal for each weekday snapshot, recorded when that metal has an observation on the snapshot day, starting on the date a currency is activated; earlier history is not backfilled.
Prices are informational only. They are not live FX rates, executable prices, transaction benchmarks or binding quotes, and must not be used for trading or settlement. FX rates are internal conversion inputs: Bullion API does not publish, redistribute or resell raw FX rates and has no FX or currency-conversion endpoint.
Currency conversion input attribution: Exchange Rates By UniRateAPI.
Postman collection
Download the official collection to explore Bullion API in Postman. Requests use https://api.bullionapi.dev and include examples for prices, macro data, central-bank reserves and your portfolio.
- Import the downloaded JSON file into Postman.
- Create an API key in your dashboard after verifying your email.
- Set the collection variable
apiKeylocally to your key. Keep it private and do not share exports containing credentials. - Leave
baseUrlset tohttps://api.bullionapi.dev. Start with Service health, then the symbols or latest prices request.
Every plan can send every request. Market-data and central-bank requests consume quota. Saved examples are synthetic. Update sample dates and identifiers before sending requests.
Portfolio writes are disabled by default. Set enableWrites to exactly true only when you intend to change your account data, and review each request first. The optional account usage request requires a dashboard session cookie configured in Postman; an API key does not authenticate it.
OpenAPI spec
The full API contract as OpenAPI 3.1, for code-gen, Postman and AI function-calling clients: api.bullionapi.dev/openapi.json.