Guide · 6 min read

Automate Your Shopify Orders to Google Sheets (No Code, No Zapier)

Every Monday morning, somebody on your team downloads the past week's Shopify orders as a CSV, opens Google Sheets, and pastes them in by hand. Most shops do this without thinking about it. At an hour per week, that's fifty hours a year — the kind of cost nobody notices until they suddenly do. This guide walks through a webhook-based fix any small business can build and own.

Quick answer

A Shopify webhook fires the moment an order is placed. A tiny Google Apps Script catches the JSON, picks out the columns you care about, and appends them as a row in a Google Sheet you already share with your team. No Zapier subscription, no cron job, no exporting.

Six DIY steps below. Total time: about an hour the first time, ten minutes on any rebuild after that.

The Sunday-night export — what it actually costs

The "weekly CSV export" starts as a small habit. The first few weeks it's fine. Within a quarter it's a Monday-morning ritual — usually owned by whoever's most senior on the operations side that week, because it's the kind of task nobody wants to take.

If you pay the bill honestly: roughly an hour each week to export the CSV, open the destination sheet, paste/append the rows, fix the headers Shopify keeps shuffling, and double-check that the totals match. That's about fifty hours a year, before counting the questions — "wait, is this week's CSV the same shape as last week's?" — that always come up after.

An hour a week to keep a spreadsheet in sync isn't a one-time setup. It's a recurring invoice that nobody ever approves.

The webhook is the fix, not Zapier

What Zapier sells you, the underlying mechanism can do for free: an HTTP request, fired by Shopify the moment something happens, hitting an endpoint you control.

Shopify's webhook system lets you subscribe to moments like orders/create (new order placed), orders/paid (payment cleared), or orders/fulfilled (shipped). The instant one of those events fires, Shopify sends a JSON payload describing the order to a URL of your choosing. Every webhook hits the same URL once. If your endpoint returns a 200 OK, Shopify marks the delivery successful and doesn't retry.

That URL can be a Zapier endpoint and cost you $20 a month. It can also be a Google Apps Script Web App, which costs nothing and runs on Google's servers. The setup below uses the second one.

Step 1 — Get a Google Cloud project and turn on the Sheets API

This step is the worst part of the whole guide — you'll touch a Google Cloud screen you've probably never used before. It takes five minutes and you only do it once.

Open console.cloud.google.com, create a new project (any name — "Shopify Orders" works), then in the left sidebar choose "APIs & Services" → "Library" → search for "Google Sheets API" → Enable. Do the same for "Google Drive API" (the Apps Script needs Drive to open the spreadsheet by ID).

Next, "APIs & Services" → "Credentials" → "Create Credentials" → "Service Account." Give it any name. Once it's created, click into it → "Keys" → "Add Key" → "Create new key" → JSON. A file downloads — that's your auth key. Save it somewhere safe; you'll upload it in a moment.

Finally, share your destination Google Sheet (from Step 2) with the service account's email address (it looks like something@project.iam.gserviceaccount.com) as an Editor. Without this step the script will return "permission denied" and stop here.

Step 2 — Make a destination Google Sheet and copy its ID

Open Google Sheets, create a new workbook. Name the first tab orders (or whatever you want — just remember the exact name for the script). In row 1, leave the sheet empty: the script writes its own header row on the first run so the column order stays in sync no matter how the human sorts the sheet.

The "Sheet ID" is the long string in the spreadsheet URL between /d/ and /edit — something like 1aBcD...XyZ. Copy that. You paste it into the SPREADSHEET_ID constant in the script below.

Step 3 — Deploy the Apps Script below as a Web App

Open script.google.com, "New Project." Delete the placeholder function myFunction() and paste the script below. Replace SPREADSHEET_ID with the ID you copied in Step 2. Save.

Then click "Deploy" → "New deployment" → type "Web app." Under "Execute as" pick your Google account (not the service account — this is the human who owns the script). Under "Who has access" pick "Anyone." Click Deploy. You'll be asked to authorize the script — accept.

Copy the deployment URL — it ends in /exec. That URL is what Shopify will POST to.

Google Apps Script — paste into script.google.com
// SPREADSHEET_ID — copy from the Sheet URL: .../d/THIS_PART/edit
const SPREADSHEET_ID = 'PASTE_SHEET_ID_HERE';
// SHEET_NAME — tab name at the bottom of the workbook (default = "Sheet1")
const SHEET_NAME = 'orders';
// HEADERS — column order, kept stable so rows stay aligned
const HEADERS = [
  'order_id',     // unique Shopify order id, primary key
  'created_at',   // ISO timestamp from Shopify (e.g. "2026-07-16T14:32:11Z")
  'email',        // customer email
  'total_price',  // string-encoded decimal ("89.00"); parseFloat to compare
  'item_count',   // number of distinct line items, from line_items.length
  'item_titles'   // comma-joined product titles from line_items[].title
];

// doPost — Shopify webhooks POST JSON. Append a row, return 200 so Shopify doesn't retry.
function doPost(e) {
  // Parse the JSON payload Shopify sends on orders/create.
  const order = JSON.parse(e.postData.contents);
  // Build the row in HEADERS order so columns stay aligned.
  const row = [
    order.id,
    order.created_at,
    order.email,
    order.total_price,
    order.line_items.length,
    order.line_items.map(li => li.title).join(', ')
  ];
  // Open the destination sheet, locked to the named tab.
  const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
  // Write header row if the sheet is empty (first run only).
  if (sheet.getLastRow() === 0) sheet.appendRow(HEADERS);
  // Append the new order row beneath whatever is already there.
  sheet.appendRow(row);
  // Return 200 OK with a JSON body so Shopify marks delivery successful.
  return ContentService.createTextOutput(JSON.stringify({ok: true}))
    .setMimeType(ContentService.MimeType.JSON);
}

Step 4 — Point a Shopify webhook at the deployed URL

Open your Shopify admin → Settings → Notifications → scroll to the bottom ("Webhooks") → Create webhook. Event: Order creation. Format: JSON. URL: the deployment URL from Step 3 (the one ending in /exec). Webhook API version: latest stable.

Save. Shopify will send a test payload — if the Apps Script is set up correctly, you should see a single test row land in your sheet within a few seconds.

Step 5 — Test with one real order, then turn it on

In the Shopify webhook screen, "Send test notification" once more to confirm the wiring. Then place a test order through your own storefront (use the discount code "TEST" if you have one, or just place a $1 order and refund it).

The row should appear in your sheet within a few seconds of order placement. If it doesn't, the most likely culprits are (in order): the SHEET_NAME in the script doesn't match the actual tab name; the service account from Step 1 was never added as an Editor on the sheet; or the Apps Script deployment was set to "Execute as" the service account instead of your human account.

Step 6 — Watch the row land on the next real order

That's it. The watcher is on. The next real order on your store produces a row in your sheet within a few seconds — order id, timestamp, customer email, total, item count, item titles.

What to put in each column (the longer list, for when you're ready to extend the script):

order_id lets you dedupe rows if a webhook is ever re-delivered. created_at is the Shopify ISO timestamp — wrap it in ARRAY_SPLIT(left(cell,10),"-") in Sheets if you want a real date column. email is the customer's order email. total_price is a string-encoded decimal (wrap with VALUE() in Sheets if you want to add it up). item_count is just a number. item_titles is one cell with titles comma-separated — split it with SPLIT(cell,", ") if you want one row per item.

Once it's running for a week, you can do everything else (pivot tables, weekly totals, cohort analysis) inside Sheets without ever opening Shopify's export screen again.

Where DIY falls apart

Four ways it breaks within a year — each costing more than $89 in someone's afternoon:

The service-account auth key expires and the sheet quietly stops growing. A flash sale hits Shopify's API throttle and Google's write limit at the same time, leaving a partial log with a missing row. Shopify renames a payload field — they've done it more than once — and the script crashes on the rename. Someone reorders the columns and there's no error, just a week of wrong totals.

If that's where your team is — wanting it just working and forgotten — see it running in the Shopify-to-Sheets demo on the homepage.

When DIY isn't enough

Hand the export back. $89 flat.

We automate one business process for $89 — same-day fix or your money back. No retainer, no monthly fee. You describe what's broken, Nikita builds the fix.

Not sure what counts? See the Shopify-to-Sheets demo running on the homepage.

Start — $89

OneFix is a small automation service for small businesses — we don't sell Shopify apps or Zapier replacements. If you'd rather keep the DIY guide, bookmark this page. Back to onefix.tech.