Guide · Gumroad, Polar & Lemon Squeezy → Google Sheets

Gumroad, Polar and Lemon Squeezy sales in Google Sheets: 5 ways

From a free CSV export to a sheet that updates itself. An honest comparison, including the options that cost nothing.

Disclosure: we make one of these options (Earnings by Small Rows, option 5). Not released yet. It is built and tested against simulated APIs only, and still has to be tested with real accounts and pass Google's review before anyone can install it. We've tried to be fair and we say where something else fits better. We have no affiliate links or deals with any company mentioned here. Small Rows is not affiliated with Gumroad, Polar or Lemon Squeezy. We do sell our own products through Polar.

The short answer

If you…Try
need the numbers once a month or once a year (say, for taxes)Export a CSV from each platform. Free, and no third party sees your data.
sell on Polar and are fine pasting a little codeOur free Apps Script. It copies your orders into a sheet and can run on a timer.
already use a connector add-on for other APIsAPI Connector or Coupler.io's JSON importer, set up by hand
want a row added for each new sale, or other actions on each saleZapier, Make or IFTTT
sell on more than one of these and want full history, fees, refunds and tax in one sheetEarnings by Small Rows, once it is released
want charts on your phone rather than a spreadsheetA revenue dashboard app

Comparison at a glance

OptionCost to startPast sales?Refreshes by itself?Watch out for
1. CSV export & importFreeYesNoManual every time. Three different file layouts. Lemon Squeezy and large Gumroad exports arrive by email.
2. Your own Apps ScriptFreeYesYes, with a time-driven triggerYou maintain it. Raw orders only.
3. API Connector / Coupler.ioFree tiersYes, if you set up pagingOn paid plansWe found no ready-made source for these platforms, so you configure the requests yourself.
4. Zapier / Make / IFTTTFree tiersMostly no (new sales only)YesOne row per new event. Refunds and edits need extra steps.
5. Earnings by Small Rows (ours)Free: one platform, last 90 days. Pro US$6/month (planned).Yes (full history on Pro)Pro: hourly or dailyNot released, and not yet tested against live accounts.

1. Export a CSV, import it into Sheets

All three platforms can export your sales as a CSV file. In Google Sheets, File → Import → Upload then turns each file into a tab, and Insert → Pivot table gives you totals by month or product.

Gumroad

On the Sales tab, click the Export icon, pick a date range and click Download. To export one product, select it with the Filter icon first. Other filters on the page don't apply to the export. If the file isn't ready straight away, Gumroad emails it to you (Gumroad: the sales dashboard). One catch: the dashboard uses your time zone but the CSV uses UTC, so Gumroad suggests adding a one-day buffer at each end of the range (Gumroad: sales analytics).

Polar

Polar added a CSV export of orders to the dashboard, and it respects the product filter you've set (Polar product updates). We haven't seen the file itself, so we can't tell you exactly which columns it has.

Lemon Squeezy

The Export button on the Orders page generates a CSV and emails it to the store owner. Lemon Squeezy documents the columns, including product, variant, country, subtotal, discount, tax, total, tax name and rate, and the order date in UTC (Lemon Squeezy: Exporting orders). Two things to know: the money columns are in US-dollar cents (so $10 shows as 1000, and you divide by 100), and the documented columns include the buyer's name and email. The documented list doesn't include a fee column.

Best for: a monthly or yearly check, or handing figures to an accountant. It's free and nothing else touches your data. Not great for: anything you want kept up to date, or combining platforms, since each file has its own layout. The files also hold buyer emails, so think about who you share the sheet with.

2. Your own Apps Script (free script for Polar)

Every Google Sheet comes with Apps Script (Extensions → Apps Script), which can call these platforms' APIs with your own token. Here's a free script for Polar, which we picked because Polar lets you make a token that can only read orders. It copies the last 90 days of orders into a tab called "Polar orders".

/**
 * Polar -> Google Sheets: your organization's orders from the last 90 days.
 * Free, no add-on. Paste into Extensions > Apps Script in your spreadsheet.
 *
 * Setup:
 *  1. Polar dashboard > your organization's Settings > Developers > New Token.
 *     Pick an expiry date and select ONLY the orders:read scope.
 *  2. Apps Script: Project Settings (gear icon) > Script properties > add
 *     POLAR_TOKEN = that token.
 * Keeping the token in a script property, not in the code, means it isn't
 * visible to people you share the code with. Anyone who can edit the script
 * project can still read it, so keep that list short.
 *
 * Polar amounts are in cents; this writes them as 12.34 in each order's own
 * currency. Nothing is converted between currencies, and no totals are made.
 */
var DAYS = 90;
var SHEET_NAME = 'Polar orders';
var INCLUDE_EMAILS = false; // buyer emails are personal data; set to true only if you need them
var MAX_PAGES = 200;        // 100 orders per page, so up to 20,000 orders per run

function importPolarOrders() {
  var token = PropertiesService.getScriptProperties().getProperty('POLAR_TOKEN');
  if (!token) throw new Error('Add a POLAR_TOKEN script property first.');

  var since = new Date(Date.now() - DAYS * 24 * 60 * 60 * 1000).toISOString();
  var rows = [];
  var page = 1;
  var more = true;
  while (more && page <= MAX_PAGES) {
    var data = polarGet(
      'https://api.polar.sh/v1/orders/?limit=100&sorting=created_at' +
        '&created_after=' + encodeURIComponent(since) + '&page=' + page,
      token
    );
    (data.items || []).forEach(function (o) { rows.push(orderRow(o)); });
    var maxPage = (data.pagination && data.pagination.max_page) || 1;
    more = page < maxPage;
    page++;
    if (more) Utilities.sleep(300); // Polar allows 500 requests a minute; no need to rush
  }

  var header = ['Created (UTC)', 'Order ID', 'Invoice #', 'Product', 'Billing reason', 'Status',
    'Currency', 'Subtotal', 'Discount', 'Net (before tax)', 'Tax', 'Total', 'Refunded',
    'Tax refunded', 'Polar fee', 'Fee currency', 'Country', 'Customer email'];
  if (more) header[0] = 'Created (UTC) - STOPPED after ' + MAX_PAGES + ' pages; raise MAX_PAGES or DAYS';

  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getSheetByName(SHEET_NAME) || ss.insertSheet(SHEET_NAME);
  sheet.clearContents();
  sheet.getRange(1, 1, 1, header.length).setValues([header]);
  if (rows.length) sheet.getRange(2, 1, rows.length, header.length).setValues(rows);
}

/** One GET with a polite retry on 429 (honouring Retry-After) and 5xx. */
function polarGet(url, token) {
  for (var attempt = 1; attempt <= 4; attempt++) {
    var res = UrlFetchApp.fetch(url, {
      headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
      muteHttpExceptions: true
    });
    var code = res.getResponseCode();
    if (code === 200) return JSON.parse(res.getContentText());
    if (code === 429 || code >= 500) {
      var h = res.getAllHeaders();
      var wait = Number(h['Retry-After'] || h['retry-after']) * 1000;
      Utilities.sleep(Math.min(wait > 0 ? wait : 2000 * attempt, 60000));
      continue;
    }
    if (code === 401 || code === 403) {
      throw new Error('Polar rejected the token (HTTP ' + code + '). Check it has the orders:read scope and has not expired.');
    }
    throw new Error('Polar returned HTTP ' + code + ': ' + res.getContentText().slice(0, 200));
  }
  throw new Error('Polar is busy or unavailable after 4 tries. Try again in a few minutes.');
}

function orderRow(o) {
  var cents = function (v) { return typeof v === 'number' ? v / 100 : ''; };
  return [
    asText(o.created_at),
    asText(o.id),
    asText(o.invoice_number),
    asText(o.product && o.product.name),
    asText(o.billing_reason),
    asText(o.status),
    asText(String(o.currency || '').toUpperCase()),
    cents(o.subtotal_amount),
    cents(o.discount_amount),
    cents(o.net_amount),
    cents(o.tax_amount),
    cents(o.total_amount),
    cents(o.refunded_amount),
    cents(o.refunded_tax_amount),
    cents(o.platform_fee_amount),
    asText(String(o.platform_fee_currency || '').toUpperCase()),
    asText(o.billing_address && o.billing_address.country),
    INCLUDE_EMAILS ? asText(o.customer && o.customer.email) : ''
  ];
}

/** Text that starts with = + - @ is stored as plain text, never run as a formula. */
function asText(s) {
  s = s == null ? '' : String(s);
  return /^[=+\-@]/.test(s) ? "'" + s : s;
}
  1. In Polar, open your organization's Settings, scroll to Developers and click New Token. Give it a name and an expiry date, and select only the orders:read scope (Polar: Organization Access Tokens). The List Orders endpoint needs just that scope (Polar API: List Orders).
  2. Paste the script into the Apps Script editor and save.
  3. Project Settings → Script properties: add POLAR_TOKEN with your token.
  4. Select importPolarOrders and click Run. Google asks you to authorize the script the first time.
  5. To refresh it automatically, open Triggers (the clock icon) and add a daily time-driven trigger for importPolarOrders.

How it works: it calls GET https://api.polar.sh/v1/orders/ with your token as a Bearer header, 100 orders per page (the maximum), and keeps going until the last page that Polar's pagination.max_page reports (Polar API overview). Polar allows 500 requests a minute per organization and answers 429 with a Retry-After header if you go over (Polar), so the script waits a little between pages and honours that header. Polar sends amounts in cents, and the script divides by 100. Text that could run as a formula is stored as plain text. Buyer emails are left out unless you set INCLUDE_EMAILS = true.

Honest limits:

Gumroad or Lemon Squeezy instead? The same approach works with a few changes:

Best for: one platform, a little code, and wanting something free that you fully control.

3. A general API connector

Connector add-ons and data-pipeline services can call almost any REST API. We didn't find a ready-made Gumroad, Polar or Lemon Squeezy source in either of the two below, so you set up the request URL, the token header and the paging yourself, using the API details in option 2.

API Connector (Mixed Analytics)

A Google Sheets add-on for calling APIs, with pagination handling on every plan. Prices in USD (API Connector pricing):

Coupler.io (JSON importer)

Coupler.io's source list doesn't include Gumroad, Polar or Lemon Squeezy (Coupler.io pricing), and its pages for those integrations redirect to sign-up. Its JSON importer takes any API URL with custom headers and can refresh on a schedule (Coupler.io: JSON to Google Sheets). The Free plan has manual refresh only. Starter is $32/month ($24/month billed annually) with daily refresh and 5,000 rows per run. We didn't test whether it can follow each platform's paging.

Better fit than ours if you already use one of these for other APIs, or want to pull fields we don't. Watch out for: you build the requests, and the output is raw, so fees, refunds and monthly totals are formulas you write. Like any connector, it needs your token, so check how it stores it.

4. Zapier, Make or IFTTT

Automation tools react to events, for example "a new sale happened", and run an action such as "add a row to a sheet".

Zapier

Make

Make's own Gumroad app (maintained by Make) includes a List Sales module, so a Make scenario can fetch past sales as well as react to new events (Make: Gumroad). For Lemon Squeezy, Make lists community-built apps, some sold by third parties (Make: Lemon Squeezy). We didn't find a Polar app in Make's integrations directory. The Free plan includes 1,000 credits a month with a 15-minute minimum interval between runs (Make pricing).

IFTTT

There's a ready-made "Log Lemon Squeezy orders to Google Sheets" applet (IFTTT).

Better fit than ours if you want something else to happen on each sale too (a Slack message, an email-list tag), or you only need a running log of new sales. Watch out for: a "new sale" automation adds rows from the day you turn it on. It doesn't import your history, and when a sale is refunded later, the original row doesn't change unless you build that step as well.

5. Earnings by Small Rows (ours)

This is our product, so weigh what we say about it accordingly. Not released yet. It is built and tested against simulated APIs only, and still has to be tested with real accounts and pass Google's review before anyone can install it.

A Google Sheets add-on for exactly this job. You paste a token for each platform you sell on, and it builds a Sales tab with one row per order, a Monthly Summary by platform and currency, and, on Pro, a Tax Collected tab and a Gumroad Payouts reconciliation. After the first full pull, each sync re-reads the last 35 days to pick up refunds. Ko-fi and Payhip, which have no API for reading sales, can be added on Pro by importing their CSV exports.

Limits we want you to know: Lemon Squeezy's API has no fee field, so its fee is an estimate (the 5% + 50¢ base fee; the real fee can be higher with international, PayPal or subscription surcharges, per Lemon Squeezy: fees). The Ko-fi and Payhip column mappings haven't been checked against real exports. And as of 2026-10-05 the add-on has only been tested against simulated APIs.

Better fit than the others if you sell on two or three of these platforms, want your whole history with fees, refunds and tax in your own Google Sheet, and don't want to maintain a script. Not a fit if you need Stripe or Paddle, want charts or a phone app, need a tool with a track record and reviews, or only look at the numbers once a year (option 1 is free).

Product page & pricing · All limitations

Don't need a spreadsheet?

If what you want is a revenue dashboard rather than rows you can work with, there are apps built for that. A few that list Gumroad, Lemon Squeezy and (for some) Polar: SaneSales (free and open source, for Apple devices; Lemon Squeezy, Gumroad and Stripe), RevOne (a Mac app; Stripe, Gumroad, Lemon Squeezy, Polar and others), Owelet (a web dashboard; Stripe, Gumroad, Lemon Squeezy and more) and MyOpcs (a web dashboard; Stripe, Gumroad and Lemon Squeezy). We haven't tried them. Check their pricing pages, since plans change.

A note on Lemon Squeezy: in January 2026 its team said it is building Stripe Managed Payments and plans to give Lemon Squeezy users a way to migrate (Lemon Squeezy 2026 update). Whatever you build on its API, keep an eye on that.

Sources

All read on 2026-10-05. Prices are in US dollars as shown on each page, before any tax. Providers change plans often, so check the live page before you buy.

Found something wrong or out of date? Tell us via Support and we'll fix it and note the change here.