Stocky has shut down. Recover what you can and replace it free
Pricing verified: 8 August 2026
Orders + inventory · Free via Make.com or Apps Script · No paid CSV app

Shopify to Google Sheets —
orders, inventory, and
the limits nobody mentions.

Shopify data reaches Google Sheets either through a Make.com webhook, which writes each order the moment it is paid, or through Apps Script on a schedule. The webhook route is real-time and free on Make.com's tier. Apps Script is free too, but caps executions at six minutes and daily triggers at 90 minutes.

Orders and inventory covered separately Real Make.com module structure, not a diagram Sourced Apps Script and Sheets API limits

Some links below are affiliate links (marked sponsored). They fund the free guides and never change a recommendation — our default pick on this page is a free tier. How we test & disclose →

Powered by Make.com — free up to 1,000 credits/month. For a full P&L layer on top of the same data, see Shopify P&L Automation.

$0
Both methods
2 webhooks
Orders + inventory
10M cells
Sheet ceiling
15 min
Full deployment
The base case

Exporting Shopify orders to Google Sheets

Every method below starts from the same trigger: a webhook firing the instant an order is paid.

When a customer completes checkout, Shopify can fire an orders/paid webhook — a JSON payload containing the order ID, line items, customer details, totals, discounts, and fulfilment status — to any URL you configure in Settings → Notifications → Webhooks. Everything downstream depends on that one event.

There are two free ways to turn that payload into rows in a spreadsheet:

Route 1 — Make.com (no code)

A Webhooks module receives the payload, a Google Sheets Add a Row module writes it. This is the fastest route to something working, and it's what the rest of this guide builds on — see the actual scenario structure below.

Route 2 — Google Apps Script (zero external tools)

A Web App deployed from inside the Sheet itself, listening for the same webhook via doPost(e). No Make.com account needed, but subject to Apps Script's own execution limits — covered in full in why Apps Script hits quota walls at volume.

function doPost(e) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Orders');
  var data = JSON.parse(e.postData.contents);

  data.line_items.forEach(function(item) {
    sheet.appendRow([
      data.id,
      data.created_at,
      data.email,
      item.sku,
      item.title,
      item.quantity,
      item.price,
      data.total_price,
      data.fulfillment_status || 'unfulfilled'
    ]);
  });

  return ContentService.createTextOutput('SUCCESS').setMimeType(ContentService.MimeType.TEXT);
}

Note the forEach over line_items — a three-item order should produce three rows, one per SKU, not one row per order. Most CSV export apps flatten this into a single row per order and lose per-product detail. Either free route above beats a $29–$99/month export app on cost; which one you pick depends on volume and whether you want a Make.com account in the loop at all.

Sync strategy

Real-time vs scheduled sync — and when each is appropriate

Not every sheet needs a webhook. Some need a nightly pull instead.

Real-time
Webhook-triggered
Shopify fires the moment an event happens. The sheet updates within seconds. Appropriate when staleness has a real cost: live inventory, fulfilment dashboards, a P&L you check daily.
Shopify webhook → Make.com / Apps Script
Scheduled
Polling / batch pull
A time-driven trigger calls the Shopify Admin API on an interval and pulls everything since the last run. Higher latency, but far fewer calls, and the natural safety net for anything a webhook missed.
Apps Script time trigger / Make.com scheduled scenario
Hybrid — recommended
Both, together
Webhooks for speed, plus a daily scheduled reconciliation pull that catches anything missed. This is the pattern this guide's setup steps build.
Two triggers, one sheet
Why a reconciliation pass matters

Shopify's webhook delivery is not guaranteed — a brief outage on your endpoint, a Make.com scenario error, or an Apps Script quota rejection during a burst can silently drop an event. A webhook-only setup has no way to notice this happened. A nightly scheduled pull comparing order counts against Shopify's own count is the cheap fix, and it's the single most commonly skipped step in DIY Shopify-to-Sheets builds.

Inventory

Syncing inventory levels to Sheets

A different webhook, a different write pattern, and a lookup problem orders don't have.

Inventory sync is not the same problem as order export, for three specific reasons:

  • Different trigger. Stock changes fire the inventory_levels/update webhook, not orders/paid — and it fires per SKU per location. One order touching three variants across two locations can trigger up to six separate inventory events.
  • The payload doesn't include a product name. It carries inventory_item_id and location_id, both opaque numeric IDs. You need a lookup — either a cached mapping tab you build once via the Shopify API, or a live API call per event — to turn that into a readable SKU.
  • You want one row per SKU, not an endless log. Appending a new row on every stock change (the same pattern that's correct for orders) turns an Inventory tab into an unreadable history instead of a current-state view. Inventory needs an update, keyed on SKU + location, not an append.
This is what replaces Stocky

A webhook-driven, update-in-place inventory tab is exactly the mechanism behind Stocky Swap, built for the same August 2026 Stocky shutdown this pattern is a free replacement for. If inventory tracking is your primary goal rather than a side effect of order export, that guide covers the dedicated build in more depth.

Where Apps Script breaks

Why Apps Script hits quota walls at volume

The specific limit you hit depends on which sync method — real-time or scheduled — you're running.

Apps Script's limits are fixed at the infrastructure level and cannot be raised by upgrading — Google Workspace raises some daily ceilings, not the core ones. Two different limits bite depending on the sync pattern from the section above:

Webhook listener (real-time route)

A doPost() web app is capped at 30 concurrent executions and the underlying Sheets service call rate limit. A flash sale generating several orders within the same second causes competing executions to queue or fail — surfacing as "Service invoked too many times." The 6-minute single-execution ceiling rarely matters here since one order write is fast; concurrency is the real constraint.

Scheduled pull (batch route)

A time-driven trigger is bound by the 90-minute daily trigger runtime cap on consumer accounts (6 hours on Workspace). A growing store's nightly reconciliation pull can eat into this budget as order volume climbs, especially if it re-fetches more history than necessary on each run.

90 min

Daily trigger runtime ceiling on free Google accounts — the number that actually matters for a scheduled Shopify sync, as distinct from the better-known 6-minute per-execution limit. Full breakdown of every quota, error message, and the structural fix: Google Apps Script Quotas Explained.

A separate ceiling

Google Sheets API limits you'll hit even with Make.com

Moving off Apps Script doesn't remove every limit — it removes a different one.

Make.com doesn't run inside Apps Script's execution environment, so the Apps Script quotas above don't apply to it. But every write it makes still goes through the Google Sheets API, which enforces its own independent quota. Per Google's published Sheets API usage limits: 300 read requests and 300 write requests per minute per project, with a 60-per-minute cap per user, and no daily limit.

A typical store's order volume is nowhere near this. It becomes relevant in two specific cases: a scenario writing one API call per line item on large multi-item orders during a traffic spike, or an Iterator module (see the scenario structure below) misconfigured to make far more calls than the order data actually requires. Hitting it returns a 429: Too many requests — Make.com's built-in retry with backoff handles this automatically, but it's worth knowing the ceiling exists independently of anything Apps Script imposes.

The actual build

The Make.com approach — the actual scenario structure

Two scenarios, not one — orders and inventory write differently, so they don't share a module chain.

Scenario 1 — Orders (append)

Webhooks (Custom webhook)Iterator on order.line_itemsGoogle Sheets: Add a Row, mapped to the Orders tab. The Iterator is the part most tutorials skip — without it, a Google Sheets module fed the raw payload writes one flattened row per order, discarding per-SKU detail. With it, a three-item order correctly produces three rows.

Scenario 2 — Inventory (update-in-place)

A separate Webhooks trigger on inventory_levels/updateGoogle Sheets: Update a Row (Search Column), keyed on SKU + Location rather than appended. This is a different module than Scenario 1 uses — "Add a Row" is wrong here; it would turn every stock change into a new row instead of updating the current one.

Scenario 3 — Scheduled reconciliation (optional but recommended)

A Scheduler trigger, once daily → an HTTP call to the Shopify Admin API for the last 24 hours of orders → a Filter comparing against what's already in the sheet → Add a Row for anything missing. This is the safety net from the sync-timing section — cheap to build, and the only thing that catches a webhook Shopify sent but your endpoint never received.

Sheet structure

The Google Sheet structure to build

Two tabs, two different write patterns — this is the layout the scenarios above assume.

Tab 1 — Orders (append-only, one row per SKU)

Orders Inventory
ABCDEFGHI
Order_IDDateCustomer_EmailSKUProduct_TitleQuantityLine_PriceOrder_TotalFulfil_Status
webhookwebhookwebhookiteratoriteratoriteratoriteratorwebhookwebhook

Tab 2 — Inventory (update-in-place, one row per SKU + location)

Orders Inventory
ABCDE
SKUProduct_TitleLocationAvailable_QtyLast_Updated
keylookupkeywebhookwebhook

Columns A and C in Inventory together form the search key Make.com's "Update a Row (Search Column)" module matches against — the same SKU at two locations needs two rows, not one.

Setup guide

Deploy it — step by step

Roughly 15 minutes for both scenarios. No developer required.

1
Create your Google Sheet
Create a Google Sheet with two tabs: Orders and Inventory, using the column structure above. In Orders: Order_ID, Date, Customer_Email, SKU, Product_Title, Quantity, Line_Price, Order_Total, Fulfil_Status. In Inventory: SKU, Product_Title, Location, Available_Qty, Last_Updated.
sheets.google.com — new sheet, 2 tabs
2
Activate Make.com free
Sign up for Make.com free — 1,000 credits/month at no cost, no credit card required.
make.com/en/register
3
Add the order payment webhook and Iterator
In Shopify Admin → Settings → Notifications → Webhooks, create a webhook for Order payment pointing at your Make.com scenario's webhook URL. In Make.com, add an Iterator module on order.line_items, then a Google Sheets — Add a Row module mapped to your Orders tab.
Shopify Admin → Webhooks → Order payment
4
Add the inventory levels webhook
Create a second Shopify webhook for Inventory levels update. Build a second Make.com scenario: Webhooks trigger → Google Sheets — Update a Row (Search Column), searching on SKU and Location together, writing to your Inventory tab.
Shopify Admin → Webhooks → Inventory levels update
5
Add a scheduled reconciliation pull (optional)
Add a third scenario on a daily Scheduler trigger that calls the Shopify Admin API for the last 24 hours of orders, filters against what's already in the sheet, and adds any rows that are missing. This catches anything the webhook missed.
Make.com Scheduler — daily
6
Test and verify
Place a test order with two different products — confirm two rows land in Orders, not one. Adjust stock on a variant in Shopify Admin — confirm the matching Inventory row updates in place rather than adding a duplicate.
Test order + manual stock edit
Start building free on Make.com — 1,000 ops/month →
The ceiling

When you've outgrown Sheets entirely

Three real signals, not a vague "eventually."

  • Approaching the cell ceiling. Google Sheets caps a spreadsheet at 10 million cells total across every tab (Google announced a 20-million-cell beta in April 2026, but 10M is the standing limit). A 9-column Orders tab hits that around 1.1 million rows.
  • Formula slowdown, well before the cell ceiling. SUMIF and VLOOKUP ranges recalculating across tens of thousands of rows visibly lag in the UI long before you're anywhere near 10 million cells — this is usually the first real symptom, not the row count itself.
  • Concurrent-write friction. Sheets was not built for high-frequency transactional writes from multiple sources. A busy store running order writes, inventory updates, and a reconciliation pass through the same spreadsheet can start to see write conflicts or queuing under sustained load.

When any of these show up consistently, the fix isn't a bigger spreadsheet — it's moving the system of record off Sheets. BigQuery is the natural next step if you're already in the Google ecosystem; a hosted Postgres instance or Airtable base works too. Keep Sheets as a lightweight view or export target on top, rather than the primary store, if you still want the formula-based reporting this guide is built around.

Common questions

Frequently asked questions

Can I export Shopify orders to Google Sheets for free?

Yes. A Shopify Order payment webhook connected to a Make.com scenario writes every order to a Google Sheet automatically, free on Make.com's tier covering 1,000 credits/month. Google Apps Script is a second free route that runs entirely inside the Sheet itself, at the cost of the quota limits Apps Script imposes at higher volume.

Should I use real-time webhooks or a scheduled sync?

Use real-time webhooks for anything where staleness has a cost — inventory levels, live P&L, fulfilment dashboards. Use a scheduled pull for bulk backfills and as a reconciliation safety net, since Shopify webhook delivery is not guaranteed. Most reliable setups run both.

How do I sync Shopify inventory levels to Google Sheets?

Subscribe to the Inventory levels update webhook, which fires per SKU per location. The payload contains inventory_item_id and location_id rather than a product name, so you need a lookup step. Write with an update-in-place module keyed on SKU + location rather than appending, or the sheet fills with duplicate rows instead of current stock.

Why does my Google Apps Script Shopify sync stop working at volume?

Depends on the sync method. A webhook-triggered web app hits the 30-concurrent-execution ceiling first, surfacing as "Service invoked too many times" during order bursts. A scheduled pull instead runs into the 90-minute daily trigger runtime cap as volume grows. Neither can be raised by upgrading — see the full quotas guide.

Does Make.com also have rate limits when writing to Google Sheets?

Yes — the Google Sheets API itself, independent of Apps Script: 300 read and 300 write requests per minute per project, 60 per minute per user, per Google's published limits. Unlikely on typical order volume, but a misconfigured Iterator can reach it.

What's the actual Make.com scenario structure for this?

One scenario per webhook topic. Orders: Webhooks → Iterator on line_items → Add a Row, one row per SKU. Inventory: a separate Webhooks trigger → Update a Row (Search Column) keyed on SKU + location. Append for orders, update-in-place for inventory — different modules, different write patterns.

How many orders can Google Sheets handle before I need a database?

Sheets caps a spreadsheet at 10 million cells total (a 20-million-cell beta was announced in April 2026). A 9-column Orders tab hits that around 1.1 million rows, but formula recalculation lag usually shows up in the low hundreds of thousands of rows, well before the cell ceiling. At that point BigQuery or a hosted database replaces Sheets as the system of record.

Do I need Google Workspace for this?

No. Free Gmail works for most stores. Google Workspace raises the Apps Script daily trigger-runtime ceiling and UrlFetch call limits — relevant only if you're running the Apps Script route at high volume. The Google Sheets API limits Make.com uses are the same regardless of account type.

Luke Sandelands
Written by Luke Sandelands
Founder, Stack Architect · Shopify Automation Specialist
Developer of StockLog and author of the open-source shopify-capi-validator npm package. Certified in Shopify server-side tracking, Meta CAPI, and Google Apps Script engineering.
Full profile and credentials · Last reviewed

Related App Alternatives