How to track expiry dates in Shopify

Three methods, what each one can tell you, and where each one stops.

First, what you are actually trying to store

An expiry date is not a property of a product. It is a property of a batch. Two deliveries of the same SKU, arriving five weeks apart, carry different dates and different quantities, and both can be sitting on the shelf at the same time. So the thing you need to record is a row per batch — variant, lot code, expiry date, quantity, and usually the supplier — rather than a single field per product.

That is precisely where Shopify's inventory model does not meet you. It holds one quantity per variant per location, and the inventory record itself carries a SKU, a unit cost, whether the item is tracked, country of origin and a harmonized system code. There is no field for a lot number, a batch code or an expiry date anywhere on it. Nothing is wrong with that — it is a general-purpose commerce model — but it means the batch a unit belongs to lives on the carton, or it lives somewhere you put it.

Source: Shopify.dev — InventoryItem object reference

Method 1 — a date metafield on the variant

Shopify's custom data system supports date values directly: the documented metafield types include date and date_time. You can define a metafield on the product variant called something like “Expiry date,” and from then on each variant has a real date field in the admin that you can set, see in the variant editor, and sort or filter against.

This is free, available on every plan, needs no app, and is the right answer more often than people expect. If one SKU only ever has one batch in stock at a time — short catalogs, made-to-order, slow-moving specialist lines — a date metafield is genuinely all you need, and you should use it rather than pay for anything.

Three limits decide whether it is enough for you. A metafield holds exactly one value per variant, so the moment two batches of the same SKU with different dates are both in stock, you have to pick which date to store and the other one becomes invisible. It stores no quantity, so “how many units expire in March” is not a question it can answer. And nothing watches it: no part of Shopify will notice that a date has passed and tell you, so the field is only as current as the last time somebody looked at it.

Source: Shopify.dev — list of metafield data types

Method 2 — a spreadsheet keyed on SKU

One row per batch: SKU, lot code, expiry date, quantity received, supplier, and the date it arrived. This is the method most merchants in food, supplements and cosmetics are already running, and it handles the one thing a metafield cannot — several live batches of the same SKU, each with its own date and its own quantity.

Sort the sheet by expiry date ascending and you have a FEFO pick list: first expired, first out. Filter it by lot code and you have a recall list, which is the job a spreadsheet does best — when a supplier flags a batch, a text filter answers in seconds what a stock-take answers in hours.

The costs are worth being honest about. The quantity column is a copy, so it starts drifting from Shopify's real stock the first time you sell a unit, and only a manual reconciliation brings it back. Variants with no SKU cannot be keyed this way at all. And nobody is warned by a spreadsheet — it tells you what is expiring only on the days you remember to open it and re-sort it, which is the gap that actually costs money, because stock does not expire on the days you happen to look.

Method 3 — per-lot records, checked every day

This is what BatchGuard does, and it is the same rows from method 2 joined to your live Shopify stock instead of copied away from it. Any variant can take a lot code, expiry date, quantity, supplier and a COA link — one at a time, or imported by CSV against your existing SKUs. Several batches per variant is the normal case, not a workaround.

From there it splits your stock into Expired, Expiring soon and Active, with the units and the inventory value sitting in each bucket, and emails a weekly digest so the checking is not a thing you have to remember. A FEFO view orders lots by date so the soonest-to-expire goes out first, with per-location pull lists telling each site what to take off its own shelf. A recall is a search by lot or supplier, exported to CSV or printed to PDF. Writing a bad batch off is explicit and per-lot: the units come out of available stock, capped so it can never go below zero, with the value recorded for your books and an undo if you mis-click.

What it does not do, so you are not surprised: it does not auto-deduct a specific lot as orders ship, does not forecast demand, and does not manage purchase orders. A recall report covers inventory, not customers — it tells you which stock is affected, not who bought it. It requests no customer or order data, so it stores no names, emails or addresses. It is a tool, not legal or regulatory advice.

The free plan tracks up to 10 lots; Standard is $9.99 per month with a 14-day free trial, billed through Shopify in USD.

Which one to pick

If one SKU only ever has one batch in stock at a time, use a date metafield and spend nothing. If you have several live batches per SKU, a catalog small enough to re-sort by hand, and no regulatory deadline for producing a recall list, the spreadsheet in method 2 is a real answer and the instructions above are complete on purpose. The app earns its price when walking the shelf is your current early-warning system, or when a supplier calling about a bad lot has to be answered today.