Sales by product, collection or tag
get_sales_byProduct sales for the period grouped by product, product type, collection, tag or sales channel, optionally narrowed to some collections, product types, tags, title words or one sales channel. Use for any question the fixed product tools cannot answer: 'how much did the Special Prices collection sell this week', 'sales by tag', 'online vs in-store by product', 'how is product X doing' (search), or any product beyond the top ten. Per row: revenue (without VAT, before returns), returns (an ESTIMATE per group: each refund is split pro rata across its order's lines because Shopify's refund lines are not stored per product, so write 'unas', 'aproximadamente' or 'estimated' for a group's returns and its return rate, never as exact), netSales (= revenue − returns), units, orders, cost, grossProfit and marginPct (null = costs unknown, never zero), share of the filtered total, and previousNetSales with changePct against the comparison period. total counts every matching line ONCE; storeNetSales is every product line in the period, and filteredShare = total.netSales ÷ storeNetSales — that IS the answer to 'what part of sales came from X', quote it rather than dividing. overlapping true (collection, tag) = a product counts in each of its collections or tags, so rows can add up to more than the total: never sum them. A row with an empty label is 'none' (no collection, no tag, no type). collections.unmatched lists names that matched no collection and collections.available the store's real ones: retry with one, or say it does not exist. partialCollections = collections Nuira holds only part of the products of (Shopify lists more), so their figures are a floor: say so. Membership is the collection's current one, not as of each order. Revenue here is product line revenue, which is why it differs from get_summary's order revenue. Optional from and to (inclusive YYYY-MM-DD, both or neither, never after today) read another span instead of the default period. A named month, a week or two dates the operator gave MUST be sent here: without them this reads the last 30 days, which is not that month.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| by | Yes | How to group the sales: product, productType (Shopify's product type), collection, tag, or salesChannel (online, pos, …). By product, every row also carries `stock`: unitsNow, variants, variantsOutNow (tracked variants at 0 now), untrackedVariants (not counted by Shopify, never 'sold out') and variantsOutDuring (variants seen at 0 during the span; observations start at stockObservedSince, null before) — the read for 'do the products that sell have stock?'. | |
| to | No | Last day of the span, inclusive, as YYYY-MM-DD; not after today. Send together with `from`, or omit both to read the default 30-day period. | |
| dir | No | desc (default, biggest first) or asc (smallest first). | |
| from | No | First day of the span, inclusive, as YYYY-MM-DD. Send together with `to`, or omit both to read the default 30-day period. | |
| sort | No | Row order: netSales (default), units, orders, changePct, returns (dir desc lists what is returned most; estimates per product), or netSalesChange (with compare: the money change, so dir asc lists the biggest falls first and desc the biggest rises). | |
| tags | No | Only products with these tags, any of them. | |
| limit | No | Rows to return, 1 to 50; default 20. | |
| search | No | Only products whose title contains these words. | |
| compare | No | Compare every row with this period: previous (default), yoy (same dates last year) or yoyWeekday (52 weeks back). When given, each row also carries lastPeriod (the comparison's revenue, returns, units and orders), netSalesChange, revenueChange, returnsChange and returnRate (returns ÷ revenue in both periods, change in points; null in a period where refunds passed sales, which moreReturnedThanSold names as current, previous or both), volumeEffect and priceEffect (revenueChange from selling more or fewer units, and from price, discounts and mix), and shareOfChange (the row's part of the whole set's change in net sales, can pass 100% or be negative when other rows moved the other way), and totalComparison compares the whole filtered set: revenue, returns, netSales, units, orders, pricePerUnit (revenue per unit sold), volumeEffect, priceEffect and returnRate — quote them instead of subtracting. | |
| compareTo | No | Last day of the named comparison period, YYYY-MM-DD. | |
| collections | No | Only products in these collections (titles or handles; a part of the title is enough), any of them. | |
| compareFrom | No | First day of a NAMED comparison period, YYYY-MM-DD, with compareTo. For 'September with October': from/to October, compareFrom/compareTo the whole of September. Takes the place of compare. | |
| productTypes | No | Only these product types, any of them. | |
| salesChannel | No | Only online orders, or only point-of-sale orders. |