# Bullion API — full documentation > Concatenated markdown documentation for Bullion API, generated from the .md variants linked in https://bullionapi.dev/llms.txt. --- # Bullion API documentation Base URL: `https://api.bullionapi.dev` New accounts: verify your email and open the dashboard to receive your first API key and a ready-to-run cURL automatically. Save the full key when it is shown; it is not recoverable later. Existing accounts can create a key from the API keys page. Keys have no portfolio access by default. 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. 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 HTTP 401 with code `conflicting_credentials`. Usage rights: Free and Pro are for non-commercial use of the Bullion API Service only. Business includes commercial display and integration. Standard plans do not permit redistribution of raw or substantially equivalent market-price data or redistribution or resale of API access. Where the source grants reuse or redistribution rights in particular public statistics, those rights remain unaffected by plan and are subject to the source terms. See https://bullionapi.dev/pricing and https://bullionapi.dev/terms. Currency conversion input attribution: [Exchange Rates By UniRateAPI](https://unirateapi.com/). Bullion API stores informational conversion snapshots; they are not provider end-of-day rates or a standalone currency-data product. ## Quick start Read the latest price. If you don't have an API key yet, get one at https://bullionapi.dev/signup. Build indicative valuations, reference displays and charts from metal prices. Metal observations refresh hourly; projected currencies use an accepted daily FX snapshot. These estimates are not executable checkout prices or binding quotes and must not be used for trading or settlement. A market-data request consumes one monthly request once it passes authentication and quota checks and reaches endpoint execution, even if it returns a validation error, missing data or a controlled service error. Repeated calls and retries each count; there is no automatic refund. Requests rejected before this point do not consume monthly allowance. Missing or invalid credentials, unverified accounts, insufficient MCP scopes, short-window rate-limit denials and exhausted monthly quota do not consume a new monthly request. MCP tool discovery and input schema failures, the currency catalogue, usage reads and portfolio operations also do not consume monthly market-data allowance. Endpoint validation after a quota claim is different from an MCP input schema rejection before execution. ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/gold/price?currency=USD" ``` ```ts const res = await fetch('https://api.bullionapi.dev/v1/gold/price?currency=USD', { headers: { 'X-API-Key': process.env.BULLION_API_KEY! }, }); const { series } = await res.json(); console.log(`Gold: $${series[0].observations[0].value}/oz`); ``` ```py import os, requests r = requests.get( 'https://api.bullionapi.dev/v1/gold/price', params={'currency': 'USD'}, headers={'X-API-Key': os.environ['BULLION_API_KEY']}, ) r.raise_for_status() print(f"Gold: ${r.json()['series'][0]['observations'][0]['value']}/oz") ``` For spreadsheet or no-code clients that cannot set custom headers, use the fallback query URL below. Treat it as a secret and prefer the header transport whenever possible. ```text GET https://api.bullionapi.dev/v1/gold/price?currency=USD&api_key=bullion_your_key ``` ## GET /v1/{metal}/price Returns the spot price of one metal in the requested base currency as a single `_price` series: the latest price, or daily history when `start` and `end` are supplied. 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-only reader that never calls an upstream provider. ### Query parameters | Name | Type | Description | |---|---|---| | currency | string | ISO 4217 currency code. Defaults to USD. | | unit | string | Optional output mass unit: gram/g, kilogram/kg, or troy_oz/toz. Defaults to troy_oz; invalid values return 400 invalid_unit. | | 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. | ### Response (latest) ```json { "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" }] } ] } ``` 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. Unsupported currencies return `400 invalid_currency`, unsupported units `400 invalid_unit`, and a missing cached price `404 no_data`; cache read failures return `503 data_unavailable`. ### Historical prices History series include `coverage.earliest`, `coverage.latest` and `coverage.observation_count` for usable stored weekday closes before request time, independently of the requested range. The five-year query limit does not guarantee five years or 365 days of data. Dates and counts do not establish uninterrupted history or freshness. Projected history begins at activation without backfill. ```bash 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" ``` ```json { "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", "coverage": { "earliest": "2025-07-08", "latest": "2026-07-08", "observation_count": 250 }, "observations": [ { "date": "2025-07-08", "value": "3320.1" }, { "date": "2025-07-09", "value": "3342.55" } ] } ] } ``` With a range, 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 accrues from hourly refreshes and is not backfilled from gold-only history. 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`. ### Projected-currency provenance For a configured projected currency the observation time is the hourly canonical USD metal observation used in the projected price, and the series adds a `conversion` object; it is omitted for USD, EUR, and GBP: ```json "conversion": { "snapshot_at": "2026-08-24T23:10:00.000Z", "stale": false, "attribution": "Exchange Rates By UniRateAPI", "attribution_url": "https://unirateapi.com/" } ``` `snapshot_at` is the newest daily Bullion API conversion snapshot used by the series, 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 (404 `no_data`) until a new snapshot is accepted. Internal UniRateAPI observations are never returned by this endpoint. 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. ## GET /v1/gold/carat Returns gold prices per gram for the approved karat keys: `24k`, `23k`, `22k`, `21.6k`, `21k`, `18k`, `16k`, `14k`, `12k`, `10k`, `9k`, `8k`, and `6k`. Gold is the only supported metal and grams are the only response unit. Without `date`, the current cached gold row is used; with `date=YYYY-MM-DD`, the final cached gold observation in that UTC day is used. This is a database-only reader and never calls upstream providers or bridges currencies. ### Query parameters | Name | Type | Description | |---|---|---| | currency | string | Three-letter ISO 4217 code, case-insensitive. Defaults to USD and must have cached gold data. | | date | YYYY-MM-DD | Optional exact UTC day. The final cached 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 ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/gold/carat?currency=USD" ``` ### Response ```json { "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" } } ``` Each value is a decimal string, calculated from the exact karat ratio against the unrounded pure-gold per-gram value and rounded half-up to eight decimal places. Malformed currency or date values return `400 invalid_currency` or `400 invalid_date`; a valid but unseeded currency or day returns `404 no_data`; cache read failures return `503 data_unavailable`. ## GET /v1/{metal}/change Returns one metal's absolute and percentage change between the final cached observation of two UTC days. Each metal has its own route: `/v1/gold/change`, `/v1/silver/change`, `/v1/palladium/change`, `/v1/copper/change`, and `/v1/platinum/change`. The API 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. | Provide either start (with an optional end) or one period, not both. ### Example ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/silver/change?currency=USD&period=1w" ``` ### Response ```json { "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 within 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. Mixing date forms returns `400 conflicting_params` and supplying neither returns `400 missing_params`; invalid dates return `400 invalid_date`, reversed or overlong ranges return `400 invalid_range`, unsupported periods return `400 invalid_period`, and unsupported currencies or units return `400 invalid_currency` or `400 invalid_unit`. Missing cached data returns `404 no_change_data`; a zero start price returns `422 invalid_change_baseline`. ## GET /v1/gold-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 stored observations in each UTC day within the inclusive range. Missing days are omitted and no history is fabricated. Each `value` is an exact decimal string rounded half-up to eight places (a JSON number before 2026-10-07). The calculation is cached in memory for five minutes per currency and range. API requests never call upstream providers. ### 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 for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | ### Example ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/gold-silver-ratio?currency=USD" ``` ### Response ```json { "status": "success", "currency": "USD", "start_date": null, "end_date": null, "points": [{ "date": "2026-09-24", "value": "68.40312345" }] } ``` Invalid currencies and incomplete, reversed or overlong date ranges return `400`. A latest request without both stored metal rows, or whose gold and silver observations are more than six hours apart, returns `404 no_data`; a historical request with no overlapping observations returns `200` with an empty `points` array. ## GET /v1/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. 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. API requests never call upstream providers. ### Query parameters | Name | Type | Description | |---|---|---| | window | integer | Required. 50 or 200 stored daily closes; a missing or other value returns `400 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 for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | ### Example ```bash 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 ```json { "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" }] } ``` Omitting either date returns `400 missing_params`; invalid dates, reversed or overlong ranges, and invalid currencies return `400`. ## GET /v1/gold/volatility Returns annualised sample volatility of 30 daily gold returns as a fraction in an exact decimal string rounded half-up to eight places, so `"0.15000000"` is 15% (a JSON number before 2026-10-07). Days before 30 returns exist are omitted, so a short range can return an empty `points` array. Uses the final stored gold observation for each UTC weekday; weekend rows are excluded and missing days are omitted. The calculation is cached in memory for five minutes per currency and range. API requests never call upstream providers. ### 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 for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | ### Example ```bash 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 ```json { "status": "success", "currency": "USD", "window": 30, "start_date": "2026-01-01", "end_date": "2026-12-31", "points": [{ "date": "2026-01-01", "value": "0.15000000" }] } ``` Omitting either date returns `400 missing_params`; invalid dates, reversed or overlong ranges, and invalid currencies return `400`. ## GET /v1/gold/real-price and GET /v1/gold/yield-correlation Both require inclusive `start`/`end` dates, at most five years, and USD only. `real_price` returns stored weekday USD gold × latest stored seasonally adjusted CPI-U index / index for the observation month, with `reference_period` (the published month end). Missing CPI months are omitted without carrying forward. `corr_10y_yield` returns Pearson correlation over 90 aligned simple daily gold returns and daily 10-year real Treasury yield changes, with `window: 90`. Both current and previous dates must match. Up to one calendar year of lookback is read; incomplete and zero-variance windows are omitted. Stored inputs are selected by SQL, then calculated exactly as eight-place decimal strings. There are no upstream calls during customer requests. Successful nonempty series have a five-minute cache. Missing source data returns `503 dependency_not_ready` with a public dependency name and metric; other endpoints remain independent. Responses include source-institution attribution. All plans have access. These calculations use revised stored data; they are not point-in-time backtests. ## GET /v1/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 in an exact decimal string rounded half-up to eight places: `"-0.25000000"` is 25% below the peak (a JSON number before 2026-10-07). Values before enough observations exist are partial warm-up drawdowns. Uses the final stored gold observation for each UTC weekday; weekend rows are excluded and missing days are omitted. The calculation is cached in memory for five minutes per currency and range. API requests never call upstream providers. ### 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 for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | ### Example ```bash 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 ```json { "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" }] } ``` Omitting either date returns `400 missing_params`; invalid dates, reversed or overlong ranges, and invalid currencies return `400`. ## GET /v1/policy-rates/{country} **Central-bank policy rates, one country per request.** US series are the federal funds target range lower and upper bounds (`policy_target_lower`, `policy_target_upper`) and the effective federal funds rate; euro-area series are the ECB deposit facility, main refinancing and marginal lending rates; the UK series is BoE Bank Rate (`IUDBEDR`). Policy rates are stored per daily source observation (`frequency: daily`). `GET /v1/policy-rates` returns the latest value for every covered country. ## GET /v1/inflation/{country} **Inflation measures, one country per request.** Filter with `basis=year_over_year|breakeven|implied|expectation`. BLS CPI-U, ONS CPI (`D7G7`) and euro-area HICP are year on year; US Treasury 5Y/10Y/30Y breakevens and BoE RPI-linked implied inflation at 2Y/5Y/10Y/30Y carry a `tenor`; ECB Consumer Expectations Survey series (`tenor` `12m` or `5y`) use `survey_expectation` and are not market-implied. All are monthly. `GET /v1/inflation` returns the latest value for every covered country. ## GET /v1/bond-yields/{country} **Government bond yields, one country per request.** Filter with `tenor=2y|5y|10y|30y` and `basis=nominal|real`. US Treasury nominal 2Y/5Y/10Y/30Y and real 5Y/10Y/30Y and ECB AAA euro-area nominal 2Y/5Y/10Y/30Y yields are `monthly_average` values; BoE gilt nominal and real zero-coupon spot yields at 2Y/5Y/10Y/30Y preserve official month-end archive dates and use `month_end`. `GET /v1/bond-yields` returns the latest value for every covered country. ### Parameters This table applies to all three endpoints: policy rates, inflation and bond yields. | Name | Type | Description | |---|---|---| | country | path | ISO 3166-1 alpha-2 code or `EA`, case-insensitive; `uk` and `eu` are aliases. `US`, `GB` and `EA` are covered; any other valid code returns HTTP 404 `no_data`, an unknown code HTTP 400 `invalid_country`. | | start | date | First inclusive date (`YYYY-MM-DD`). Omit start and end for the latest observation of each series. The collection routes ignore `start` and `end`. | | end | date | Last inclusive date. Requires `start`. | | tenor, basis | string | Inflation (`basis`) and bond yields (`tenor`, `basis`) only; policy rates take no filters. Unknown values return HTTP 400 `invalid_series_filter`. They also apply to the collection routes. | | api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | ### Example ```bash 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" ``` ### Response ```json { "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" } ] } ] } ``` `GET /v1/policy-rates` returns the latest observation per series for every covered country (abridged: the full response also lists the US target range bounds and EA): ```json { "status": "success", "metric": "policy_rates", "countries": [ { "country": "US", "country_name": "United States", "series": [ { "id": "effective_federal_funds_rate", "label": "Effective federal funds rate", "unit": "percent", "frequency": "daily", "method": "latest", "source": { "id": "ny_fed", "series_id": "EFFR.percentRate", "attribution": "Federal Reserve Bank of New York" }, "observations": [{ "date": "2026-06-30", "value": "3.63" }] } ] }, { "country": "GB", "country_name": "United Kingdom", "series": [ { "id": "bank_rate", "label": "Bank Rate", "unit": "percent", "frequency": "daily", "method": "latest", "source": { "id": "boe", "series_id": "IUDBEDR", "attribution": "Bank of England" }, "observations": [{ "date": "2026-06-30", "value": "3.75" }] } ] } ] } ``` Values are decimal strings. Series without observations in the range are left out, and a request with none returns HTTP 404 `no_data`. Collection responses list `countries: [{ country, country_name, series }]` in the order US, GB, EA and count as one quota-consuming request. The API reads the database cache; protected cron collection is the only upstream caller. 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 curves are direct BoE nominal and real zero-coupon spot values plus BoE-derived RPI-linked implied inflation values from month-end archives; source dates are preserved, including final business days. Neither UK curve values nor UK Bank Rate/CPI are averaged. Sources: [Federal Reserve Bank of New York reference rates](https://www.newyorkfed.org/markets/reference-rates/effr), [U.S. Department of the Treasury Daily Interest Rate XML Feed](https://home.treasury.gov/resource-center/data-chart-center/interest-rates/pages/xml), [U.S. Bureau of Labor Statistics Public Data API](https://api.bls.gov/publicAPI/v1/timeseries/data/), [Bank of England Database](https://www.bankofengland.co.uk/boeapps/database/_iadb-fromshowcolumns.asp), [BoE yield curve archives](https://www.bankofengland.co.uk/statistics/yield-curves), and [Office for National Statistics D7G7 CPI series](https://www.ons.gov.uk/economy/inflationandpriceindices/timeseries/d7g7/mm23/data). Reuse and attribution follow the linked source terms and the [Open Government Licence](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). BLS disclaimer: *BLS.gov cannot vouch for the data or analyses derived from these data after retrieval.* ### Euro-area series Use the `EA` country code (alias `eu`). Euro-area inflation includes metadata-verified all-items HICP year-over-year inflation as `ecb_hicp_yoy` plus ECB Consumer Expectations Survey weighted-median 12-month and five-year expectations. CES observations use `survey_expectation` and are not market-implied. The HICP series is `HICP.M.U2.N.000000.4D0.ANR`: monthly, euro area changing composition (`U2`), neither seasonally nor working-day adjusted (`N`), all-items (`000000`), Eurostat (`4D0`), annual rate of change (`ANR`). ECB AAA yields use `YC.B.U2.EUR.4F.G_N_A.SV_C_YM` with `SR_2Y`, `SR_5Y`, `SR_10Y`, and `SR_30Y` series. The API reads the database cache; protected cron collection is the only upstream caller. Sources: [European Central Bank Data API](https://data-api.ecb.europa.eu/service/data) and the [ECB Data Portal](https://data.ecb.europa.eu/). European Central Bank statistical information is freely available for reuse when accurately reproduced and cited under the [ECB policy on reuse of ESCB statistics](https://www.ecb.europa.eu/stats/ecb_statistics/governance_and_quality_framework/html/usage_policy.en.html); ECB source conditions and disclaimers apply. ECB yields are arithmetic averages of valid observations from complete UTC calendar months. ## Monetary metrics endpoints Use GET /v1/money-supply/{country} or GET /v1/central-bank-balance-sheet/{country} for one country, or omit {country} for the latest registered series across countries. Country routes accept inclusive start and end dates; end requires start. Omit both for the latest observation per series. Valid countries without registered data return HTTP 404 no_data. These endpoints read the database cache; protected cron collection is the only upstream caller. Monetary values are exact decimal strings. Each series includes its upstream source 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. For 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 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. Bank of Canada total assets use weekly Wednesday vector V36610, also converted exactly from millions of dollars to CAD. Valet value 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. Third-party M2++ components are excluded. The underlying source data is available on the [Bank of Canada website](https://www.bankofcanada.ca/valet/) free of charge. Attribute the Bank as the source and indicate changes without implying endorsement. See the [Bank of Canada terms](https://www.bankofcanada.ca/terms/); third-party content is excluded unless separately licensed. Prospective purchasers should be told before purchase that this source data is available free of charge. Australian money supply comprises the RBA's original, not-seasonally-adjusted monthly D3 levels: au_money_supply_m1 (DMAM1N, from 1975-02-28), au_money_supply_m3 (DMAM3N, from 1959-07-31), and au_money_supply_broad_money (DMABMN, from 1976-08-31). Values are stored in base AUD from native AUD billions using the exact factor 1000000000. These levels are revised and not break-adjusted; Bullion API does not derive growth rates. The A1 weekly au_central_bank_total_assets series (ARBAATAW, from 2013-07-03) is a point-in-time observation dated on the source's actual Wednesday and stored in base AUD from AUD millions using the exact factor 1000000. ARBALTLEW is total liabilities and equity and is not represented as liabilities. Australian attribution is “Source: Reserve Bank of Australia [year]”; the API supplies the year of the read. RBA financial data is governed by section 5 of the RBA Copyright and Disclaimer Notice, not CC BY 4.0. The underlying data is published by the RBA free of charge. Paid Bullion API plans charge for API access, caching, normalization and delivery, not for the underlying RBA data. The RBA does not endorse Bullion API or products or uses based on these data. See https://www.rba.gov.au/copyright/. Example: GET /v1/money-supply/ca?start=2026-07-01&end=2026-07-31. The Canada response below is a static, not live, example 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](https://www.bankofcanada.ca/valet/observations/V41552786/json?start_date=2026-07-01&end_date=2026-07-01). The response includes source metadata and exact decimal-string observations. ```json { "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." } ] } ] } ``` ## Government finance endpoints `GET /v1/government-debt/{country}`, `GET /v1/government-borrowing/{country}`, `GET /v1/debt-interest/{country}`, and the collections `GET /v1/government-debt`, `GET /v1/government-borrowing`, and `GET /v1/debt-interest`. **Government debt, borrowing, and debt interest, one endpoint per metric.** `/v1/government-debt` (metric `government_debt`), `/v1/government-borrowing` (`government_borrowing`), and `/v1/debt-interest` (`debt_interest`) return the shared country-series envelope. Per-metric path coverage is generated from registered, servable government-finance series: 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`. These are request path codes; aliases in parentheses resolve to the same geography, and `EA` is the euro-area aggregate rather than an ISO country. Coverage describes registered metric capability, not a guarantee of current stored observations. The bare path returns the latest observation of every series for each covered geography, in registry order, as one quota-counted request. An unknown country code returns HTTP 400 `invalid_country`; any other valid country, an unsupported category, or a range with no observations, returns HTTP 404 `no_data`. Every plan can call these endpoints. They are database-only readers. ### Parameters | Name | Type | Description | |---|---|---| | country | path | Case-insensitive path code; 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`. Responses echo the canonical upper-case code. | | start | YYYY-MM-DD | Optional inclusive first period end. Ranges may span at most 10 calendar years; longer ranges return 400 `invalid_range`. 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. | | api_key | string | Fallback credential for clients that cannot set the X-API-Key header. Prefer the header transport whenever possible. | Each returned government-finance series includes `coverage.earliest` and `coverage.latest`: the earliest and latest usable stored observation period ends, independent of the requested range. Coverage excludes withdrawn or unusable observations and does not imply that every intervening period is available. Each series has `id`, `label`, `scope` (government scope such as `central_government`), `basis` (for example `gross_debt` or `surplus_or_deficit`), `unit` (for example `usd_millions`, `aud_millions`, or `percent_of_gdp`), `frequency` (`monthly`, `quarterly`, or `annual`), `period_type` (`point_in_time`, `month`, `quarter`, or `fiscal_year`), `method` (`reported`, `derived_ratio`, or `derived_cumulative`), a plain-language `definition` with its sign convention, `derivation` (the `formula` and `inputs`, each with `source_id`, `source_series_id`, and `description`; only on derived series), and `source` (`id`, upstream `series_id`, and `attribution`; a derived series is attributed as a Bullion API calculation naming every input source). Unlike measures are never renamed into a common definition; compare series only when scope and basis match. Each observation has a decimal-string `value` and `date` (the period end); flows such as borrowing and interest add `period_start`. Where published, observations also include source `status` (`actual`, `provisional`, or `revised`). Forecasts, projections, and editorial classifications are never returned. United States series (U.S. Treasury Fiscal Service data distributed by FRED, millions of U.S. dollars, central-government scope): `treasury_total_public_debt_outstanding` (FRED `GFDEBTN`) and `treasury_debt_held_by_the_public` (`FYGFDPUN`) at the end of each calendar quarter; `treasury_surplus_or_deficit_monthly` from the Monthly Treasury Statement (`MTSDS133FMS`), where a deficit is negative; `treasury_surplus_or_deficit_fiscal_year_to_date` (method `derived_cumulative`), a Bullion API sum of the monthly values from 1 October, the federal fiscal-year start; and `treasury_federal_debt_to_gdp`, a quarterly Bullion API ratio (`derived_ratio`, unit `percent_of_gdp`) of total public debt outstanding at quarter end to BEA nominal GDP (NIPA Table 1.1.5 series `A191RC`, seasonally adjusted annual rate), with both inputs listed in `derivation`. United Kingdom series (ONS Public sector finances, dataset `PUSF`, current prices, not seasonally adjusted): `ons_public_sector_net_debt` (series `KSE6`, public sector net debt excluding public sector banks at month end, millions of pounds, `public_sector` scope); `ons_net_debt_to_gdp` (`HF6X`, the same measure as a percentage of GDP, reported by ONS with a denominator of GDP for the 12 months centred on the month, which includes projected GDP, so recent ratios are revised as outturn GDP arrives); `ons_net_borrowing_monthly` (`DZLS`, public sector net borrowing excluding public sector banks for the calendar month, millions of pounds, positive for borrowing and negative for a surplus); and `ons_central_government_debt_interest` (`NMFX`, central government debt interest payable for the calendar month, millions of pounds, `central_government` scope, including the capital uplift on index-linked gilts). `ons_net_borrowing_fiscal_year_to_date` (method `derived_cumulative`, period from 1 April to the month end) is a Bullion API calculation, not an ONS-published series: the exact sum of `DZLS` from 1 April, the UK fiscal-year start, produced only when every month since April is published. ONS revises earlier months in each monthly release, and a revised month flows into every later cumulative value. Euro-area series (`EA`; Eurostat, general-government scope, sector `S13`, euro-area aggregate `EA21`: the 21 member states in the euro area from 2026, back-calculated for earlier periods): `eurostat_general_government_gross_debt_annual` and `eurostat_gross_debt_to_gdp_annual` (consolidated gross debt `GD` at 31 December, `eur_millions` and Eurostat-published `percent_of_gdp`, `point_in_time`), `eurostat_general_government_net_lending_annual` (net lending (+) or net borrowing (-), `B9`) and `eurostat_interest_expenditure_annual` (interest expenditure, `D41PAY`) for the calendar year as `fiscal_year`, all from `gov_10dd_edpt1`; `eurostat_general_government_gross_debt_quarterly` and `eurostat_gross_debt_to_gdp_quarterly` at the quarter end from `gov_10q_ggdebt`; and `eurostat_general_government_net_lending_quarterly` and `eurostat_interest_expenditure_quarterly` for the calendar quarter, not seasonally adjusted, from `gov_10q_ggnfa`. A negative net lending value is net borrowing. Ratios are published by Eurostat, not calculated by Bullion API. Recent quarterly values are Eurostat provisional figures and may be revised; forecast-flagged values are never stored. Peru series (`PE`, BCRPData, non-financial-public-sector scope): `peru_nonfinancial_public_sector_debt` (series `PN03371FQ`, quarter-end debt balance in millions of Peruvian soles) and `peru_nonfinancial_public_sector_debt_to_gdp` (series `PN03432FQ`, BCRPData-published percentage of PBI for that balance). Both are reported quarterly observations; Bullion API does not calculate the ratio or infer its GDP-denominator method. BCRPData metadata credits the Ministry of Economy and Finance, Banco de la Nación, Ministry of Transportation and Communications, ONP and Cofide as source institutions. BCRPData terms permit total or partial reproduction with source attribution, do not state a non-commercial-only restriction, and are not a Creative Commons licence; responsibility for externally sourced statistics remains with the original provider. Its keyless API is refreshed daily, with recent revisions re-collected for 12 months. Peru borrowing and debt interest are not covered. Singapore (`SG`) serves annual central-government `singstat_total_government_debt_annual` (SingStat `M130071`, `sgd_millions`, 31 December) and `singstat_government_debt_to_gdp_annual` (Bullion API `derived_ratio` using same-calendar-year nominal GDP, `M015731`, multiplied by 100). Includes non-publicly held instruments, advance deposits and non-spending-purpose borrowing whose proceeds are invested and cannot be spent, not net or consolidated general-government debt. No Singapore borrowing, interest or quarterly feeds are served. Both official data.gov.sg catalogues assign Singapore Open Data Licence v1.0 (https://data.gov.sg/open-data-licence): commercial distribution with source acknowledgement and licence link, no endorsement, limited application/website sublicensing and no rights for downstream sub-licensees. Customers needing additional rights must obtain the original licence directly. Each series' attribution links the licence and both original catalogues. GDP historical estimates may be revised; no forecasts are emitted. EU member states (`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`) serve the same eight Eurostat series ids with the same definitions, general-government scope and units, for that country instead of the aggregate; Greece is `GR` although Eurostat publishes it as `EL`. Iceland series (`IS`; Statistics Iceland, annual general-government figures): `statice_gross_debt_annual` and `statice_gross_debt_to_gdp_annual` from THJ05181 at 31 December; `statice_financial_balance_annual` (surplus positive, deficit negative) and `statice_interest_payments_annual` from THJ05121 for the calendar year. Millions of ISK at current prices or reported percent of GDP. Debt includes pension/insurance liabilities and payables, **not Maastricht debt**. Full PX decimal precision is preserved, not rounded CSV/JSON display precision. Source: Statistics Iceland, THJ05181 and THJ05121, under [CC BY 4.0](https://www.statice.is/publications/open-data-access/); modifications are not attributed to Statistics Iceland. Norway series (`NO`; Statistics Norway (SSB) StatBank, national-accounts general-government scope, `nok_millions`): `ssb_general_government_gross_debt_annual` (table `08753`, at 31 December) and `ssb_general_government_gross_debt_quarterly` (table `11559`, at the quarter end), both `point_in_time`; `ssb_general_government_net_lending_annual` (table `14668`, `fiscal_year`) and `ssb_general_government_net_lending_quarterly` (table `14667`, not seasonally adjusted, `quarter`), where a negative value is net borrowing; and `ssb_interest_expenditure_annual` (table `14668`, item `B41`, calendar year), which is annual only because Statistics Norway publishes no quarterly interest item. Statistics Norway publishes no debt-to-GDP ratio, so `ssb_gross_debt_to_gdp_annual` and `ssb_gross_debt_to_gdp_quarterly` (`percent_of_gdp`) are Bullion API calculations (`derived_ratio`, formula and inputs in `derivation`) of that debt to GDP at current market prices including petroleum (tables `09189` and `09190`; quarterly debt over the sum of the last four quarters of GDP). Qatar (`QA`) is also covered by `/v1/government-debt` only: `qatar_public_debt_annual` is national central-government debt at 31 December, in millions of Qatari riyals, from Ministry of Finance dataset `MOF-OD-C0-00004`, through 2025. This is not general-government or Maastricht debt. `qatar_gross_debt_to_gdp_annual` is a Bullion API `derived_ratio` of the debt over the same-year nominal GDP total in National Planning Council dataset `NPC-OD-C0-01288`; matched-year ratios are served only through 2024, with no inferred 2025 GDP or ratio. Both datasets identify [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), and the ratio is attributed as a Bullion API calculation. Catalogue metadata observed 2026-09-30 reported the generic `data-trust-index.ready-for-publication=false` flag; it is not a publisher certification, data withdrawal or deprecation. The government-debt collection appends `QA` after `CH`; Qatar has no borrowing or interest series. Canada series (`CA`; Statistics Canada, national-accounts general-government scope, all quarterly, `cad_millions` or `percent_of_gdp`): `statcan_general_government_gross_debt_quarterly` (table `36-10-0467-01`) and `statcan_gross_debt_to_gdp_quarterly` (table `38-10-0237-01`, a ratio published by Statistics Canada, not calculated by Bullion API; its gross debt is defined more widely than the debt series), both `point_in_time` at the quarter end; `statcan_general_government_net_lending_quarterly` (net lending (+) or net borrowing (-)) and `statcan_interest_expenditure_quarterly` (interest on debt), both from table `36-10-0477-01`, unadjusted, `quarter`. Recent quarters are revised. Switzerland series (`CH`; Swiss Federal Finance Administration, IMF GFSM 2014 model, whole-state general government, annual accounts years only, `chf_millions` or `percent_of_gdp`): `efv_general_government_gross_debt_annual` (gross debt excluding financial derivatives, IMF definition, variable `fremdkapital`) and `efv_general_government_maastricht_style_debt_annual` (debt in line with the Maastricht definition, variable `maastricht_schuld`, not verified as comparable with Eurostat), both `point_in_time` at 31 December; `efv_maastricht_style_debt_to_gdp_annual` (the ratio the Administration publishes for that debt, in percent, GDP as used by the Administration); and `efv_general_government_net_lending_annual` (net lending (+) or net borrowing (-), variable `saldo`, `fiscal_year`). Forecast years are never returned and accounts years can be revised. There is no Swiss interest series. Japan series (`JP`; Cabinet Office Economic and Social Research Institute, consolidated general-government scope, quarterly): `cabinet_jp_general_government_gross_debt_quarterly` is face-value gross debt in millions of yen from row 6 of the `GGD` workbook, including SDRs, currency and deposits, debt securities, loans and payables and excluding unfunded pay-as-you-go pension entitlements and certain Fiscal Investment and Loan Program liabilities. `cabinet_jp_general_government_gross_debt_to_gdp_quarterly` is a Bullion API `derived_ratio`: quarter-end debt divided by nominal current-price GDP (Expenditure Approach, original series) summed over the same and three preceding quarters. The ratio is not published by the Cabinet Office; GDP is not seasonally adjusted or annualised. The [Cabinet Office notice](https://www.cao.go.jp/en/notice-e.html) says numerical data and simple tables are not protected by copyright and may be used freely; the website Terms of Use govern website content and do not apply to those data. Source attribution and disclosure of Bullion API's calculation are Bullion API policy, not conditions imposed on those numerical data. This is not a claim of rights to separate third-party copyrighted material, and the Cabinet Office does not endorse this product. The provider discovers the official GDP CSV link from the release page but reads financial values only from the official workbook and CSV. It uses no API key or third-party IMF/e-Stat data. Australia series (`AU`; ABS, general-government scope): `abs_general_government_gross_debt_annual` is gross L2 debt at 30 June from Government Finance Statistics, Annual Table 14 (row `Equals L2`, column `All Australia general government`), in `aud_millions`, valued at current market value using the ABS creditor approach. `abs_general_government_gross_debt_to_gdp_annual` is a Bullion API calculation, not an ABS-published ratio: Table 14 gross L2 debt divided by the sum of the matching four original, current-price quarterly GDP observations from ABS SDMX flow `ANA_AGG` version `1.0.0`, key `M3.GPM.10.AUS.Q`, stored to eight decimal places. It is not the net L2 ratio in Table 16. Source: Australian Bureau of Statistics, [Government Finance Statistics, Annual](https://www.abs.gov.au/statistics/economy/government/government-finance-statistics-annual) and [Australian National Accounts](https://www.abs.gov.au/statistics/economy/national-accounts/australian-national-accounts-national-income-expenditure-and-product). CC BY 4.0 permits commercial reuse of ABS website material, subject to the exclusions and third-party rights identified in the [ABS copyright terms](https://www.abs.gov.au/website-privacy-copyright-and-disclaimer#copyright-and-creative-commons). Those terms require attribution to the ABS for material obtained from its website; do not imply ABS endorsement. The ABS Data API is Beta and may change or be unavailable. ### Example ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/government-debt/us" curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/government-debt/pe" curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/government-borrowing/uk?start=2026-01-01&end=2026-08-31" curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/debt-interest" ``` United States debt interest is not currently available: `/v1/debt-interest/us` returns HTTP 404 `no_data` and the bare path omits the US. Peru data: Source: [Banco Central de Reserva del Perú BCRPData](https://estadisticas.bcrp.gob.pe/estadisticas/series/ayuda/condiciones-de-uso); reproduction is permitted with source attribution under its terms, which are not a Creative Commons licence. BCRPData metadata names the original statistical source institutions in the Peru series definitions. Sources: U.S. Department of the Treasury, Bureau of the Fiscal Service data via [FRED](https://fred.stlouisfed.org/), 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 debt-to-GDP ratio: [U.S. Bureau of Economic Analysis Data API](https://apps.bea.gov/api/data). This product uses the Bureau of Economic Analysis (BEA) Data API but is not endorsed or certified by BEA. United Kingdom data: Source: Office for National Statistics, [Public Sector Finances](https://www.ons.gov.uk/economy/governmentpublicsectorandtaxes/publicsectorfinance/datasets/publicsectorfinances). Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). Euro-area data: Source: Eurostat, [government finance statistics](https://ec.europa.eu/eurostat/web/government-finance-statistics) (datasets `gov_10dd_edpt1`, `gov_10q_ggdebt`, `gov_10q_ggnfa`); reuse is authorised provided the source is acknowledged. Norway data: Source: Statistics Norway (SSB), www.ssb.no, licensed under [CC BY 4.0](https://creativecommons.org/licenses/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](https://www.statcan.gc.ca/en/terms-conditions/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](https://opendata.swiss/en/dataset/hauptaggregate-und-prognosen-im-fs-und-gfs-modell), published as open government data under the opendata.swiss [Open use](https://opendata.swiss/en/terms-of-use) terms (commercial use permitted, source recommended); provided without verification of the derived series and not endorsed by the Federal Finance Administration. ## Market-indicator endpoints `GET /v1/gold/mine-production`, `GET /v1/gold/recycling`, `GET /v1/gold/trade`, `GET /v1/silver/mine-production`, `GET /v1/silver/recycling`, `GET /v1/silver/consumption`, and `GET /v1/silver/trade`. **Annual gold and silver statistics from the U.S. Geological Survey (USGS), one endpoint per metal and indicator.** Every plan can call these endpoints; each request needs an API key and consumes monthly quota. They read Bullion API's database only and never call USGS. Values are decimal strings in metric tonnes of metal content, one observation per calendar year, dated 31 December with `period_start` 1 January. | Endpoint | Series | |---|---| | `/v1/gold/mine-production` | `world-mine-production`, `us-mine-production` | | `/v1/gold/recycling` | `us-secondary-production` | | `/v1/gold/trade` | `us-imports`, `us-exports` | | `/v1/silver/mine-production` | `world-mine-production`, `us-mine-production` | | `/v1/silver/recycling` | `us-secondary-production` | | `/v1/silver/consumption` | `us-apparent-consumption` | | `/v1/silver/trade` | `us-imports`, `us-exports` | World mine production is the only global measure; recycling (secondary production), consumption and trade are United States figures, and each series states its `geography` (`world` or `US`). There is no gold consumption endpoint, because the USGS gold consumption figures come from third-party sources rather than USGS. These are partial market indicators, not total demand, a market balance or a forecast. The annual statistics have a multi-year publication lag. When USGS identifies an observation as estimated, it carries an `estimated` status; historical observations without a reported estimate status omit that field. ### Parameters | Name | Type | Description | |---|---|---| | start / end | YYYY-MM-DD | Optional inclusive dates. Omit both for each series' latest year; with a range, every year whose 31 December falls inside it is returned in chronological order. `start` alone runs to today; `end` without `start` returns `missing_params`. | Each series carries `id`, `label`, `unit` (`metric_tonnes`), `frequency` (`annual`), `method` (`reported`), `geography`, `category`, a plain-language `definition` of what the series measures and its limits, `collected_at` (when Bullion API last collected the source), `freshness` and a `source` object with its attribution and notice. `freshness` is `stale` when scheduled collection is overdue; the last collected data are still served. It does not describe the multi-year USGS publication lag. Years USGS marks as not available are omitted, never returned as zero. A malformed date returns `invalid_date`, a reversed range `invalid_range`, and a request with no stored observation `no_data`. ### Example ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/gold/mine-production" curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/silver/consumption?start=2012-01-01&end=2021-12-31" ``` ### Response ```json { "status": "success", "metric": "gold-mine-production", "metal": "gold", "start": null, "end": null, "series": [{ "id": "world-mine-production", "label": "World gold mine production", "unit": "metric_tonnes", "frequency": "annual", "method": "reported", "geography": "world", "category": "supply", "definition": "Gold content of worldwide mine production in the calendar year, in metric tonnes. …", "collected_at": "2026-10-07T07:55:04.000Z", "freshness": "current", "source": { "attribution": "Source: U.S. Geological Survey.", "notice": "U.S. Geological Survey data are in the U.S. public domain. Credit: U.S. Geological Survey. The USGS does not endorse this product. …" }, "observations": [{ "date": "2022-12-31", "value": "3000", "period_start": "2022-01-01" }] }] } ``` Illustrative values only; the example value is not a USGS figure. USGS data are in the U.S. public domain. Credit the U.S. Geological Survey when you display them; the USGS does not endorse this product. ## GET /v1/central-bank-reserves/{country} **Annual central-bank reserve values and gold share.** This endpoint is available on every plan and is served from Bullion API's automated database cache. The bare `/v1/central-bank-reserves` returns the latest observation of each series for every covered country, ordered by code. ### 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 of each series. | | end | YYYY-MM-DD | Optional last inclusive date; requires start. Annual observations are dated at year end. | 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 with exact decimal arithmetic 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`. An unknown code returns `400 invalid_country`; a country without observations in range returns `404 no_data`. ### Example ```bash 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 ```json { "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" } ] } ] } ``` 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; coverage is per dataset, and 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. Redistribution of Bullion API price data or API access remains subject to the Terms of Service. ## GET /v1/countries **Which datasets each country has.** `/v1/countries` lists every country (ISO 3166-1 alpha-2, plus `EA` for the euro area) with at least one dataset, sorted by code; `/v1/countries/{country}` returns one. Each dataset names its metric and the path to request it. Coverage reflects the series Bullion API serves, so a dataset is listed only where its endpoint can answer. `aliases` lists accepted path aliases (`uk`, `eu`). An unknown code returns `400 invalid_country`; a valid code with no dataset returns `404 no_data`. ```bash curl -H "X-API-Key: bullion_your_key" "https://api.bullionapi.dev/v1/countries/uk" ``` Abridged response; the full `datasets` list includes every metric served for the country: ```json { "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" } ] } ``` ## GET /v1/central-bank-gold **Central-bank gold holdings.** Returns cached reported gold holdings by country on every plan; API requests never call upstream providers. `GET /v1/central-bank-gold/{country}` returns one country's series in the shared single-country envelope (`metric: central_bank_gold`), and `GET /v1/central-bank-gold` returns the latest observation of each series for every covered country, ordered by country code (sort client-side for leaderboards). 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. | Name | Type | Description | |---|---|---| | country | path, ISO2 | Case-insensitive country code; `uk` is accepted for GB. Unknown codes return `invalid_country`; countries without coverage return `no_data`. | | start / end | YYYY-MM-DD | Optional inclusive dates, at most 10 years apart. Omit both for the latest observation of each series; `start` alone runs to today. | Each series is one measure at one cadence: `holdings_tonnes`, `holdings_million_fine_troy_oz`, `holdings_change_tonnes`, `reported_value_usd_millions`, `reported_value_change_usd_millions`, `reported_value_eur_millions`, `reported_value_pln_millions`, `reported_value_inr_crores`, `reported_value_sar_millions` or `reported_value_sgd_millions`. Series ids combine the measure and cadence, for example `holdings_tonnes_monthly` or `reported_value_usd_millions_weekly`, with a `_derived` suffix for derived holdings, 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 (true when its newest successful refresh is more than 45 days old, or when its newest observation is more than six months older than the country's newest observation in any series; stale observations stay visible), a `provenance` object and a `reuse_notice` covering the returned observations; a stock-change series also covers the prior observation used to calculate the change. A range with no data returns `no_data`. Reuse rights for public statistics remain separate from API plan terms and are governed by applicable terms. When weight is reported in fine troy ounces, the tonnes series label states that Bullion API converted the reported weight, while the original ounce series remains available. Quantities derived from a reported value are labelled separately and are never presented as reported weight. ```json { "status": "success", "metric": "central_bank_gold", "country": "DE", "country_name": "Germany", "start": null, "end": null, "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-06-30", "value": "1000.00" }] }] } ``` Illustrative synthetic values only; this example is not live data. `holdings_change_tonnes` is calculated by Bullion API as a change between consecutive reported stocks of one series, not purchases or sales, and is omitted where reported stocks are not directly comparable. `holdings_basis` is `reported` when the quantity originates in a reported physical weight and `derived` when tonnes are computed from a reported USD value and month-end gold price; a derived quantity keeps its reported value as a separate reported series. Value-only measures (`reported_value_*`) carry no quantity series rather than invented tonnes. Some valuations use a fixed official price rather than market prices, and a reported zero is valid data, not a missing value. `reported_value_sgd_millions` is a combined gold and foreign-exchange reserve value, not a gold-only figure. Changes between observations describe changes in reported stock, valuation and revisions, not purchases or sales; a flat reported stock does not establish that no transactions occurred. ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/central-bank-gold/de?start=2026-01-01&end=2026-06-30" curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/central-bank-gold" ``` ## GET and PUT /v1/portfolio Configure and read one private multi-metal portfolio for the API-key owner's account. Portfolio endpoints use X-API-Key only; the api_key query parameter is not accepted. Existing and newly created keys have no portfolio access by default. The account owner selects one permission in the API keys dashboard: - None: no portfolio access. - Read: stored as `portfolio: read` and permits GET. - Read and write: stored as `portfolio: read, write` and permits GET and PUT. 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. PUT accepts one supported reporting currency while the portfolio ledger is empty: ```json { "reporting_currency": "GBP" } ``` GET returns settings and an exact summary. The empty-portfolio example: ```json { "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 values remain strings, including zero. The summary includes a metals array with separate quantities, cost basis, realised gains and valuation for each recorded metal. The legacy gold_quantity_grams field contains only gold; it never combines different metals. Mixed-metal aggregate spot_price is null. Valuation uses only each matching latest stored metal spot price in the portfolio reporting currency and never calls an upstream provider or bridges currencies. `status` is `current` when that stored price is at most six hours old, `stale` when it is older, and `unavailable` when no matching-currency price exists; unavailable valuation returns `null` for `spot_price`, `current_value`, `unrealised_pnl`, and `price_timestamp` instead of zeros, while holdings and cost basis stay available. Open troy ounces multiplied by the stored spot price give `current_value`; subtracting the remaining cost basis gives `unrealised_pnl`. Missing settings return `portfolio_not_found`; an unscoped or read-only key used for a forbidden action returns `insufficient_scope`. ## Multi-metal portfolio behaviour Each account keeps one portfolio and one reporting currency. Transaction metal values are gold, silver, palladium, copper and platinum; omitted metal defaults to gold for existing clients. Holdings are kept per metal, and FIFO or explicit sales consume only the matching metal's lots. Each metal is valued from its matching cached reporting-currency observation without provider calls or cross-currency fallback. ## Price alerts The authenticated dashboard supports multiple USD target alerts per supported metal. Each target fires independently once. Owners can edit targets or delete individual alerts; an identical active target for the same metal is rejected. Editing an inactive alert uses an explicit Save and reactivate action. Evaluation receives a matching fresh persisted native-metal observation only after the complete hourly refresh succeeds; stale, omitted, or partially persisted data does not trigger delivery. Emails identify the selected metal. Changes and unsubscribe affect only the selected alert, including when the owner has other targets for the same metal. ## POST /v1/portfolio/transactions Record an owned purchase or sale with an API key holding portfolio write access. Send an opaque client-generated `Idempotency-Key` header on every request. Send `X-Portfolio-Transaction-Type: sale` for a sale. The same key and canonical request returns the original transaction; reusing a key with changed input returns `409 idempotency_conflict`. Raw keys are never stored or logged. Quantity accepts at most eight fractional digits in `gram`/`g`, `kilogram`/`kg`, or `troy_oz`/`toz`, then returns normalized grams at scale 15. Unit price, delivery, fees, gross cost, total cost, and cost per gram are decimal strings at scale 8, using round-half-up. Transaction dates must be real UTC calendar dates not in the future. Forms are `bar`, `coin`, or `other`; CGT status is `exempt`, `not_exempt`, or `unknown`. `missing_idempotency_key`, `invalid_purchase`, and `invalid_sale` return 400. Sales consume eligible open purchase lots FIFO by transaction date, creation time, then ID by default; sale dates cannot be future-dated or precede allocated purchases. `insufficient_holdings` returns 409 and rolls back the entire mutation. Net proceeds, disposed cost, and realised P&L are exact decimal strings; idempotent replay never reallocates a completed sale. Sales can instead opt into explicit allocation by sending `allocations`: an array of `{ "lot_id", "quantity_grams" }` objects whose quantities must sum exactly to the sale quantity. List the account's open lots with GET /v1/portfolio/lots. Every selected lot must belong to the account, match the sale's metal, form and CGT treatment, predate or equal the sale date, and have sufficient remaining quantity. A missing, foreign, duplicated, incompatible, over-allocated, or under-allocated lot fails the whole sale without mutation: `invalid_allocation` returns 400 for eligibility and exact-sum violations, `sale_date_invalid` for lots dated after the sale, and `insufficient_holdings` when a selected lot no longer has enough remaining quantity. Committed sales identify their `allocation_method` as `fifo` or `explicit` and return the committed allocations ordered by purchase date, creation time, then ID. ## GET /v1/portfolio/lots List only the authenticated account owner's open purchase lots with a positive remaining quantity, ordered by `transaction_date, created_at, id`. Each lot exposes its stable `lot_id`, originating `purchase_transaction_id`, purchase date, form, CGT status, remaining quantity in grams, and remaining cost basis as decimal strings (`remaining_quantity_grams` at scale 15, `remaining_cost_basis` at scale 8). Use these identifiers to build explicit sale allocations; closed lots and other accounts' lots are never returned. ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/portfolio/lots" ``` ```json { "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" }] } ``` ## GET /v1/portfolio/transactions Browse only the authenticated account owner's portfolio transactions. Results are ordered by `transaction_date DESC, created_at DESC, id DESC` and use keyset pagination. The default page size is 50 and the maximum is 100. The returned `next_cursor` is an opaque cursor; send it as `cursor` to fetch the next page and do not depend on its internal format. All quantities and monetary values remain decimal strings. Filter the page with `type` (`purchase` or `sale`), `form` (`bar`, `coin`, or `other`), and `cgt_status` (`exempt`, `not_exempt`, or `unknown`). Restrict the period with either `calendar_year=2025` for 1 January through 31 December inclusive, or `uk_tax_year=2025-26` for 6 April through 5 April inclusive, where the two years in the label must be consecutive. The two period filters are mutually exclusive: combining them returns 400 `conflicting_periods`. Empty query values are ignored. Malformed labels return 400 `invalid_calendar_year` or `invalid_tax_year`; unknown attribute values return 400 `invalid_filter`. Filters compose with `cursor` and `limit` so a filtered view paginates stably. ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/portfolio/transactions?limit=50&uk_tax_year=2025-26" ``` ```json { "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": [] }], "next_cursor": null } ``` ## GET /v1/portfolio/transactions/{id} Return one owned transaction using the same representation as the list endpoint. Missing, malformed, and cross-account identifiers return 404 `transaction_not_found` rather than revealing another account's data. Invalid page, filter, or period parameters return 400 `invalid_limit`, `invalid_cursor`, `invalid_filter`, `invalid_calendar_year`, `invalid_tax_year`, or `conflicting_periods`. ## DELETE /v1/portfolio/transactions/{id} Delete one owned transaction with a portfolio write key. Send an `If-Match` header carrying the ETag-style quoted `version` from the transaction payload (`"1"`; the weak form `W/"1"` is also accepted). A missing or mismatched version returns 412 `stale_transaction` without mutation. Deleting a sale restores every allocated quantity and cost basis to its originating purchase lot and rebalances holdings. A purchase that has funded a sale cannot be deleted: it returns 409 `transaction_in_use`, so delete the dependent sale first. Missing, malformed, and cross-account identifiers return 404 `transaction_not_found`. ```bash 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"' ``` ```json { "status": "success", "deleted_transaction_id": "11111111-1111-4111-8111-111111111111" } ``` ## GET /v1/portfolio/gains Summarise realised profit or loss from owned sale transactions inside one period, grouped by the CGT status recorded on each sale. Supply exactly one period filter: `calendar_year=2025` covers 1 January through 31 December inclusive, and `uk_tax_year=2025-26` covers 6 April through 5 April inclusive with consecutive years in the label. The response echoes the resolved `period` (`kind`, `label`, inclusive `start` and `end` dates) and returns `buckets.exempt`, `buckets.not_exempt`, and `buckets.unknown` plus their `total`, all exact 8-place decimal strings. Omitting both filters returns 400 `missing_period`; supplying both returns 400 `conflicting_periods`; malformed labels return 400 `invalid_calendar_year` or `invalid_tax_year`. An account without a portfolio returns 404 `portfolio_not_found`. These figures exclude annual allowances, matching and pooling rules, and tax rates. They are not tax advice. ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/portfolio/gains?uk_tax_year=2025-26" ``` ```bash curl -H "X-API-Key: bullion_your_key" \ "https://api.bullionapi.dev/v1/portfolio/gains?calendar_year=2025" ``` ```json { "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" }] }``` ## DELETE /v1/portfolio/transactions/{id} Delete one owned transaction with a portfolio write key. Send an `If-Match` header carrying the ETag-style quoted `version` from the transaction payload (`"1"`; the weak form `W/"1"` is also accepted). A missing or mismatched version returns 412 `stale_transaction` without mutation. Deleting a sale restores every allocated quantity and cost basis to its originating purchase lot and rebalances holdings. A purchase that has funded a sale cannot be deleted: it returns 409 `transaction_in_use`, so delete the dependent sale first. Missing, malformed, and cross-account identifiers return 404 `transaction_not_found`. ```bash 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"' ``` ```json { "status": "success", "deleted_transaction_id": "11111111-1111-4111-8111-111111111111" } ``` Invalid page parameters return 400 `invalid_limit` or `invalid_cursor`. Every response carries an `ETag` header quoting the transaction version. ## PATCH /v1/portfolio/transactions/{id} Edit one owned transaction with optimistic concurrency. Send `If-Match: ""` quoting the current version from the ETag header or the `version` field; wildcard and multi-tag forms are rejected. The body replaces every mutable field (`transaction_date`, `quantity`, `weight_unit`, `unit_price`, `delivery_cost`, `fees`, `form`, `cgt_status`, `description`); type and owner are immutable. Purchases must keep enough quantity and cost for every dependent sale allocation, cannot move after an allocating sale, and cannot change form or CGT treatment while sales depend on them. Sales release and rebuild only their own allocations with the persisted method: FIFO edits omit `allocations` (sending them is an error) while explicit edits must resend the full lot allocation list under the same eligibility and exact-sum rules as creation. Success increments `version`, returns the new ETag, and recalculates lot totals, disposed cost, and realised P&L immediately. Missing or stale preconditions return 412 `stale_transaction` with no mutation; foreign or malformed identifiers return 404 `transaction_not_found`; any failure rolls the whole edit back. ```bash 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":"31.1034768","weight_unit":"gram","unit_price":"80.80798407","delivery_cost":"12.34","fees":"1.005","form":"coin","cgt_status":"exempt"}' ``` ## Rate limits Authenticated successful market-data /v1 responses include these headers so you can track usage in real time. Portfolio responses omit these monthly quota headers because portfolio operations do not use the monthly market-data quota: X-API-CURRENT and X-API-QUOTA are additive compatibility aliases; the existing x-ratelimit-* names remain supported. | 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. | Each API key permits 60 requests per rolling 60-second window across REST. The next request returns HTTP 429 with code `rate_limited` and a `Retry-After` header. Short-window denials do not change the monthly usage counter. Monthly exhaustion is a separate HTTP 429 with code `quota_exceeded`. ## 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 and framework-owned responses keep their 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 HTTP/1.1 401 Unauthorized content-type: application/json { "status": "error", "error": { "code": "missing_api_key", "message": "Missing API key" } } ``` ### 401 — Conflicting credentials ```http 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 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" } } ``` ### 400 — Bad request ```http HTTP/1.1 400 Bad Request content-type: application/json { "status": "error", "error": { "code": "invalid_range", "message": "The requested range is invalid." } } ``` ### 400 — Invalid latest price unit ```http 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 | HTTP status | Description | |---|---:|---| | dependency_not_ready | 503 | Required data for this metric is not ready. | | 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 | ## GET /v1/symbols Returns the supported metal descriptions plus native currencies and configured projected currencies that can serve complete five-metal prices with FX provenance. It is authenticated and request-logged, but does not consume monthly product quota or call an upstream provider. The runtime response is authoritative for available currency choices; static documentation reflects configuration only. Supported metal slugs are `gold`, `silver`, `palladium`, `copper`, and `platinum`. All metal prices default to troy ounces. `/v1/gold/carat` is gold-only. ## Supported 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. `USD` is the default. 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 [Price methodology](#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](https://unirateapi.com/). --- # MCP server Source: https://bullionapi.dev/docs/mcp - Endpoint: `https://api.bullionapi.dev/mcp`, Streamable HTTP, OAuth 2.1 with PKCE. - Any agent that supports remote MCP servers with sign-in can connect. It registers itself as a public client the first time it connects; the user then signs in with their Bullion API account and approves the requested read-only scopes (`market:read`, `macro:read`, `portfolio:read`, and `offline_access` to stay connected). - Read-only: tools look up prices, derived metrics, country monetary and government-finance data and the signed-in user's portfolio; they never change anything. - Access tokens last five minutes and renew with single-use refresh tokens for up to 30 days; withdrawing approval cuts the connection off. - Market and macro calls use the same credits and plan limits as the REST API. Each connection is limited to 60 requests a minute. --- # Bullion API pricing One API. Predictable pricing. No overage fees. No surprise bills. Cancel any time on every plan. ## Plans ### Free — $0 Evaluate the API. No credit card. - 30 requests / month - Non-commercial use of the Bullion API Service only - Every endpoint and dataset - Available stored history ### Pro — $9 USD/mo Annual: $90/year ($7.50 effective monthly; $18 saving). Higher-volume evaluation and internal use. - 50,000 requests / month - Non-commercial use of the Bullion API Service only - Every endpoint and dataset - Available stored history ### Business — $29 USD/mo Annual: $290/year ($24.17 effective monthly; $58 saving). Commercial use of the Service in your own product or service. - 100,000 requests / month - Commercial use included; raw or substantially equivalent market-price data and API access cannot be redistributed or resold - Every endpoint and dataset - Available stored history Annual list prices do not establish checkout availability. New annual subscriptions are offered only when annual checkout is enabled; check the billing options shown on the pricing page. Request quotas still reset monthly, and existing annual subscriptions retain their billing interval. ## Compare | | Free | Pro | Business | |---|---|---|---| | Monthly requests | 30 | 50,000 | 100,000 | | Gold price data | Yes | Yes | Yes | | Gold/silver ratio | Yes | Yes | Yes | | Gold technical indicators | Yes | Yes | Yes | | Active currency catalogue | Yes | Yes | Yes | | Price history | Available stored history | Available stored history | Available stored history | | Price cache TTL | 1 hour | 1 hour | 1 hour | | Annual central-bank reserve values and gold share | Yes | Yes | Yes | | Monthly central-bank gold holdings | Yes | Yes | Yes | | Usage rights | Non-commercial use of the Bullion API Service only | Non-commercial use of the Bullion API Service only | Commercial use included; raw or substantially equivalent market-price data and API access cannot be redistributed or resold | ## Included on every plan - **No overage fees** — requests stop at your monthly limit. There are no surprise charges. - **Gold price data** — gold spot prices in the currencies listed by the live symbols catalogue. - **Derived gold/silver ratio** — calculate the latest or stored daily gold-to-silver ratio with `/v1/gold-silver-ratio`. - **Gold technical indicators** — request moving averages, realised volatility, and drawdown with `/v1/gold/moving-average`, `/v1/gold/volatility`, and `/v1/gold/drawdown`. - **Macro-derived USD gold** — `/v1/gold/real-price` rebases to the latest stored seasonally adjusted CPI index month; `/v1/gold/yield-correlation` calculates a 90-pair rolling correlation of daily gold returns and daily real-yield changes. Stored inputs are required; all plans have access. - **JSON responses** — predictable shape. No GraphQL, no SDK lock-in. - **Status page** — a public status page with live incident information. Prices in currencies other than USD, EUR and GBP are derived from the most recent accepted Bullion API daily FX snapshot, not a provider end-of-day reference rate. 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. Currency conversion input attribution: [Exchange Rates By UniRateAPI](https://unirateapi.com/). See the [price methodology](https://bullionapi.dev/docs#methodology). ## FAQ **What counts as one request?** Usage increments after API-key and quota checks but before endpoint validation, so invalid date-range requests count. Requests rejected for a missing or invalid key, or because the quota is already exhausted, do not. **What price history is available?** A price-history request may span up to five years, but this is a query limit, not a guarantee of five years or 365 days of observations. Available dates vary by metal and currency; missing weekdays are omitted. Non-gold history starts with retained refresh observations. Projected-currency history starts at activation and is not backfilled. History responses include stored weekday coverage dates and observation counts, separately from the requested start and end. **Are price alerts included on every plan?** Dataset access is the same on every plan. Price alerts require an eligible paid Pro or Business subscription and enabled alert delivery; dataset access does not establish alert eligibility. **What currency do you bill in?** All plans are billed in USD: Pro is $9 per month and Business is $29 per month. Annual list prices are $90/year for Pro ($7.50 effective monthly; $18 saving) and $290/year for Business ($24.17 effective monthly; $58 saving). Annual list prices do not establish checkout availability. New annual subscriptions are offered only when annual checkout is enabled; check the billing options shown on the pricing page. Request quotas still reset monthly, and existing annual subscriptions retain their billing interval. Pro subscribers can upgrade to Business from the billing dashboard; the switch is immediate and the charge is prorated. **Can I cancel any time?** Yes. Cancel from the dashboard at any time. You keep access until the end of the period you have paid for. Within 14 days of the first payment on a new Pro or Business subscription, you can request a full refund, whether that first payment is monthly or yearly. Later renewals and unused time in a billing period are not automatically refunded, except where required by law or manually approved. Your API keys remain valid at the Free tier limits after cancellation. **What happens if I exceed my monthly quota?** Requests beyond your monthly quota return 429 Too Many Requests with a Retry-After header. We never charge overage fees — you can either upgrade your plan or wait for the monthly reset on the first of each month. **Is there a free trial of the paid plans?** There is no formal trial, but the Free plan (30 requests per month) is enough to evaluate the API for non-commercial Service use. Upgrade to Pro for higher-volume non-commercial Service use or Business for commercial Service use. **Do you offer custom plans?** Contact security@bullionapi.dev if your volume requirements exceed the published Business quota. **How is the price data sourced?** Gold prices are cached in our database and refreshed every hour by a scheduled job. API responses are served from that cache. **How is Bank of Canada monetary data sourced?** The monetary series are sourced from the Bank of Canada's [Valet Web Services](https://www.bankofcanada.ca/valet/) JSON observations. The source data is available on the Bank's website free of charge; Bullion API charges for caching, exact normalization, and API delivery. Valet decimal-string values are normalized exactly and returned as decimal strings; monthly source dates are represented as calendar month-end periods. Before you purchase, please note that the data comes from the Bank and is available free from its site. Attribute the Bank and indicate our changes without implying its endorsement. Third-party content is excluded unless separately licensed. See the [Bank of Canada terms](https://www.bankofcanada.ca/terms/). **Can I get the Australian monetary data directly from the RBA?** Yes. The Australian money-supply and Reserve Bank balance-sheet data we serve is published free of charge by the Reserve Bank of Australia. Paid Bullion API plans charge for API access, caching, normalization and delivery, not for the underlying RBA data. This RBA Financial Data is governed by section 5 of the RBA Copyright and Disclaimer Notice, not CC BY 4.0. See the [RBA D3 Monetary Aggregates](https://www.rba.gov.au/statistics/tables/csv/d3-data.csv) and [A1 Balance Sheet](https://www.rba.gov.au/statistics/tables/csv/a1-data.csv) tables and the [RBA notice](https://www.rba.gov.au/copyright/) for reuse terms. Bullion API attributes the source and does not imply RBA endorsement. **What does /v1/gold-silver-ratio provide?** The `/v1/gold-silver-ratio` endpoint provides the gold-to-silver ratio from Bullion API’s local price cache. It supports a latest snapshot or an inclusive historical UTC-day range and is available on every plan under the same Service-use terms as the rest of the API. **How is the central-bank reserve data sourced?** The `/v1/central-bank-reserves` endpoint is available on every plan and returns cached annual reserve observations. Annual value changes combine holdings changes, price movement, and revisions; they do not measure tonnes bought or sold. Source-data reuse rights remain separate from API plan terms and are governed by applicable terms. **Can I use this in commercial products?** Commercial use of the Service is included with Business. Free and Pro are for non-commercial Service use only. Standard plans do not permit redistribution of raw or substantially equivalent market-price data or redistribution or resale of API access. Where the source grants reuse or redistribution rights in particular public statistics, those rights remain unaffected by plan and are subject to the source terms; see the Terms of Service. --- # Terms of Service Last updated 27 September 2026. Effective 11 October 2026. For accounts created and paid subscriptions started after this version was published, these Terms apply from that moment, including the Business plan, its refund terms and its support commitment. These are the terms for using Bullion API. They are written in plain English and are a living document. Have a lawyer review them before relying on them commercially. ## Agreement These Terms of Service ("Terms") govern your use of the Bullion API service operated by us at `api.bullionapi.dev` (the "Service"). By creating an account, generating an API key, or otherwise using the Service, you agree to these Terms. If you do not agree, do not use the Service. We may update these Terms from time to time. Material changes will be announced at least 14 days before they take effect, either by email or by a notice on this page. Continued use after the effective date constitutes acceptance. ## Eligibility and accounts You must be at least 18 years old and legally able to enter a contract in your jurisdiction. When you create an account you agree to: - Provide accurate, current information. - Keep your email and password secure. - Tell us promptly at [security@bullionapi.dev](mailto:security@bullionapi.dev) if you suspect unauthorised access to your account. You are responsible for everything that happens under your account, whether or not you authorised it. ## API keys and security The Service uses bearer-token API keys passed in the `X-API-Key` header. We store only a SHA-256 hash of each key; the plaintext is shown to you once at creation and cannot be recovered. - Keep API keys confidential. Do not commit them to public source control. - Rotate any key you believe has been exposed and delete the old one. - Each request is attributed to the key that made it. Quota usage is shared across all keys on a single account. - You may revoke a key at any time from the dashboard. Revocation takes effect within 60 seconds. We will never ask for your API key, password, or payment details by email. Anyone claiming to need them is not us. ## Acceptable use For these Terms: - **Commercial use** means using the Service in connection with a product, service, business, or activity intended to generate revenue or provide value to customers. - **Internal non-commercial use** means evaluation, personal use, research, or internal analysis that is not customer-facing and is not used in a revenue-generating product or service. - **Commercial display or integration** means showing derived price information or using API responses within your own customer-facing product or service. - **Redistribution or resale** means providing raw or substantially equivalent API responses, feeds, datasets, or API access to a third party. Your plan determines which uses of the Service are permitted: - Free and Pro plans permit internal non-commercial use of the Service only. - Business permits commercial display or integration within your own product or service. - No standard plan permits redistribution or resale of raw or substantially equivalent material provided through the Service, or of API access, except that this restriction does not reduce the independent source-granted rights in public statistics described below. - A separate written agreement may grant additional rights. If the publisher's applicable licence or other terms grant you rights to reuse or redistribute particular public statistical material included in a Service response, nothing in these Terms reduces those independent rights in that material, regardless of your plan. A source may, for example, licence material under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Use only the material and rights that the source actually grants, and meet all applicable source conditions, including attribution, notices of changes, share-alike terms, and restrictions on implying endorsement where applicable. This exception applies only to the source-licensed statistical content; it does not grant rights in Bullion API's response structure or other material. This is not a Bullion API licence or a blanket permission for every dataset. It does not apply to bullion or other metal prices, foreign-exchange or currency-conversion rates, or other market, commodity, reference, or benchmark price data. This source-licence exception does not authorize commercial use of the Service on a plan limited to non-commercial use, use beyond your plan, bypassing quotas or API-key security, sharing or reselling API keys or API access, or reselling the Service, its software, branding, or documentation. Your plan continues to govern access to and use of the Service. You agree not to: - Use the Service outside the usage rights granted by your plan. - Use the Service to violate any applicable law or regulation. - Interfere with the Service or attempt to access it by means other than the documented API endpoints. - Use the data for market manipulation, insider trading, or to inform automated trading decisions without independent verification. The data is provided for informational use; we make no representation that it is suitable for any particular financial purpose. - Send us content that infringes intellectual property rights, contains malware, or is otherwise unlawful. We may suspend an account or key that we reasonably believe is in breach of this section. Where practical, we will notify you and allow a reasonable opportunity to remedy the breach. We may suspend access immediately for a serious or repeated breach. ## Plans, billing and refunds Annual list prices do not establish checkout availability. New annual subscriptions are offered only when annual checkout is enabled; check the billing options shown on the pricing page. Request quotas still reset monthly, and existing annual subscriptions retain their billing interval. The Service is offered in three plans: Free, Pro ($9/month or $90/year; $7.50 effective monthly; $18 saving) and Business ($29/month or $290/year; $24.17 effective monthly; $58 saving). Free and Pro permit internal non-commercial use of the Service only. Business permits commercial display or integration within your own product or service. Standard plans do not permit redistribution or resale of raw or substantially equivalent material provided through the Service, or of API access; this does not reduce the independent source-granted rights in public statistics described under Acceptable use. Plan limits and features are described on the [Pricing](/pricing) page. - Paid plans are billed in advance through Stripe. Pro is $9 USD per month or $90 USD per year. Business is $29 USD per month or $290 USD per year. Monthly and yearly subscriptions receive the same plan entitlement; request quotas reset monthly. - Monthly billing renews on the same day each month. Yearly billing renews once per year and covers twelve months of access. - Quotas are soft caps. If you go over your monthly quota, requests return HTTP `429` until the cycle resets. We do not charge overage fees and we do not auto-upgrade your plan. - You can cancel at any time from the dashboard. Service continues until the end of the period you have paid for. We do not automatically refund unused time in a monthly or yearly billing period, except where required by law or manually approved. - Within 14 days of the first payment on a new Pro or Business subscription, you can request a full refund by replying to the Stripe receipt. This applies whether the first payment is monthly or yearly. - Later renewals are non-refundable except where required by law or manually approved. - Prices may change with 30 days' notice. The notice will be sent to the email on your account and posted on the Pricing page. Changes apply to the next billing cycle. You authorise us (via Stripe) to charge the payment method on file for all recurring fees. ## Service availability The Service depends on third-party upstream price data and Cloudflare Workers and D1 for compute and storage. On Free and Pro plans, the Service is provided on a best-effort basis with no formal uptime SLA. On the Business plan, the Service is also provided without a formal uptime SLA, but we will reply to support requests sent by email within one business day (Monday to Friday, excluding UK public holidays). This is a response-time commitment, not a resolution time. Prices are refreshed hourly and served from our cache. The availability of upstream price data may affect how current those cached values are. Currency conversion inputs are attributed to [Exchange Rates By UniRateAPI](https://unirateapi.com/). Bullion API uses them to calculate informational metal prices in supported currencies and does not sell them as a standalone exchange-rate feed. Prices in currencies other than USD, EUR and GBP are derived from the most recent accepted Bullion API daily FX snapshot, not a provider end-of-day reference rate. 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. Bullion API does not publish, redistribute or resell raw FX rates. See the [price methodology](https://bullionapi.dev/docs#methodology). ## Data licensing Disclosure updated 8 October 2026. This section brings together existing publisher reuse notices; it does not change the Service terms, their effective date or the rights granted by your plan. ### What your subscription pays for Original source information may be available free of charge from its publisher. Bullion API charges for caching, normalization, and delivery, not exclusive ownership of the underlying source information. Publisher licensing and attribution requirements apply alongside Bullion API plan terms. Identify changes made to original source information. Other publishers may have different terms; this notice is not a blanket licence for every dataset. Standard plans do not authorize raw-data redistribution or resale of the Service; independent source-granted rights remain unaffected as described under Acceptable use. ### Publisher-specific requirements ### National Bank of Ukraine - National Bank of Ukraine open data are available free of charge from the publisher. - The NBU permits commercial reuse of its open data with attribution to the National Bank of Ukraine. - Prior approval is required before modifying or editing NBU source data; Bullion API preserves the reported valuation in millions of US dollars and derives no quantities or changes. ### ECB/ESCB - Original ECB statistics are freely available from the European Central Bank. - The ECB permits commercial and non-commercial reuse when statistics and metadata are unchanged; quote the source, for example “Source: ECB statistics.” - Identify any changes; Bullion API applies the published unit multiplier, normalizes ounces, and converts reported weight to tonnes. ### Banco Central do Brasil - Original Banco Central do Brasil time series are freely available from the Banco Central do Brasil. - The Banco Central do Brasil publishes this series under the Open Database License (ODbL) 1.0: attribute the Banco Central do Brasil, and offer any adapted database you make public under the ODbL (share-alike). - Identify any changes; Bullion API stores the published values in millions of US dollars and derives the change from the previous observation. Required publisher attribution and reuse notices also accompany the relevant data. The summaries above do not replace the applicable publisher conditions. ## Intellectual property We retain all rights, title and interest in and to the Service, including the software, branding and documentation. These Terms grant you a limited, non-exclusive, non-transferable, revocable licence to use the Service in accordance with them. You retain all rights to data and content you submit to us or that your applications generate. You grant us a limited licence to host and process that content solely to operate the Service for you. Price data returned by the API originates from third-party upstream providers and is provided to you only within the usage rights granted by your plan. Business permits commercial display or integration within your own product or service, but no standard plan permits redistribution or resale of raw or substantially equivalent price data. This restriction continues to apply to metal prices, foreign-exchange and currency-conversion rates, and other market, commodity, reference, or benchmark prices. The source-licensed public-statistics exception is described under Acceptable use; nothing in these Terms grants rights that the source does not grant. ## Disclaimer The Service is provided "as is" and "as available". To the maximum extent permitted by law, we disclaim all warranties, whether express, implied or statutory, including warranties of merchantability, fitness for a particular purpose, accuracy, non-infringement, and quiet enjoyment. We do not warrant that the data will be uninterrupted, accurate, current, or free from defects, or that defects will be corrected. ## Limitation of liability To the maximum extent permitted by law, our aggregate liability for all claims arising out of or relating to the Service in any 12-month period will not exceed the greater of (a) the fees you paid us in that period and (b) USD $100. In no event will we be liable for any indirect, incidental, special, consequential, exemplary or punitive damages, including lost profits, lost revenue, loss of goodwill, or loss of data, even if we have been advised of the possibility of such damages and even if a remedy fails of its essential purpose. ## Indemnification You will defend, indemnify and hold us harmless from any third-party claim arising out of (a) your breach of these Terms, (b) your application or content, or (c) your violation of any law or the rights of any third party. We will defend and indemnify you against any third-party claim that the Service, as provided by us and used in accordance with these Terms, infringes that party's intellectual property rights, and pay damages awarded against you or settlement amounts agreed by us. ## Termination You may terminate these Terms at any time by deleting your account from the dashboard. We may terminate or suspend your access at any time, with or without notice, if (a) you breach these Terms, (b) your payment fails and is not cured within 7 days, or (c) we discontinue the Service. - On termination, all API keys on the account are revoked. - Sections that by their nature should survive (Liability, Indemnification, Governing law, Contact) survive termination. - Provisions of these Terms that were intended to operate, or to have effect, after termination remain in full force. Termination does not relieve you of the obligation to pay any fees that accrued before termination. ## Changes, governing law and contact These Terms are governed by the laws applicable to a contract formed in your country of residence, without regard to its conflict of laws provisions. Any dispute will be resolved in the courts of competent jurisdiction in your country of residence. If any provision of these Terms is held to be invalid or unenforceable, the remaining provisions remain in full force and effect. These Terms are the entire agreement between you and us regarding the Service and supersede any prior agreements. Questions: [the contact form](/contact) or [hello@bullionapi.dev](mailto:hello@bullionapi.dev). Security disclosures: [security@bullionapi.dev](mailto:security@bullionapi.dev). --- # Privacy Notice Last updated 7 October 2026. This is what we collect, why, and what we do with it — written in plain English. Last updated 7 October 2026. Have a lawyer review the notice before relying on it for your business. ## Scope This Privacy Notice explains what personal data Bullion API ("we", "us") collects, why we collect it, who we share it with, and what rights you have. It applies to the website at `api.bullionapi.dev` and the API endpoints under that domain. It does not apply to any third-party sites linked from our pages. ## Data we collect We collect the minimum needed to run the service: - **Account data:** your name and email address, plus a securely hashed password when you use email/password sign-in. If you choose Google or GitHub sign-in, we receive your provider account identifier, name, email address, email-verification status and, when supplied, profile image. We do not receive your Google or GitHub password. - **API keys:** the keys you create. We store only their SHA-256 hash; the plaintext is shown to you once at creation and we cannot recover it. - **Request logs:** for every API call we receive, we log a timestamp, the API key ID (not the key itself), the endpoint and currency, the HTTP status, the byte size of the response, and your IP address and user agent. We use these for abuse detection, quota enforcement and debugging. - **Billing data:** handled by Stripe. We receive your plan, the Stripe customer ID and the subscription status back from Stripe. We do not see or store your card number. - **Support emails:** anything you send us at [hello@bullionapi.dev](mailto:hello@bullionapi.dev) or one of the alias addresses listed in section 12. We do not set tracking cookies, do not run advertising pixels, and do not sell or share personal data for marketing. ## Portfolio data If you use the portfolio features, we store the financial details you enter about your gold holdings: - **Transaction records:** the transaction date, quantity and weight unit, amount paid or received, delivery costs, fees, product description, and the tax treatment you declare. - **Calculated figures:** your holdings, cost basis, profit or loss, valuations, and gains worked out from those records. We use this information only to provide the portfolio features: showing what you hold, what it cost you, what it is worth now, and any gains or losses. You can delete individual transactions at any time in the dashboard, or delete your whole account to remove everything at once. We never sell this information or use it for marketing. ## How we use it We use the data above to: - Provide, authenticate and bill the service. - Enforce per-plan quotas and detect abusive usage. - Diagnose outages and respond to support requests. - Comply with legal obligations and respond to lawful requests. - Notify you of service changes or security incidents that affect your account. We do not use your data to train third-party models and we do not run behavioural advertising. ## Sub-processors We use the following sub-processors to operate the service. Each operates under a data-processing agreement with us that restricts use to the documented purposes. - **Cloudflare** — hosting, edge compute and D1 application database for the API and website (data may transit Cloudflare’s global network). - **Stripe** — payment processing and subscription management. Card data is handled by Stripe and never touches our servers. - **Twelve Data and Gold API** — upstream metal and exchange-rate pricing. They receive requested symbols, currencies and time ranges, not your account data. - **Cloudflare Workers Email Service** — transactional email (account verification, password reset, billing receipts). Emails are sent through Cloudflare’s network infrastructure as an integrated Worker binding, not through a third-party SMTP relay. Cloudflare operates the service and its email delivery infrastructure. We will give at least 30 days' notice by email before adding a new sub-processor that handles customer personal data. ### Optional social sign-in If you choose to sign in with or connect Google or GitHub, your browser visits that provider and the provider learns that you are authenticating with Bullion API. These optional identity providers operate under their own privacy notices: [Google Privacy Policy](https://policies.google.com/privacy) and [GitHub Privacy Statement](https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement). We request only identity and email information for authentication, not access to your files or repositories. Email/password sign-in remains available. Matching email addresses do not automatically connect accounts; existing users connect a provider while signed in through Settings. ## Cookies and sessions We use a small number of strictly necessary cookies and server-set session tokens. None of them are used for tracking. - `better-auth.session_token` — signed session token set when you sign in. HTTP-only, Secure on `https://`. - Temporary authentication cookies protect social sign-in redirects. Your explicit signup preferences are carried in protected OAuth state while you authenticate with a provider and are applied only when your account is first created. - `__cf_bm` (Cloudflare) — bot management for the public pages. Not on by default for authenticated dashboard traffic. ## Retention We keep data only as long as we need it: - **Account data:** for as long as your account is active. Account deletion from the dashboard happens immediately and cannot be undone. - **Portfolio data:** kept while you use the portfolio features. Deleting a transaction removes it immediately; deleting your account removes all portfolio data immediately and cannot be undone. - **API keys:** retained while you have a key with that name. Revoking a key stops it working immediately and the row is deleted within 24 hours. - **Request logs:** 90 days, then aggregated into anonymous counters. We use the aggregated counters indefinitely for capacity planning. - **Stripe billing history:** Stripe retains this per their own policy (typically 7 years for tax/audit reasons). We retain the subscription status as long as the subscription is active. - **Support emails:** deleted within 12 months of the last exchange unless we have a continuing business reason to keep them. Where we are required to keep data longer for legal reasons (for example, anti-money-laundering records), we retain it for the minimum period required by law. ## Your rights Depending on where you live, you have some or all of these rights: - **Access** — request a copy of the personal data we hold about you. - **Correction** — ask us to fix anything wrong. - **Deletion** — delete your account and the data we hold. The dashboard has a Delete account button; deletion happens immediately and cannot be undone. - **Export** — receive your data in a portable machine-readable format. We don't have a self-serve button for this yet; email [privacy@bullionapi.dev](mailto:privacy@bullionapi.dev) and we'll send a JSON export within 30 days. - **Object or restrict** — object to certain processing or ask us to pause it. - **Withdraw consent** — where our lawful basis is consent, you can withdraw it at any time without affecting prior processing. - **Complain** — to your local data protection authority. We respond to verified requests within 30 days. We may need to confirm your identity before acting on a request. ## Legal basis (GDPR / UK GDPR / CCPA) Under the EU GDPR and UK GDPR we rely on the following bases: - **Contract:** to provide the API you have signed up for, including authentication, billing and support. - **Legitimate interests:** to keep the service secure (request logging, abuse detection), to keep records of financial transactions and to improve the product based on aggregate usage. - **Legal obligation:** to comply with applicable law, respond to lawful requests and keep accounting records. - **Consent:** for anything optional (for example, marketing emails, if we add them later). Under the California Consumer Privacy Act (CCPA/CPRA), we do not sell or share personal information for cross-context behavioural advertising. California residents have the same access, deletion and portability rights listed above and can exercise them through the same channels. If you are in a jurisdiction that requires a different lawful basis for any processing described here, contact us and we will work with you to resolve it. ## Security We protect data with controls proportionate to the sensitivity of the information: - TLS everywhere — our service is HTTPS-only. - Email/password credentials are securely hashed through Better Auth and are never stored in plaintext. Social-only accounts do not require a Bullion API password. - API keys are hashed with SHA-256 before storage; the plaintext is shown once at creation. - Production access is gated by role, audited and limited to operational necessity. - We log and alert on anomalous auth patterns (impossible travel, high-volume enumeration, key sharing). No system is perfectly secure. If you find a vulnerability, please report it to [security@bullionapi.dev](mailto:security@bullionapi.dev) rather than filing a public issue. ## International transfers Cloudflare and Stripe may process your data in the United States or other countries outside your own. Where we transfer personal data out of the EEA, the UK or Switzerland, we rely on the European Commission's Standard Contractual Clauses (or the UK International Data Transfer Agreement) together with supplementary technical and organisational measures. Cloudflare and Stripe publish their transfer mechanisms in their own privacy documentation; please review those if you need assurance specific to your situation. ## Children The Service is intended for adults. We do not knowingly collect personal data from anyone under 18. If you believe a child has provided us with personal data, contact [privacy@bullionapi.dev](mailto:privacy@bullionapi.dev) and we will delete it. ## Changes and contact We will post material changes on this page at least 14 days before they take effect and email the address on your account. The "Last updated" date at the top reflects the current version. Earlier versions are available on request. Any question about this notice or how we handle your data: [privacy@bullionapi.dev](mailto:privacy@bullionapi.dev). Security disclosures: [security@bullionapi.dev](mailto:security@bullionapi.dev).