Shopify's Cost per Item: One Number, No History — and Why Your Margins Lie
Every Shopify variant has a field called Cost per item. It looks like a small piece of bookkeeping. It is actually the single number that decides whether your profit reports tell you the truth — and it has three structural limits that quietly break it.
Where the field lives and what it feeds
You'll find it in the admin under Products → (a product) → Pricing → Cost per item. Under the hood it isn't stored on the product at all — it lives on the variant's inventory item (in the API it's InventoryItem.unitCost), which is why it survives when you edit prices and why bulk tools address it separately from the price field.
Fill it in and Shopify gives you three things:
- a margin and profit figure right next to the price in the product editor;
- cost of goods sold in your analytics, which is what makes gross-profit reporting possible at all;
- profit-by-product breakdowns you can sort — the report most merchants use to decide what to push, discount, or drop.
Leave it empty and those reports simply don't exist for that product. That much is well known. The interesting part is what happens when the field is filled in but wrong.
Limit 1: one value, no history
A variant holds exactly one cost. When your supplier's price moves from $6.00 to $9.00 and you update the field, the $6.00 is gone. Not archived, not versioned, not visible anywhere in the admin — overwritten.
This matters more than it sounds. Nearly every useful cost question is a question about change over time:
- What did this part cost us in January, and what does it cost now?
- Which supplier has crept up 15% over the year while we weren't looking?
- Was Q1's margin drop a pricing problem or a cost problem?
None of those can be answered from Shopify alone. The platform is an operational system: it knows what things cost now. The trail — the thing that tells you a leak is happening — has to live somewhere else.
Limit 2: no supplier attribution
The cost field has no idea where the number came from. If you buy the same brake pad from two distributors depending on who has stock, Shopify holds one blended-or-last-one-wins figure with no record of which price list it came from, when, or at what quantity break.
So the question "which of my suppliers is getting more expensive?" — the one that actually changes purchasing decisions — has no answer inside the platform. You can only see the aggregate result, long after the fact, as a margin that got worse.
Limit 3: nothing updates it for you
This is the one that costs money. The cost field changes only when a human changes it. Suppliers, meanwhile, send new price files on their own schedule — monthly, quarterly, or "whenever the manufacturer says so", usually as a spreadsheet attached to an email nobody has time to diff.
The result is a gap. Between the day a supplier's price goes up and the day someone updates Shopify, every margin number you look at is optimistic. And because nothing is broken — no error, no warning, no red flag — the gap tends to be measured in months.
The subtle part: your reports don't fail loudly
Here's the trap that catches careful merchants. Shopify records the unit cost at the time of the order for reporting, so historical profit reports don't retroactively change when you update a cost. That sounds reassuring — it means your March numbers stay March's numbers.
But it also means the reports faithfully preserve whatever was in the field on the day of sale. If the field said $6.00 while you were actually paying $9.00, your March gross profit is permanently overstated in your own analytics, and nothing will ever correct it. The report isn't lying; it's accurately reporting a number you gave it.
Garbage in, permanently archived garbage out. This is why "we'll update costs when we get around to it" is a more expensive habit than it looks: you're not just delaying a data entry task, you're writing wrong history.
Two more ways the field goes stale
CSV imports that clear it
Product CSV imports overwrite what's in the file. If your import file includes the cost column but leaves cells empty — very common when a file has been round-tripped through a spreadsheet, or exported by a tool that doesn't carry cost — you can blank out costs in bulk without noticing. See our guide to bulk-updating cost per item safely before your next import.
Per-pack vs per-unit confusion
Supplier price lists often quote a case, a box of 12, or a per-100 price. Paste that straight into a per-unit field and your margin looks spectacular — or catastrophic — for reasons that have nothing to do with your business. More on that in reading supplier price lists without getting burned.
What good practice looks like
You can close most of the gap without any software, as long as you're disciplined:
- Export before you change anything. Products → Export gives you a CSV containing current costs. That export is your price history — Shopify won't keep one for you. Date the file and archive it.
- Diff each new supplier file against the last one. Not the whole catalog: just the lines where the price moved. On a few hundred SKUs a spreadsheet
VLOOKUPhandles it in ten minutes. - Recompute margin at your current retail, not at the retail you had when you set the price. The question is which SKUs just fell below your floor. (The formula is in this guide.)
- Update Shopify the same day so future orders record the right cost.
- Decide on the outliers deliberately — reprice, absorb, renegotiate, or drop. Doing nothing is also a decision; just make it knowingly.
The whole loop is maybe an hour per supplier file. The reason it doesn't happen isn't difficulty — it's that nothing prompts you. There's no notification when a price list changes, because the price list arrived as an email attachment and the change is invisible until you go looking.
Or let something watch it for you
CostSentry keeps the price history Shopify doesn't, diffs every new supplier file automatically, and emails you the SKUs whose margin just dropped — with the retail price you'd need to hold your margin.
Try the free margin checker ↗