Key takeaways
- WooCommerce has no import limit. Your hosting does, and images are what exhaust it.
- Batches of 200 to 500 products are reliable on typical shared hosting.
- Updates match on SKU or ID. Blank or changing SKUs guarantee duplicates.
- For catalogues that change, a feed beats a file. It re-syncs instead of re-uploading.
There are two genuinely different problems hiding behind "bulk import products into WooCommerce".
The first is a one-time load: you have a catalogue, you want it in WooCommerce, and once it is there you will maintain it by hand. A CSV is the right tool.
The second is an ongoing relationship with a catalogue you do not control, such as a supplier or a store you are mirroring. Prices move, stock moves, products appear and vanish. A CSV is the wrong tool, and using it anyway is why people end up re-uploading files every Monday morning.
The CSV route, done properly
WooCommerce's built-in importer lives at Products, All Products, Import. Upload, map columns, run.
Four settings decide whether it works:
Update existing products
Off for a first import. On for corrections and re-runs. When on, WooCommerce matches incoming rows against existing products by ID first, then SKU.
If a row matches nothing, it creates a new product. This is the mechanism behind almost every "why do I have 4,000 products when I imported 2,000" question: the second run had different SKUs, or none.
Column mapping
WooCommerce auto-detects its own standard headers. Anything it does not recognise defaults to Do not import, and it does not warn you. Scroll the entire mapping list before running. This is where custom fields silently disappear.
Batch size
Not exposed in the UI. Effectively governed by PHP max_execution_time, memory_limit and your host's request timeout. The importer processes rows in AJAX chunks, so a job that exceeds those limits dies partway with a generic error.
Images
The real bottleneck. Every URL in the Images column is downloaded into the media library during the import request. Not queued, not backgrounded. A 2,000 product catalogue averaging six images each means 12,000 HTTP fetches plus thumbnail generation, inside a request your host wants to finish in 60 seconds.
Size your batches by images, not products. A text-only import of 2,000 rows is fine. The same 2,000 rows with galleries needs splitting into batches of 200. If your import dies at an unpredictable point each time, images are almost always the reason.
Preparing the file
Before you upload anything, run these checks. Each one prevents a specific failure:
- Encoding is UTF-8. In Excel, Save As, CSV UTF-8. Anything else mangles accented characters.
- Every product has a unique SKU. Not for WooCommerce's sake, for your future self's. Without stable SKUs you can never safely re-import.
- Prices are bare decimals. No currency symbols, no thousands separators.
1299.00, not$1,299.00. - Image URLs are absolute and reachable. Paste three into a private browser window and confirm they load.
- Parent rows precede variation rows. Sort by Parent.
- Categories use consistent spacing.
Clothing > Shirts, always with the same spaces.
The full column reference, including the attribute flags that decide whether variations display, is in the WooCommerce product CSV schema explained.
Raising the limits
If you control the server, these are the values worth changing:
memory_limit = 512M
max_execution_time = 300
max_input_vars = 5000
upload_max_filesize = 64M
post_max_size = 64M
Set them in php.ini, or via your host's control panel. Adding them to .htaccess works on some Apache setups and is ignored on most managed hosting.
On managed WordPress hosting these are frequently locked and support will not raise them. In that case batching is your only option, and you should stop fighting it.
The feed route
Everything above assumes a file. For a catalogue that keeps changing, a file is the wrong shape of solution: it is a snapshot of a moving thing, and every time the thing moves you produce another snapshot by hand.
A feed inverts this. Instead of uploading a file, WooCommerce reads a URL that always returns the current state of the catalogue. The import runs on a schedule and applies only differences:
- New products at the source get created.
- Price changes propagate, with your markup rule reapplied.
- Stock changes propagate, so items that sold out upstream stop being sellable on your store.
- Removed products get marked out of stock rather than deleted, so URLs and any accumulated SEO value survive.
This is how our free WordPress plugin works. You scrape a store on Scrapify, save it as a live feed that refreshes hourly, then connect the plugin once. It handles batching, retries and image fetching in the background, applies a percentage markup and rounding rule, and matches on SKU so re-syncs update rather than duplicate.
Every account includes it, free accounts included, and the setup is walked through step by step in the plugin guide.
Import once, stay in sync forever
Scrape a store, save it as a live feed, connect the free WordPress plugin. Products, variations, images, categories and currency conversion, re-synced automatically.
File or feed?
| CSV import | Live feed | |
|---|---|---|
| Best for | One-time migration | Catalogues that change |
| Price updates | Manual re-upload | Automatic |
| Stock updates | Manual re-upload | Automatic |
| New products | Manual | Automatic |
| Markup rules | Recalculate in the sheet | Set once, applied every sync |
| Currency conversion | Manual | Live rate at sync time |
| Timeout risk | High on big files | Low, work is backgrounded |
| Works offline | Yes | No, needs the source reachable |
After the import
Whichever route you took, check these before considering the job finished:
- Count products. Products, All Products should match your row count, minus variation rows.
- Check the media library. Roughly your expected image total. A number far too low means image fetches failed.
- Open a variable product on the front end. Confirm the dropdowns render and selecting options changes the price.
- Sort by price ascending. Anything at 0.00 imported with a blank or malformed price.
- Regenerate thumbnails if images look wrong, using WP-CLI or a plugin.
- Re-run product lookup tables. WooCommerce, Status, Tools, Regenerate product attributes lookup table. Skipping this makes filtered navigation return wrong results.
If something did go wrong, the symptom-to-cause mapping is in 12 WooCommerce import errors and how to fix them.