Download metal price history as CSV

One request to the bars endpoint returns daily open, high, low and close for a commodity, and a short script writes them to CSV. Gold for 1 to 23 October 2026 has 17 daily rows. Weekends have no bars.

Download daily gold bars with the API

Use GET /v1/commodities/spot/XAU/USD/bars for gold priced in USD. Each bar has a timestamp and decimal strings for open, high, low and close. The script writes those price fields directly to CSV and takes the date from the timestamp. Its output has the columns date, open, high, low and close.

  1. Put your API key in the environment Get a free API key and keep it in the environment variable NARWHAL_API_KEY. The script reads that variable and sends the key in the Authorization: Bearer YOUR_KEY header.
  2. Set the dates and interval Both start and end are required ISO timestamps. The example uses 2026-10-01T00:00:00Z through 2026-10-24T00:00:00Z, with interval set to 1d, for the daily gold bars dated 1 to 23 October 2026.
  3. Save the script as bars.py Keep the CSV header and the bar fields in the same order. The script sends requests to https://api.narwhalapi.com and writes the returned rows to standard output, so the command in the following step can put them in a file.
    Python, daily gold bars to CSV (run it as: python bars.py > gold.csv)
    import csv, os, sys
    import requests
    
    H = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}
    params = {"start": "2026-10-01T00:00:00Z", "end": "2026-10-24T00:00:00Z", "interval": "1d", "limit": 500}
    out = csv.writer(sys.stdout)
    out.writerow(["date", "open", "high", "low", "close"])
    cursor = None
    while True:
        if cursor:
            params["cursor"] = cursor
        body = requests.get("https://api.narwhalapi.com/v1/commodities/spot/XAU/USD/bars", params=params, headers=H, timeout=15).json()
        for b in body["bars"]:
            out.writerow([b["timestamp"][:10], b["open"], b["high"], b["low"], b["close"]])
        cursor = body.get("next_cursor")
        if not cursor:
            break
    
  4. Write gold.csv Use python bars.py > gold.csv to write the CSV file. For 1 to 23 October 2026, the output has 17 daily data rows, with the column header above them.

The out.writerow before the loop creates the header. Inside the loop, each call to out.writerow adds a bar in the order given by that header. The price fields stay as the decimal strings from the response, and b["timestamp"][:10] supplies the date. To use a daily closing price in a sheet or model, take the close value from the row for the date you need. The open, high and low for that bar are available in the same row. For hourly or minute exports, this script still writes only the date portion of each timestamp.

Use the history table or import into a sheet

Open the price history on the gold commodity page to use its table without writing a script. The website's commodity pages also have a CSV download of their history for Basic and Pro. If you want the website download, that option depends on your plan; see plans and limits.

To bring the script's CSV into Excel, use Data > From Text/CSV. In Google Sheets, use File > Import. The file contains the date alongside each bar's open, high, low and close, so you can use the same exported history in a sheet or as input to a model. Retain the date column with the prices when you import the rows.

Choose a download method and interval

SituationBest optionWhy
A quick lookThe website chartView gold price history on the commodity page
A repeatable download or a model inputThe scriptWrite the requested bars to CSV
Hourly or minute dataThe script with interval set to 1h or 1mRequest the interval you need and shorten the window

The bars endpoint accepts 1m, 1h and 1d for interval. Keep 1d for daily history. For hourly or minute data, change that parameter and shorten the requested window; the sample's dates and CSV example describe daily bars.

Why are weekends missing from gold price history?

Spot gold has no bars when the market is closed on weekends. The range from 1 to 23 October 2026 spans 23 calendar days and contains 17 daily bars, all on weekdays. Read the dates in the CSV as the dates of the returned bars. A daily interval does not add weekend rows to the file.

How many bars come back per request?

Each request returns up to limit bars, which is 500 in the script. The loop follows next_cursor until it is null, writing each response's bars to the same CSV. Leave that loop in place when you change the requested dates or interval, so the export can include the following pages. For a longer range, retain start, end and interval while the cursor selects the following page. The script adds that cursor to params before making the following request. Its header stays outside the loop, so all pages share the same set of column names.

A 409 cursor_expired means you need to restart without a cursor. Start the export again with cursor = None, as at the beginning of the script, and write the file from that fresh request.

What if my start date is older than my plan allows?

A start date before your history window moves up to the window's first day without an error. Free includes the last 12 months, Basic includes 5 years, and Pro includes full history. Read the first returned date to see where your CSV begins, since it can be later than the start date in your request.

You get 403 plan_upgrade_required when the whole requested range ends before your plan's window. The same error applies when you read a single date or period outside that window. An earlier start alone does not cause that error when the range reaches into the available history.

Worked example: October 2026 gold

Start with the sample's daily request for October 2026. The returned dates begin at 2026-10-01 and end at 2026-10-23, with 17 data rows. Write the CSV header, then write each bar's date and price fields in that header's order. The first close is 4177.4458 on 1 October 2026. It belongs in the close column of the first data row, after the date, open, high and low.

DateOpenHighLowClose
2026-10-014156.83524192.52154139.77874177.4458
2026-10-024177.21834225.64974125.92324141.5280

Published .