The REST API
The REST API is a way to work with the shop over HTTP: an external program sends a request to a site address and receives JSON. This is how an accounting system updates stock, a warehouse collects orders and a mobile app shows the catalog. The API is built into WooCommerce and does not need enabling.
Access keys
Requests are signed with a key. Create one in WooCommerce → Settings → Advanced → REST API → Add key: give a description, a user and permissions — Read, Write or Read/Write. The panel shows two strings once: ck_... (the key) and cs_... (the secret) — save them.
The first request
curl "https://shop.example.com/wp-json/wc/v3/products?per_page=5" \
-u ck_your_key:cs_your_secretThe key and the secret are passed as a user name and a password. The response is a JSON array of five products with the fields id, name, price, stock_quantity and others. Passing keys this way is acceptable only over HTTPS.
Endpoints
| Address | What it is |
|---|---|
/wp-json/wc/v3/products | Products |
/wp-json/wc/v3/products/12 | One product |
/wp-json/wc/v3/products/15/variations | A product's variations |
/wp-json/wc/v3/products/categories | Categories |
/wp-json/wc/v3/orders | Orders |
/wp-json/wc/v3/customers | Customers |
/wp-json/wc/v3/coupons | Coupons |
The methods are the usual ones: GET reads, POST creates, PUT changes, DELETE deletes.
List parameters
| Parameter | What it does |
|---|---|
per_page, page | Page size and number, at most 100 records |
search | Search by name |
status | Filter by status |
modified_after | Only records changed after a date |
_fields | Only the listed fields: _fields=id,status,total |
The total number of records and pages comes in the X-WP-Total and X-WP-TotalPages response headers.
Changing data
curl -X PUT "https://shop.example.com/wp-json/wc/v3/products/12" \
-u ck_your_key:cs_your_secret \
-H "Content-Type: application/json" \
-d '{"stock_quantity": 15, "regular_price": "11.90"}'The request changes the stock and price of product 12 and returns the updated product. The key needs write permission. Many products are updated in one request through /products/batch with the create, update and delete fields.
Your own field in a response
Create the includes/rest.php file and include it in acme-shop.php:
// wp-content/plugins/acme-shop/includes/rest.php
add_filter('woocommerce_rest_prepare_product_object', function (WP_REST_Response $response, WC_Product $product) {
$response->data['lead_time'] = (int) $product->get_meta('_acme_lead_time');
return $response;
}, 10, 2);The filter fires before each product is sent. Repeat the first request: every product now has a lead_time field. Orders have the same kind of filter, woocommerce_rest_prepare_shop_order_object.
A custom route
When the ready-made endpoints are not enough, you register your own:
// wp-content/plugins/acme-shop/includes/rest.php
add_action('rest_api_init', function () {
register_rest_route('acme/v1', '/low-stock', [
'methods' => 'GET',
'permission_callback' => fn () => current_user_can('manage_woocommerce'),
'callback' => function () {
$products = wc_get_products(['manage_stock' => true, 'limit' => 100]);
$low = array_filter($products, fn ($product) => (int) $product->get_stock_quantity() < 5);
return array_map(
fn ($product) => ['id' => $product->get_id(), 'name' => $product->get_name()],
array_values($low)
);
},
]);
});The address /wp-json/acme/v1/low-stock returns products with fewer than five left. permission_callback lets in only users allowed to manage the shop. The ck_ and cs_ keys work only for wc/... addresses: your own route is called with a WordPress application password, created in Users → Profile → Application Passwords.
Common mistake
An integration polls /orders every minute and fetches all orders in full. On a large shop such requests load the database noticeably. Request only what has changed — with modified_after — and only the fields you need through _fields. Better still, receive orders with a webhook at the moment of checkout — that is the next lesson.