Skip to main content

Overview

The API speaks CSV. Ask for it with Accept: text/csv and every view and every function answers with a header row and comma-separated values, straight into a spreadsheet. The catch is headers. A spreadsheet formula like Google Sheets’ IMPORTDATA cannot set any, and without Accept: text/csv the server answers JSON. So the working recipes are the ones that can set a header: Apps Script in Sheets, Power Query in Excel.
Everything under “What the server does” was observed on 2026-09-24 against the live API with the curl commands shown. The Apps Script and Power Query snippets were not executed: they are written against those observations, and are marked as such.

What the server does

Base URL and key are the ones on Quickstart:
A view, as CSV:
A function, as CSV. POST with a JSON body, or GET with the arguments in the query string; both return the same CSV:
quote_history returns one JSON object per session holding only the fields you select (default ticker, trade_date, close_adj), so as CSV it is a single column of JSON. For a price series in a sheet, read the typed views (equities, units, raw closes) as below, or call quote_history as JSON.
The key in the URL, with no headers at all, returns JSON:
The 1,000-row cut is invisible in a spreadsheet. A view asked for 5,000 rows returned 1,000, and the only sign was the Content-Range response header, which no spreadsheet shows. The sheet simply ends. Filter to fewer than 1,000 rows, or check the count as the Apps Script below does. Functions refuse instead of cutting (the 400 above), which is safer, but the refusal arrives as JSON. See Paging without losing rows.

Google Sheets: IMPORTDATA does not work

IMPORTDATA(url) fetches a URL and splits it as CSV. It cannot set headers, so:
  • the key can go in the URL (?apikey=… worked, above);
  • the schema needs no header (api is the default);
  • but the format cannot be chosen, and without Accept: text/csv the server returned JSON in every request we made.
What Accept header Google’s fetcher actually sends was not observed. Unless it sends text/csv, IMPORTDATA gets JSON and splits it on commas into cells like [{"ticker":"PETR4". We found no way to request CSV from the URL alone, so IMPORTDATA is not a supported recipe.

Google Sheets: Apps Script custom function

Not executed. Written against the server behaviour above, not run inside Google Sheets. UrlFetchApp.fetch, Utilities.parseCsv and custom functions are standard Apps Script; the details (header casing, number handling in your locale) are exactly where an untested snippet can be wrong.
Extensions → Apps Script, paste, save:
The key sits in the script, not in the sheet’s cells. It is the shared publishable key printed on Quickstart, so this hides nothing, but anyone with edit access to the sheet can read it.

Excel: Power Query

Not executed. Written against the server behaviour above, not run in Excel. Web.Contents with Headers, Csv.Document and Table.PromoteHeaders are standard Power Query M.
Data → Get Data → From Other Sources → Blank Query → Advanced Editor:
For a function, pass a body; Web.Contents sends a POST when Content is set:
If Excel asks how to authenticate, choose Anonymous: the key travels in the header. The "en-US" culture on the type step matters. The server writes decimals with a dot (49.600000), and a workbook in a Brazilian locale reads a dot as a thousands separator. We did not test this in a pt-BR Excel; typing the columns with an explicit culture is the safe default either way.

Key in the URL: the trade-off

It works (?apikey=…, observed above), and it is the only way to authenticate from a tool that cannot set headers. Two honest caveats:
  • The key then appears in the sheet, the query, the browser history and any proxy log. It is the shared publishable key, already printed on these docs, so nothing secret leaks. A user token from sign-in is different: never put one in a URL.
  • It does not solve the format. A tool that cannot set Accept gets JSON, so the URL-key route is useful only to tools that parse JSON.

Conventions & limits

Auth tiers, the 1,000-row cap, nulls.

Paging without losing rows

Three ways a response gets cut, and only one tells you.