The WooCommerce REST API in Practice
Authentication keys, creating and updating orders and products from outside, webhooks for the reverse direction, and syncing without duplicate records.

In this article
Sooner or later most growing stores need WooCommerce to talk to another system: an ERP that owns stock, an accounting package that needs every order, a marketplace, a warehouse or a mobile app. The WooCommerce REST API makes that possible without touching the database directly, and the basic calls are well documented.
The difficult part is not making the first request. It is keeping two systems in agreement over months of real orders, retries, partial failures and staff editing records on both sides. This guide covers authentication and the main endpoints briefly, then spends most of its time on the parts that decide whether an integration survives: webhooks, idempotency, external ID mapping and logging.
Keys, permissions and why read-write should be rare
WooCommerce issues REST API keys under WooCommerce, Settings, Advanced, REST API. Each key belongs to a WordPress user and has read, write or read-write permission. Requests use the consumer key and secret with HTTP Basic authentication over HTTPS, and the key can do anything its user and permission level allow.
Create one key per integration, attached to a dedicated user with only the capabilities it needs, and give it read access unless it must write. A reporting tool needs read; an ERP that pushes stock levels needs write. Name each key after the system that uses it, store secrets in that system's secret store rather than in code, and revoke keys when an integration is retired. If a key leaks, you can then revoke one integration without breaking the others.
Products: create, update and the variation gotcha
The products endpoint creates and updates simple products with a single request. Variable products work differently: the parent product holds the attributes, and each variation is a separate resource under the parent's variations endpoint, with its own SKU, price and stock. An integration that updates only the parent's stock will not change what customers can buy.
Use the SKU as the shared key between systems wherever possible, because it is visible to staff and stable across both. Look up a product by SKU before creating it, update stock and prices on the variation itself, and decide which system owns each field. If the ERP owns stock and price but WooCommerce owns descriptions and images, never let a sync overwrite the fields it does not own.
Orders: creating one that behaves like a real order
Orders created through the API, for example from a marketplace or a phone sales tool, should behave like orders placed on the website: correct customer, line items linked to real product IDs, shipping lines, taxes and totals, and the right status. Set set_paid only when payment has genuinely been taken, because it changes the status and can reduce stock and trigger customer emails.
Decide in advance which emails such orders should send and what status they start in. Store the source system's order reference in order meta so the order can always be traced back. With high-performance order storage enabled, orders live in dedicated tables, which is another reason to use the API or WooCommerce's own functions rather than writing to the database.
Webhooks out, so the external system hears about changes
Polling the API every few minutes for new orders works, but it is slow and wasteful. WooCommerce webhooks push an event to a URL you choose when an order, product, customer or coupon is created, updated or deleted. Each delivery is signed with a secret using an HMAC-SHA256 header, which the receiver should verify before trusting the payload.
Treat webhook delivery as at least once, not exactly once. Deliveries can be retried, arrive out of order or fail while the receiver is down, and WooCommerce disables a webhook after repeated failures. The receiver should acknowledge quickly, put the event in a queue, process it idempotently and run a periodic reconciliation that catches anything missed. The webhook delivery reliability guide covers this in depth.
Idempotency and external IDs: the two-way sync that does not duplicate
Most failed integrations fail here. A request times out, the sender retries, and the store now has two orders. A product is renamed in one system and created again in the other. Two systems both update stock and overwrite each other's changes.
Keep a mapping between the two systems' IDs, either in WooCommerce meta or in a small table owned by the integration, and check it before every create. Make each write idempotent: if a record with that external ID already exists, update it instead of creating another. Give each field one owner and sync it in one direction only. For stock, prefer sending adjustments with a reference, or a full count from the system of record at a set interval, rather than both systems writing absolute numbers.
- One owner for each field, synced in one direction
- An external ID stored with every synced record
- Look up before create, update instead of duplicating
- A scheduled reconciliation that compares both systems
Batch endpoints, pagination and rate limits
Bulk operations should use the batch endpoints, which create, update and delete up to 100 items per request by default. Sending thousands of single requests is slower and puts much more load on the store. List endpoints are paginated, with a per_page parameter up to 100, and the response headers report the total number of items and pages.
WooCommerce does not impose a strict rate limit of its own, but your hosting, firewall or CDN may, and a busy store needs capacity for customers. Schedule large syncs outside peak hours, add a short pause between batches and back off when you receive 429 or 5xx responses.
Logging, because someone will ask what happened to an order
Sooner or later someone asks why an order never reached the warehouse or why stock showed the wrong number on a busy afternoon. Without a log, nobody can answer. Record every request and webhook event the integration handles: time, direction, endpoint, the record's IDs in both systems, the result and any error.
Keep personal data out of the logs where you can, keep them for a sensible period and make them searchable by order number and SKU. Add alerts for repeated failures and for a disabled webhook. The WooCommerce development guide, the REST API guide and the webhooks guide cover related ground, and the WooCommerce development service explains how I approach integrations.
Frequently asked questions
How do I authenticate with the WooCommerce REST API?
Create a consumer key and secret under WooCommerce, Settings, Advanced, REST API, attached to a user with the right capabilities, and send them with HTTP Basic authentication over HTTPS. Use a separate key with the narrowest permission for each integration.
Can I create orders through the WooCommerce API?
Yes. Send customer details, line items with product IDs, shipping lines and the intended status. Mark an order paid only when payment has really been taken, and store the source system's reference in order meta.
How do I keep an external system in sync with WooCommerce?
Store each record's ID in both systems, give every field one owner, make writes idempotent, use webhooks for changes in WooCommerce and run a scheduled reconciliation to catch missed events.
What are WooCommerce webhooks used for?
They notify another system when orders, products, customers or coupons change, so it does not have to poll the API. Deliveries are signed, can be retried and should be processed idempotently.
Topics
- WooCommerce REST API
- WooCommerce API keys
- create WooCommerce order via API
- WooCommerce ERP sync