11/22/2022
XML import of 2,700 WooCommerce products in < 5 minutes
TL;DR: Importing 2,700 products from XML via standard plugins = 2 hours. A custom PHP script with diff + batch insert + disabled WooCommerce hooks = < 5 minutes.
Running a dropshipping store with a supplier that provides an XML feed? Standard import plugins (WP All Import, etc.) work via admin AJAX — one product at a time, with a full WooCommerce hook cycle. For 2,700 products that’s a recipe for timeouts and grey hairs.
Diagnosing the problem
What makes the standard import slow?
- WooCommerce hooks on every product —
woocommerce_update_product, search index rebuild, cache invalidation. For 2,700 products = 2,700 × a dozen hooks each. - No diffing — you re-import everything even if 2,690 products haven’t changed.
- Individual SQL queries — one query per product instead of batch inserts.
Solution: a custom importer
Key elements:
// 1. Disable unnecessary hooks during import
remove_all_actions('woocommerce_update_product');
wp_suspend_cache_addition(true);
wp_defer_term_counting(true);
// 2. Parse XML into an associative array
$xml = simplexml_load_file($feed_url);
$products = [];
foreach ($xml->product as $p) {
$products[(string)$p->ean] = [
'title' => (string)$p->name,
'price' => (float)$p->price * MARGIN_MULTIPLIER,
'stock' => (int)$p->stock,
'ean' => (string)$p->ean,
];
}
// 3. Fetch current state from DB (one query)
global $wpdb;
$existing = $wpdb->get_results(
"SELECT meta_value as ean, post_id FROM {$wpdb->postmeta} WHERE meta_key = '_ean'",
OBJECT_K
);
// 4. Diff — only update what changed
foreach ($products as $ean => $data) {
if (isset($existing[$ean])) {
// update price and stock if they changed
$post_id = $existing[$ean]->post_id;
update_post_meta($post_id, '_price', $data['price']);
update_post_meta($post_id, '_stock', $data['stock']);
} else {
// new product — insert
wc_create_product($data);
}
}
// 5. Restore hooks and recount terms
wp_suspend_cache_addition(false);
wp_defer_term_counting(false);
Results
- Full import (first run): 4 minutes 23 seconds
- Delta import (changes only): 47 seconds
- Previously via WP All Import: > 2 hours (timeout at 1,000 products)
Schedule
Cron every 6 hours via WP-Cron or system cron:
0 */6 * * * php /var/www/html/wp-content/plugins/my-importer/run.php >> /var/log/import.log 2>&1
After import — force nginx cache flush (if you have FastCGI cache):
find /tmp/nginx-cache -type f -delete