HomeShopify & E-commerceShopify Weekly Best Sellers Report With…

Shopify Weekly Best Sellers Report With n8n

Shopify Weekly Best Sellers Report With n8n










A Shopify weekly best sellers report in n8n pulls last week’s orders, ranks your products by units sold, writes the top ten to a Google Sheet, and emails you the list every Monday morning. It runs on any paid Shopify plan with a free n8n instance, needs no analytics add-on, and takes about 35 minutes to build. Below is the full node-by-node build, the exact Code node logic, common mistakes, and real running costs.

What it does

Every Monday at 8am, this workflow asks Shopify for all orders from the previous seven days, adds up how many units each product sold, and ranks them. The top ten land in a running Google Sheet so you build a week-over-week history, and the same list arrives in your inbox as a clean HTML table. You open your email and immediately know what moved, without logging into the admin or clicking through the Analytics tab.

It is a small piece of the larger picture covered in our n8n Shopify automation guide: instead of pulling reports by hand, you let a scheduled workflow deliver the numbers you actually act on. Best-seller data drives restock decisions, ad budget, and which products deserve a homepage slot, so having it pushed to you beats remembering to go fetch it.

Why it beats the default

Shopify already shows best sellers under Analytics, so why build this? Three reasons.

  • It comes to you. A report you have to remember to open is a report you stop reading after week two. Email plus a Sheet means the numbers show up whether or not you log in.
  • It builds history you own. The native dashboard is a live view. This workflow appends every week to a Google Sheet, so after a couple of months you can chart trends, spot a product fading, and pivot a table however you like.
  • It is yours to shape. Want the top 20, a revenue ranking, only a single collection, or the report sent to three people? Those are one-line edits here. The built-in dashboard gives you none of that control.

The native report is fine for a quick glance. This is for the owner who wants the data delivered, logged, and reshaped on their terms.

What you need

  • An n8n instance, cloud or self-hosted. A free self-hosted setup is enough.
  • A Shopify store on any paid plan, plus an app access token with read_orders and read_products scopes.
  • A Google account for Google Sheets, connected to n8n by OAuth2.
  • A Gmail account for the email, also connected by OAuth2.
  • A blank Google Sheet with a header row: Week Of, Rank, Product, Variant, Units Sold, Revenue.

Build time: about 35 minutes from scratch, or under 10 minutes with the ready-made template.

Node-by-node list

Six nodes, one clean line with a short branch at the end so the Sheet gets every row while the email gets one message.

┌───────────────────────────────────────────────────────────────┐
│  SHOPIFY WEEKLY BEST SELLERS REPORT                            │
│                                                               │
│  [Schedule Trigger]  weekly, Mon 08:00                        │
│         ↓                                                     │
│  [Shopify: Get Many Orders]  created in last 7 days           │
│         ↓                                                     │
│  [Code: Rank Best Sellers]  aggregate + sort + top 10         │
│         ├──────────────→ [Google Sheets: Append]  all rows    │
│         ↓                                                     │
│  [Limit: Keep 1]  →  [Gmail: Send Report]  one email          │
└───────────────────────────────────────────────────────────────┘
  
  1. Schedule Trigger (n8n-nodes-base.scheduleTrigger) — fires weekly, Monday 08:00.
  2. Shopify (n8n-nodes-base.shopify) — Get Many orders created in the last 7 days.
  3. Code (n8n-nodes-base.code) — ranks products by units sold and builds the report HTML.
  4. Google Sheets (n8n-nodes-base.googleSheets) — appends one row per top product.
  5. Limit (n8n-nodes-base.limit) — reduces the ranked list to a single item for the email.
  6. Gmail (n8n-nodes-base.gmail) — sends the HTML best-sellers report.

Step-by-step build

1 Schedule Trigger

Add a Schedule Trigger. Set the interval field to Weeks, trigger day to Monday, and trigger hour to 8. This gives you a fresh report at the start of every week, covering the seven days that just closed.

💡

Tip: Set your n8n instance timezone under Settings so 08:00 means 08:00 in your store’s time, not UTC.

2 Shopify — Get Many Orders

Add a Shopify node. Set resource to Order and operation to Get Many. Turn on Return All. Under Options, add Created At Min and set it to an expression that resolves to seven days ago:

={{ $now.minus({ days: 7 }).toISO() }}

Also add the Status option and set it to Any so you capture every order, then let the Code node decide what counts. Each order returned carries a line_items array, which is what we rank on.

📌

Create your Shopify credential from an app in the Shopify Developer Dashboard (dev.shopify.com). Give it read_orders and read_products Admin API scopes.

3 Code — Rank Best Sellers

Add a Code node (Run Once for All Items, JavaScript). This is the heart of the workflow. It flattens every line item, sums units and revenue per product, sorts, keeps the top ten, and builds one HTML report attached to every row.

// Aggregate best-selling products from the past week's orders
const stats = {};

for (const item of $input.all()) {
  const order = item.json;
  if (order.cancelled_at) continue; // skip cancelled orders
  for (const li of order.line_items || []) {
    const key = li.product_id + '|' + (li.variant_title || '');
    if (!stats[key]) {
      stats[key] = { product: li.title, variant: li.variant_title || '—', unitsSold: 0, revenue: 0 };
    }
    stats[key].unitsSold += li.quantity;
    stats[key].revenue  += li.quantity * parseFloat(li.price);
  }
}

const weekOf = $now.minus({ days: 7 }).toFormat('LLL d')
  + ' – ' + $now.toFormat('LLL d, yyyy');

const ranked = Object.values(stats)
  .sort((a, b) => b.unitsSold - a.unitsSold)
  .slice(0, 10)
  .map((p, i) => ({ rank: i + 1, weekOf, ...p, revenue: Math.round(p.revenue * 100) / 100 }));

const rows = ranked.map(p =>
  `<tr><td>${p.rank}</td><td>${p.product}</td><td>${p.variant}</td>`
  + `<td>${p.unitsSold}</td><td>$${p.revenue.toFixed(2)}</td></tr>`
).join('');

const reportHtml = `<h2>Best sellers — ${weekOf}</h2>`
  + `<table border="1" cellpadding="6" cellspacing="0">`
  + `<tr><th>#</th><th>Product</th><th>Variant</th><th>Units</th><th>Revenue</th></tr>`
  + `${rows}</table>`;

return ranked.map(p => ({ json: { ...p, reportHtml } }));

After this node runs on a sample week, one output item looks like this:

{
  "rank": 1,
  "weekOf": "Jun 23 – Jun 30, 2026",
  "product": "Cedar Wax Melts",
  "variant": "Vanilla Bourbon",
  "unitsSold": 214,
  "revenue": 2461.50,
  "reportHtml": "<h2>Best sellers — Jun 23 ... </table>"
}
💡

Tip: To rank by money instead of units, change the sort to b.revenue - a.revenue. To show 20 products, change .slice(0, 10) to .slice(0, 20).

4 Google Sheets — Append

Add a Google Sheets node. Set operation to Append, pick your document and sheet, and set mapping to Map Each Column Manually. Map the columns to the fields from the Code node:

Sheet column Value
Week Of ={{ $json.weekOf }}
Rank ={{ $json.rank }}
Product ={{ $json.product }}
Variant ={{ $json.variant }}
Units Sold ={{ $json.unitsSold }}
Revenue ={{ $json.revenue }}

Because the Code node returns ten items, this node appends ten rows in one run, one per top product. Over the weeks the sheet becomes your best-seller history.

5 Limit — Keep 1

Connect a second line from the Code node into a Limit node and set Max Items to 1. The email should be a single message, not ten. Because every ranked item carries the identical reportHtml, keeping just the first item still gives you the whole table.

📌

Draw the Limit connection from the Code node, not from Google Sheets. Both branches then read the full ranked data directly.

6 Gmail — Send Report

Add a Gmail node, operation Send. Set the recipient to your address, the subject to an expression, and the message to the report HTML with the email type set to HTML:

  • Subject: =Best sellers this week {{ $json.weekOf }}
  • Message: ={{ $json.reportHtml }}

Save the workflow and toggle it Active. Next Monday at 8am the report lands on its own.

Common mistakes

  • Wiring Limit after Google Sheets. The append node’s output does not carry the report HTML, so the email comes out empty. Branch Limit straight off the Code node instead.
  • Leaving the timezone on UTC. The schedule then fires at the wrong local hour and Created At Min covers a shifted window. Set the instance timezone once.
  • Forgetting read_products scope. Orders return but some fields are thin. Grant both order and product read scopes on the app.
  • Counting cancelled orders. Without the cancelled_at skip, a large cancelled order can crown a false best seller. The Code node handles this, so do not remove that line.
  • Not turning on Return All. With it off you only get the first page of orders and undercount every product in a busy week.

Cost at realistic volume

This workflow uses no paid AI and no metered messaging, so the running cost is close to zero.

Component Cost Notes
n8n (self-hosted) $0 One weekly execution; well inside any free setup.
n8n Cloud From ~$20/mo Four executions a month barely touches the quota.
Shopify Admin API $0 Included on every paid Shopify plan.
Google Sheets $0 Free with any Google account.
Gmail $0 Four emails a month is nothing.

Even at thousands of weekly orders the only variable is how long the Shopify pull takes, which costs you time, not money. Compare that to a paid analytics app charging a monthly fee for a view you can generate yourself.

Get the Shopify Best Sellers Report, done for you

The guide above is free to follow. If you would rather skip the build, download the ready-to-import workflow, then just add your credentials. Prefer it installed and tuned for you? See our done-for-you services.

Download the template ($12) →

Instant download · Works on n8n Cloud and self-hosted

Frequently asked questions

Does this Shopify best sellers report work on the Basic plan?

Yes. The workflow reads orders through the standard Shopify Admin API, which every paid Shopify plan exposes. You only need an app access token with read_orders and read_products scopes. There is no dependency on Shopify Plus or any paid analytics add-on.

Can I rank by revenue instead of units sold?

Yes. In the Code node, change the sort comparator from b.unitsSold - a.unitsSold to b.revenue - a.revenue. The report already tracks both fields, so revenue ranking is a one-line change and needs no new nodes or extra data pull.

How many orders can the workflow handle in one run?

The Shopify node paginates automatically when Return All is on, so it will pull thousands of weekly orders. For very high volume stores, request fewer fields and raise your n8n execution timeout so a large pull has room to finish cleanly.

Will cancelled or refunded orders skew the ranking?

The Code node skips any order with a cancelled_at value, so cancellations are excluded. Refunds are not deducted line by line in this version, but you can subtract refund quantities inside the same loop if your store refunds often enough to matter.

Can I send the report to Slack or Telegram instead of email?

Yes. Swap the Gmail node for a Slack or Telegram node and pass the same data. Telegram wants Markdown or HTML parse mode, so send a short text summary rather than the full HTML table for the cleanest result in a chat window.

Related guides

n8n
Shopify
Google Sheets
Gmail
automation