In Shopify, change prices for multiple products by filtering your list at Products, clicking Edit products, adding the Price column and using the percentage adjustment dropdown. For scheduled sales use a discount or a price-scheduler app; for site-wide feed-visible sales use a real price change with a rollback snapshot.
- Bulk-editor percentage adjustments apply uniformly but don't round.
- Discounts apply at checkout; real price changes propagate to all channels including feeds.
- Never schedule price changes manually at midnight — automate it or use discounts.
- Always store a compare-at price so the sale is visible in the theme.
- Real price changes trigger Google Merchant feed updates within hours.
The bulk editor handles small filtered price changes. For sales and scheduled changes, use a price scheduler or discount codes — never rely on midnight manual edits.
The five ways to change prices on many Shopify products
Every Shopify price change belongs to one of five practical categories, and mixing them up is the single most common cause of botched sales and Google Merchant Center disapprovals. The five are: a real price change written to the variant price field (visible everywhere — storefront, checkout, feeds, marketplaces), a compare-at price change that puts a strike-through next to the real price (visible on the storefront only if the theme renders it), an automatic discount that applies at checkout (invisible in most feeds, invisible on marketplace listings), a discount code that requires the customer to enter it (same feed visibility as automatic discounts, plus friction), and a scheduled price change written by a price-scheduler app that flips the real price at a chosen time and restores it when the sale ends. Which one you should use depends on where you need the price to be visible, how long the change should last, and whether it needs to reset itself. Sitewide seasonal sales that must show up in Google Shopping and Meta ads need real price changes with compare-at hygiene. Targeted campaigns for existing customers need discount codes. Flash sales that must revert at midnight need a scheduler. Getting this wrong is why so many stores accidentally sell for the sale price for a week after the sale ends. The rest of this guide covers each method in detail, with concrete click-paths and a worked numeric example.
Native bulk price editing — the everyday tool
The native admin path for price changes is Products → filter → checkboxes → Edit products → click 'Add fields' → tick Price and Compare-at price → edit inline. The header of each price column exposes a small dropdown that offers 'Adjust prices' with two modes: percentage change (up or down) and fixed amount adjustment. The percentage adjustment applies uniformly across every selected row and is the fastest way to run a straightforward '+8%' or '-15%' sweep across a filtered collection. Two structural limits catch every first-time user. First, the percentage adjustment does not round: a 10% increase on a $19.00 variant produces $20.90, not $20.99 — you either accept the ugly ending or run a second pass with a rounding rule. Second, the editor rewrites values one at a time as you tab out of cells, with no confirmation and no undo; if you selected the wrong filter, you will not notice until the storefront shows it. Always sanity-check the row count on the selection banner before opening the editor, always add only the Price and Compare-at columns (nothing else) so you cannot accidentally overwrite unrelated fields, and always spot-check three or four random products on the live storefront before closing the tab.
- 1Filter to the exact target set
Products → Filters. Combine collection, vendor, tag and status. Sanity-check the count on the 'N products match this search' banner before proceeding.
- 2Open the bulk editor
Tick the header checkbox, choose 'Select all N matching', then click Edit products. Add only the Price and Compare-at price columns — do not expose anything else.
- 3Apply the percentage adjustment
Click the small dropdown on the Price column header, choose 'Adjust prices', pick Increase or Decrease, enter the percentage. The change writes as you tab out.
- 4Run a rounding pass
Native editor does not round. Either accept the ugly endings, or export to CSV, apply a rounding formula (=ROUND(price-0.01,0)+0.99) and re-import, or use a dedicated app with a built-in rounding rule.
- 5Spot-check on the storefront
Open three random products on the live site — desktop and mobile — and confirm the price and compare-at look correct. Only close the editor after this step.
Price change vs discount code vs automatic discount — the trade-off matrix
Real price changes, automatic discounts and discount codes all lower what a customer pays, but they behave differently on the surfaces that matter. Real price changes propagate everywhere — the online store, POS, marketplaces, Google Shopping, Meta Advantage+ shopping and any embedded storefront. That's what you want for a seasonal sale you're promoting outside the store. Automatic discounts apply at checkout (or in the cart, depending on theme) and are visible in the store but not in the Google Merchant Center product feed unless you specifically configure a Promotion in Merchant Center. Discount codes require the customer to enter the code and are entirely invisible in every external channel. The trade-offs cascade further: real price changes need a scheduled restore, discounts revert themselves; discounts stack with other discounts subject to your combination rules, real prices don't; real price changes reset your EU Omnibus 30-day reference price, discounts don't; discounts can be limited to specific customer segments, real prices can't (unless you use Markets or B2B).
| Approach | Storefront | Google Shopping / Meta feeds | Marketplaces | Auto-restore | EU Omnibus impact |
|---|---|---|---|---|---|
| Real price change (bulk edit) | Yes | Yes | Yes | No — manual restore | Resets 30-day ref. |
| Compare-at price (strike-through) | Yes (theme-dep.) | Feeds `sale_price` | Depends on channel | No | Must be truthful |
| Automatic discount | Yes | Only with Merchant promo | No | Yes (end date) | None |
| Discount code | Only on entry | Only with Merchant promo | No | Yes (end date) | None |
| Price scheduler app | Yes | Yes | Yes | Yes (auto restore) | Resets on flip, restores on end |
A real price change with a matching compare-at makes the sale visible in every external channel. A stackable discount code on top rewards loyal buyers with an extra percent. Track redemption to measure incremental lift — if the code doesn't move the needle, drop it next time.
Scheduled price changes — the honest options
Shopify does not natively schedule real price changes; the admin has no 'apply this price at 09:00 next Tuesday' button on the product page. Your three honest options are (1) an automatic discount with a start and end date, which reverts itself but is invisible in feeds, (2) a manual price edit at the exact time you want it live and another manual edit to restore, which is where midnight-Friday mistakes come from, and (3) a price-scheduler app that writes the new price at the scheduled time, records a snapshot of the original, and automatically restores the original when the sale window closes. Manual scheduling is fine for a single, well-diarised, non-time-critical change; anything recurring or anything that must go live outside working hours belongs in an app. The subtle failure mode with manual scheduling isn't the flip — that part usually goes fine — it's the restore. Two weeks after a sale ends, half the compare-at prices are still visible on the storefront, Google Shopping is still showing sale_price without a promotion, and someone in ads is asking why the ROAS collapsed. The restore is not optional; if you can't automate it, don't run the sale as a price change.
Compare-at prices, EU Omnibus and price anchoring
The compare-at price is a secondary price field on every variant. When it is higher than the real price, most themes render it struck through and Google Shopping populates `sale_price` and `price` accordingly. It is not a scheduling primitive — Shopify does not clear it when a sale ends. Since the EU Omnibus Directive came into force in 2022, any price-reduction announcement inside the EU must reference the lowest price the trader charged in the 30 days before the reduction. Practically that means your compare-at must be, at most, the lowest 30-day price — not the manufacturer's suggested retail price, not the launch price. UK ASA guidance is similar in spirit. Several US state attorneys general have brought comparable enforcement actions. Every sale therefore needs three coordinated writes: raise compare-at to the correct 30-day reference, drop the real price to the sale price, and then, when the sale ends, drop compare-at back to blank. Missing the third write is where merchants get letters from regulators or Google Merchant Center disapprovals. Automated compare-at hygiene at the end of every promotion is now a compliance requirement, not a nice-to-have. Blank the field with a bulk edit as soon as the promotion clock hits zero, and log it in your price change log.
A worked example — 10% increase with .99 rounding on 50 products
Concrete numbers. Assume a 50-product Summer collection with an average price of $28.40 and you want a +10% increase rounded to .99 endings. Doing this correctly in the native editor takes two passes: apply the +10% via the column dropdown, then a second pass to snap the endings. Doing it in a CSV is three columns (current, ×1.10, rounded). Doing it in a dedicated app with a rounding rule is one preview. The table below shows five representative variants; note how the naive ×1.10 rarely lands on a .99 ending, and note the third row where a compare-at of $64.00 is left in place — a previous sale's compare-at that was never cleaned up. Under EU Omnibus, if that $64.00 does not reflect the true lowest price in the last 30 days, it must be cleared before the new price goes live. This is exactly the kind of trailing debt that turns a routine +10% into a compliance issue.
| SKU | Title (example) | Before price | ×1.10 raw | Rounded to .99 | Compare-at (before) | Compare-at (after) |
|---|---|---|---|---|---|---|
| TSH-BLK-M | Cotton T-Shirt — Black M | $29.00 | $31.90 | $31.99 | — | — |
| TSH-BLK-L | Cotton T-Shirt — Black L | $29.00 | $31.90 | $31.99 | — | — |
| HDY-NVY-M | Fleece Hoodie — Navy M | $54.00 | $59.40 | $59.99 | $64.00 (stale) | cleared |
| CAP-BLK | Twill Cap — Black | $19.00 | $20.90 | $20.99 | — | — |
| SCK-WHT | Crew Sock — White (3-pack) | $12.50 | $13.75 | $13.99 | — | — |
Programmatic price changes with the GraphQL Admin API
For scheduled changes, ERP-driven daily syncs, or catalog-wide refreshes bigger than about 5,000 variants, the GraphQL Admin API is the right primitive. `productVariantsBulkUpdate` handles up to 250 variants per call and is the modern replacement for the deprecated single-variant mutation; `bulkOperationRunMutation` wraps a mutation in an asynchronous job that runs against the entire catalog, bypasses the per-request rate limit and returns a JSONL result file when it finishes. Every serious price integration must be idempotent — set absolute values, not deltas — because Shopify's cost-based rate limit will force retries on any real catalog, and a retry that multiplies price by 0.9 a second time will silently double-apply the discount. Log every variant ID with its old and new price before you write, respect the `extensions.cost` block that comes back in every response, back off exponentially on `THROTTLED` errors, and treat any non-2xx or non-empty `userErrors` array as a hard failure that needs manual review. Shopify's own Bulk Operations documentation is worth reading end-to-end before you ship any pricing script.
mutation UpdateVariantPrices($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
productVariants { id price compareAtPrice }
userErrors { field message }
}
}Rollback strategy — the snapshot-before-edit routine
Every price change on more than a handful of variants needs a rollback plan, and that plan starts before you open any editor. The minimum viable snapshot is a filtered CSV export of the exact products you are about to touch, saved with a date-and-reason filename (2026-07-15_summer_+8pct.csv) into a shared drive where a colleague can find it if you are unavailable. If you are using a dedicated bulk-edit app, its Smart Undo snapshot is your rollback — but verify the snapshot exists in the job history before assuming it does, and confirm the app's drift-detection behavior so you do not overwrite manual edits made after the job. Log every intentional price sweep in a running Price Change Log (see the /templates page for a copy-ready template): date, scope, direction, rule, row count, snapshot ID, owner, EU Omnibus impact. That log is your audit trail for regulators, your handoff document for the person who covers your role next quarter, and the input to your next reprice — you cannot make consistent decisions about a catalog you do not measure.
Pitfalls and gotchas that break real price changes
The pitfalls below are catalog-specific and universally learned the hard way. Every one of them has cost a real merchant real money in the last twelve months. Read this list before the next reprice.
- Percentage adjustments in the native editor do not round. A 10% increase produces $20.90 endings — the ugly middle ground between $19.99 and $21.99 that erodes the psychological anchor. Always round in a second pass or bake rounding into the transform.
- Compare-at is not cleared automatically when a sale ends. Two weeks later the storefront still says 'Save 30%'. This is the single most common cause of EU Omnibus warnings and Google Merchant misrepresentation disapprovals.
- Bulk-editing price at product level in CSV overwrites every variant. Size-based pricing you spent months building disappears in seconds. Always confirm whether your transform operates at product or variant granularity.
- Market-specific price overrides drift silently. Editing base prices does not touch Shopify Markets overrides. Six months later, EU customers see prices from Q1 while US customers see Q4. Audit Markets after every base-price sweep.
- Subscription contracts do not auto-update. Changing the variant price only affects new subscribers; existing recurring contracts stay at the old price until you push the update inside the subscription app. Some jurisdictions require 30 days' notice.
- Automated collections based on price break on a bump. A '+$5 increase' can move products out of the 'Under $50' collection you paid to build inbound links to. Audit price-based automated collections after every sweep.
- Google Shopping approves feeds within hours and disapproves feed/landing-page price mismatches. If your feed says $29.99 and the landing page says $31.99, expect a disapproval within the day. Always change price → wait for feed → verify.
- Rate limits kill long API jobs halfway through. A price sweep script that ignores 429 responses stops writing but exits with success. Half your prices are wrong. Always implement exponential backoff, always log which IDs succeeded.
Safety checklist for every price change
The routine below takes about five minutes and eliminates the vast majority of price-related incidents. Copy it into your Bulk Edit Planning Sheet (see /templates) and refuse to skip a step.
1. Snapshot: export the filtered set to CSV, save with date + reason in the filename, keep for 30 days minimum. 2. Compare-at audit: before applying, check that no stale compare-at values remain from earlier promotions — clear them first. 3. Test on five: run the transform on a five-product subset, verify on live storefront (desktop, mobile) and in Google Merchant Center Preview. 4. Preview and round: use a rounding rule; do not accept .90/.34 endings unless intentional. 5. Apply outside peak hours: schedule for 02:00–05:00 store time. 6. Verify feeds within 24h: Google Merchant Center, Meta catalog, any marketplace. 7. Schedule the restore now: even before the sale starts, put the restore in your calendar with the snapshot ID. 8. Log it in your Price Change Log — required for EU Omnibus, invaluable for next quarter's planning.
Related guides
Prices rarely change in isolation. The three guides below are the ones you will most often want to read alongside a repricing sweep — one for the compare-at compliance angle, one for the scheduling side, and one for how a scheduled sale interacts with product feeds and marketplaces.
- Shopify Compare-at Price Explained — theme rendering, Google Shopping behavior, EU Omnibus and UK ASA compliance rules, bulk set/remove workflow.
- How to Schedule a Shopify Sale — discount codes vs automatic discounts vs real price changes, feed visibility trade-offs, timezone gotchas, restore strategies.
- How to Undo Product Changes in Shopify — snapshot mechanics, activity-log limitations, disaster-recovery checklist.
Plan price changes, promotions and automatic restoration — without discount codes.
Learn moreFrequently asked questions
The most common questions merchants ask us about pricing.