# How to pull prediction-market prices into Google Sheets

Five ways to get Kalshi and Polymarket prices into a spreadsheet — a short Apps Script, a CSV feed, an add-in, a file — and why a cell sits stale for an hour.

*https://predictionmarkets.tools/how-to/pull-prediction-market-prices-into-google-sheets · next to Prediction Market Analytics & Dashboards*

**Answer:** IMPORTDATA cannot do it on its own, because Kalshi and Polymarket answer in JSON and IMPORTDATA reads only CSV or TSV. The working route is a few lines of Apps Script calling the venues' public price endpoints, refreshed by a time-driven trigger rather than left as a formula. If you would rather not write code, a scraper that publishes CSV or a file export gets the numbers in, at the cost of freshness.

## The approaches, in order

1. [Kalshi API](https://predictionmarkets.tools/tools/kalshi-api.md) — Public market reads with no key: GET /markets/{ticker} from Apps Script, with bid, ask and last trade as dollar strings such as 0.4200.
2. [Polymarket CLOB API](https://predictionmarkets.tools/tools/polymarket-clob-api.md) — Keyless midpoint, best price and last-trade endpoints per outcome token, each returning one decimal string a script can drop straight into a cell.
3. [Apify prediction-market scrapers](https://predictionmarkets.tools/tools/apify-prediction-market-scrapers.md) — Scheduled actors write Kalshi and Polymarket rows to a dataset that the Apify API serves as CSV — the one shape IMPORTDATA reads natively.
4. [Artemis prediction-market metrics](https://predictionmarkets.tools/tools/artemis-prediction-markets.md) — A paid Google Sheets add-in, but for venue-level daily volume, open interest and fees across thirteen venues, not a single market's price.
5. [Lychee](https://predictionmarkets.tools/tools/lychee.md) — Pull trade, market or book history in a browser and export CSV or XLSX to open in Sheets — a snapshot with no refresh at all.

*Ordered editorially. Paid placement does not affect this order.*

## The short way

Write one small Apps Script function that fetches the price and writes it into a cell, and run it
on a time-driven trigger. Do not reach for a formula first: `IMPORTDATA` reads only CSV or TSV,
according to Google's own reference, and both venues' price endpoints answer in JSON.

Kalshi's market reads are public — the OpenAPI description of `GET /markets/{ticker}` carries an
empty security list — and Polymarket's order-book prices need no key either. In the sheet, open
**Extensions → Apps Script** and paste something like this:

```javascript
// Writes the latest prices into the "Prices" tab. Column A holds the identifier.
function refreshPrices() {
  var sheet = SpreadsheetApp.getActive().getSheetByName('Prices');
  var rows = sheet.getRange('A2:A').getValues().filter(function (r) { return r[0]; });
  var out = rows.map(function (r) {
    var id = String(r[0]);
    if (id.length > 40) {
      // A long numeric id is a Polymarket outcome token: the midpoint of its book.
      var mid = UrlFetchApp.fetch('https://clob.polymarket.com/midpoint?token_id=' + id);
      return [Number(JSON.parse(mid.getContentText()).mid), new Date()];
    }
    // Otherwise treat it as a Kalshi market ticker: the last traded YES price.
    var res = UrlFetchApp.fetch('https://external-api.kalshi.com/trade-api/v2/markets/' + encodeURIComponent(id));
    return [Number(JSON.parse(res.getContentText()).market.last_price_dollars), new Date()];
  });
  sheet.getRange(2, 2, out.length, 2).setValues(out);
}
```

Then add a time-driven trigger for `refreshPrices` under **Triggers** in the editor. Write the
timestamp beside every price, as above. It is the only way anyone reading the sheet can tell a
live number from one that stopped updating on Tuesday.

## What the options are

[The Kalshi API](https://predictionmarkets.tools/tools/kalshi-api) is the cleanest source for this. `GET /markets/{ticker}` returns
one `market` object with `yes_bid_dollars`, `yes_ask_dollars`, `no_bid_dollars`, `no_ask_dollars`
and `last_price_dollars`, each a fixed-point dollar string with up to four decimal places, plus
quantities as `_fp` strings. `GET /markets?series_ticker=` gives a whole series in one call, which
matters once the sheet has more than a handful of rows.

[The Polymarket CLOB API](https://predictionmarkets.tools/tools/polymarket-clob-api) prices outcome tokens, not markets. Each
market carries its token ids as a JSON-encoded array in a field called `clobTokenIds`, which you
read once from [Gamma](https://predictionmarkets.tools/tools/polymarket-gamma-api) and keep in the sheet. After that,
`/midpoint` returns `{"mid": "0.085"}`, `/price?side=BUY` the lowest ask, `/last-trade-price` the
last fill, and batch forms of all three take several tokens in one POST.

[Apify's prediction-market actors](https://predictionmarkets.tools/tools/apify-prediction-market-scrapers) are the no-code route
that ends in an actual formula. An actor run on Apify's scheduler writes Kalshi and Polymarket rows
— question, YES and NO price, bid, ask, volume — into a dataset, and the dataset items endpoint
takes `format=csv`, which `IMPORTDATA` can read. Each actor is a different author's code with its
own field names and its own per-row price, so pin one and expect to fix the sheet when it changes.

[Artemis](https://predictionmarkets.tools/tools/artemis-prediction-markets) is the one card here with a real Sheets add-in, and it
answers a different question. What it serves is venue-level daily metrics — volume, open interest,
fees, users — across thirteen venues, not the price of a market. The add-in comes with the 100 USD
Investor tier at 50,000 sheet calls a month, or alone as the 500 USD Plugin tier.

[Lychee](https://predictionmarkets.tools/tools/lychee) is for when the sheet is an analysis, not a monitor. Pull Kalshi or
Polymarket history in the browser, export CSV or XLSX, and open the file in Sheets. There is no
refresh; the plans start at 19.99 USD a month with 12,500 rows per historical pull.

## Where this breaks

**A cell is not live, and Sheets decides when it updates.** Google's help page states the intervals:
`IMPORTDATA`, `IMPORTHTML`, `IMPORTFEED` and `IMPORTXML` recalculate every hour, `IMPORTRANGE` every
30 minutes. A custom function — `=KALSHI("TICKER")` written as a script — is worse: Google's guide
says it does not recalculate until you edit it or a cell passed to it changes, and passing `NOW()`
to force it is not allowed and leaves the cell on "Loading..." for good. A custom function must
also return within 30 seconds or show `#ERROR!`. That is why the short way above is a triggered
script that writes values, not a formula.

**The quota is per Google account, not per sheet.** A consumer account gets 20,000 `UrlFetchApp`
calls a day and 90 minutes of trigger runtime, a Workspace account 100,000 calls and six hours. A
script that fetches 20 markets one at a time every minute makes 28,800 calls a day and stops
part-way through the day. Batch: Kalshi's list endpoint by series, Polymarket's POST forms for many
tokens.

**The venue's rate limit sees Google, not you.** Requests leave from Google's servers. Polymarket
enforces its limits per IP address through Cloudflare and delays requests over them rather than
rejecting them; Kalshi answers an over-limit request with a 429 and no `Retry-After` header. A
script that hammers either one gets slow or empty answers, and the error lands in a cell you may not
be looking at. Check the trigger's execution log.

**Cents, dollars and probabilities are three different numbers.** Kalshi now returns prices as
fixed-point dollar strings — `"0.1200"` — and its own documentation says integer-cent fields cannot
represent sub-cent ticks, which some markets now use. A tutorial dividing `yes_bid` by 100 was
written against a field this endpoint no longer returns. Polymarket's prices are decimal strings
between 0 and 1. Both are prices of a contract paying one unit, not probabilities: a wide spread or
a stale last trade makes the "probability" in your sheet a quote of one side of a thin book. Both
arrive as strings, so convert with `Number()` in the script rather than writing the string: a
sheet whose locale writes decimals with a comma can keep `"0.42"` as text, and a sum over text
cells does not fail — it leaves them out.

**Which price is in the cell is a choice.** Bid, ask, midpoint and last trade differ, sometimes by a
lot, and the sheet does not say which unless you label the column. On a market that has not traded
today, `last_price_dollars` is an old number that looks exactly like a current one.

**Your own account is a different problem.** Everything above is public data. Reading your Kalshi
positions or balance needs signed headers — RSA-PSS over SHA-256, or an Ed25519 key — and Apps
Script's built-in Utilities service offers RSA-SHA256 and HMAC signing but no PSS or Ed25519.
Pasting a private key into a script bound to a shared sheet also gives it to every editor of that
sheet. Treat it as a separate project; [where your key lives](https://predictionmarkets.tools/guides/where-your-key-lives) is the
page for that decision. The same goes for an Apify URL carrying `token=`: anyone who can read the
formula can read the token.

## If you outgrow this

A sheet polling once a minute is a dashboard with an hour's worth of failure modes. If what you
want is a number that moves the moment the book does, that is a stream, not a spreadsheet, and
[streaming an order book](https://predictionmarkets.tools/how-to/stream-an-order-book) is where that starts. If what you want is
history to chart, pull it once from the price-history endpoints or an archive instead of keeping a
sheet open — [getting resolved market history](https://predictionmarkets.tools/how-to/get-resolved-market-history) covers the
sources. And if the sheet is becoming a dashboard other people read,
[building a dashboard on Polymarket data](https://predictionmarkets.tools/how-to/build-a-dashboard-on-polymarket-data) covers the
tools built for that job.

## FAQ

### Is there an IMPORTJSON function in Google Sheets?

Not a built-in one. IMPORTJSON is a name for community-written Apps Script that people paste into their own sheets. The built-in import functions are IMPORTDATA for CSV and TSV, IMPORTXML, IMPORTHTML, IMPORTFEED and IMPORTRANGE. Any JSON reader in a sheet is a script, and you own it the moment you paste it.

### Can I pull my own Kalshi positions into the sheet the same way?

Not with the same few lines. Market prices are public; your positions sit behind Kalshi's signed requests, which use RSA-PSS over SHA-256 or an Ed25519 key. Apps Script's Utilities service lists RSA-SHA256 and HMAC signing methods and no PSS or Ed25519 variant, so the signature has to come from code you write or from somewhere outside Sheets.

### How often can the prices refresh?

A time-driven trigger can run a script as often as once a minute, and Google says the time may be slightly randomised. IMPORTDATA refreshes about once an hour. A consumer Google account gets 20,000 URL fetches a day across all its scripts, which one market polled every minute uses 1,440 of.

## Sources

1. [IMPORTDATA](https://support.google.com/docs/answer/3093335) — Google, read 2026-10-04
2. [Change a spreadsheet's locale, time zone, settings and language (recalculation of external data functions)](https://support.google.com/docs/answer/58515) — Google, read 2026-10-04
3. [Custom functions in Google Sheets](https://developers.google.com/apps-script/guides/sheets/functions) — Google, read 2026-10-04
4. [Quotas for Google services](https://developers.google.com/apps-script/guides/services/quotas) — Google, read 2026-10-04
5. [Installable triggers](https://developers.google.com/apps-script/guides/triggers/installable) — Google, read 2026-10-04
6. [Get Market (GET /markets/{ticker})](https://docs.kalshi.com/api-reference/market/get-market) — Kalshi, read 2026-10-04
7. [Fixed-Point Representation](https://docs.kalshi.com/getting_started/fixed_point_migration) — Kalshi, 2026-08-20
8. [Quick Start, Authenticated Requests](https://docs.kalshi.com/getting_started/quick_start_authenticated_requests) — Kalshi, read 2026-10-04
9. [Prices and Order Books](https://docs.polymarket.com/market-data/prices-order-books) — Polymarket, read 2026-10-04
10. [Run Actor and retrieve data via API](https://docs.apify.com/academy/api/run-actor-and-retrieve-data-via-api) — Apify, read 2026-10-04

*Last updated 2026-10-04. A reference page, corrected in place — not a dated post.*
