
Woocommerce Rest Api
- 79 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Integrates or builds headless frontends on WooCommerce using its REST API for products, orders, customers, and coupons with key authentication.
About
Uses the WooCommerce /wc/v3 REST API with consumer key/secret auth to read and write products, orders, customers, and coupons. A developer uses it for headless storefronts, ERP/CRM sync, or external order dashboards.
- Full CRUD over products, orders, customers, coupons
- Official Node.js client handling OAuth/Basic auth
Woocommerce Rest Api by the numbers
- 79 all-time installs (skills.sh)
- Ranked #3,054 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill woocommerce-rest-apiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 79 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Integrates or builds headless frontends on WooCommerce using its REST API for products, orders, customers, and coupons with key authentication.
Files
WooCommerce REST API
Overview
WooCommerce ships a versioned REST API (/wp-json/wc/v3/) that exposes products, orders, customers, coupons, and store settings over HTTPS. It uses OAuth 1.0a for non-HTTPS environments and Basic Auth (consumer key/secret) over HTTPS. The official @woocommerce/woocommerce-rest-api Node.js client handles authentication automatically and supports the full CRUD surface.
When to Use This Skill
- When building a headless storefront that reads products and categories from WooCommerce
- When integrating WooCommerce with an ERP, CRM, or fulfillment system
- When creating an order management dashboard outside of WordPress Admin
- When syncing inventory between WooCommerce and a warehouse or POS system
- When automating bulk product imports or price updates from an external catalog
- When building a mobile app that needs access to WooCommerce store data
Core Instructions
1. Generate API credentials
In WordPress Admin → WooCommerce → Settings → Advanced → REST API → Add Key:
- Description:
My Integration - User: (admin user)
- Permissions: Read/Write
This generates a Consumer Key (ck_xxx) and Consumer Secret (cs_xxx). Store them in environment variables, never in source code.
WOOCOMMERCE_URL=https://mystore.com
WOOCOMMERCE_CONSUMER_KEY=ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WOOCOMMERCE_CONSUMER_SECRET=cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2. Set up the Node.js client
npm install @woocommerce/woocommerce-rest-api // lib/woocommerce.ts
import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";
export const woo = new WooCommerceRestApi({
url: process.env.WOOCOMMERCE_URL!,
consumerKey: process.env.WOOCOMMERCE_CONSUMER_KEY!,
consumerSecret: process.env.WOOCOMMERCE_CONSUMER_SECRET!,
version: "wc/v3",
axiosConfig: {
timeout: 15000,
},
});3. Query products with filtering and pagination
// lib/products.ts
interface ProductQuery {
page?: number;
perPage?: number;
category?: number;
status?: "publish" | "draft" | "private";
stockStatus?: "instock" | "outofstock" | "onbackorder";
orderby?: "date" | "popularity" | "price" | "title";
}
export async function getProducts(params: ProductQuery = {}) {
const response = await woo.get("products", {
page: params.page ?? 1,
per_page: params.perPage ?? 20,
status: params.status ?? "publish",
stock_status: params.stockStatus,
orderby: params.orderby ?? "date",
category: params.category,
});
return {
products: response.data,
totalPages: parseInt(response.headers["x-wp-totalpages"]),
totalProducts: parseInt(response.headers["x-wp-total"]),
};
}
export async function getProductById(id: number) {
const response = await woo.get(`products/${id}`);
return response.data;
}
// Get product variations
export async function getProductVariations(productId: number) {
const response = await woo.get(`products/${productId}/variations`, {
per_page: 100,
});
return response.data;
}4. Create and manage orders
// lib/orders.ts
interface OrderLineItem {
product_id: number;
variation_id?: number;
quantity: number;
}
interface CreateOrderParams {
billing: {
first_name: string;
last_name: string;
email: string;
address_1: string;
city: string;
postcode: string;
country: string;
};
line_items: OrderLineItem[];
payment_method?: string;
}
export async function createOrder(params: CreateOrderParams) {
const response = await woo.post("orders", {
...params,
status: "pending",
payment_method: params.payment_method ?? "stripe",
payment_method_title: "Credit Card",
set_paid: false,
});
return response.data;
}
export async function updateOrderStatus(
orderId: number,
status: "pending" | "processing" | "on-hold" | "completed" | "cancelled" | "refunded",
note?: string
) {
const updateData: any = { status };
if (note) {
// Add an order note
await woo.post(`orders/${orderId}/notes`, { note });
}
const response = await woo.put(`orders/${orderId}`, updateData);
return response.data;
}
export async function getOrders(params: {
status?: string;
after?: string; // ISO 8601 date
page?: number;
} = {}) {
const response = await woo.get("orders", {
status: params.status ?? "processing",
after: params.after,
per_page: 50,
page: params.page ?? 1,
});
return {
orders: response.data,
totalPages: parseInt(response.headers["x-wp-totalpages"]),
};
}5. Manage inventory and product updates
// Update stock quantity for a product or variation
export async function updateStock(productId: number, quantity: number, variationId?: number) {
const endpoint = variationId
? `products/${productId}/variations/${variationId}`
: `products/${productId}`;
const response = await woo.put(endpoint, {
stock_quantity: quantity,
manage_stock: true,
});
return response.data;
}
// Batch update products (up to 100 per request)
export async function batchUpdateProducts(
updates: Array<{ id: number; regular_price?: string; stock_quantity?: number; status?: string }>
) {
const response = await woo.post("products/batch", { update: updates });
return response.data;
}Examples
Full product sync from external catalog
import pLimit from "p-limit";
export async function syncProductsFromCatalog(
externalProducts: Array<{ sku: string; price: number; stock: number }>
) {
const limit = pLimit(5); // Max 5 concurrent API calls
const results = await Promise.allSettled(
externalProducts.map((ext) =>
limit(async () => {
// Look up WooCommerce product by SKU
const searchResponse = await woo.get("products", { sku: ext.sku });
const existing = searchResponse.data[0];
if (existing) {
// Update existing product
return woo.put(`products/${existing.id}`, {
regular_price: ext.price.toFixed(2),
stock_quantity: ext.stock,
manage_stock: true,
});
} else {
console.warn(`SKU not found in WooCommerce: ${ext.sku}`);
return null;
}
})
)
);
const failed = results.filter((r) => r.status === "rejected");
if (failed.length > 0) {
console.error(`${failed.length} products failed to sync`);
}
return results;
}Customer management
// Create or update customer
export async function upsertCustomer(email: string, data: Record<string, any>) {
const searchResponse = await woo.get("customers", { email });
const existing = searchResponse.data[0];
if (existing) {
const response = await woo.put(`customers/${existing.id}`, data);
return response.data;
} else {
const response = await woo.post("customers", { email, ...data });
return response.data;
}
}
// Get customer order history
export async function getCustomerOrders(customerId: number) {
const response = await woo.get("orders", {
customer: customerId,
per_page: 50,
orderby: "date",
order: "desc",
});
return response.data;
}Best Practices
- Always use HTTPS — OAuth 1.0a works over HTTP but transmits credentials with every request; HTTPS + Basic Auth is simpler and safer for server-to-server calls
- Respect rate limits — WordPress doesn't enforce API rate limits by default, but high request volumes can cause PHP-FPM or MySQL exhaustion; use
p-limitor a queue for bulk operations - Use batch endpoints for bulk updates —
/products/batchaccepts up to 100 create/update/delete operations in one request vs. 100 individual requests - Filter fields with `_fields` parameter —
?_fields=id,name,price,stock_quantityreduces response payload significantly for large product lists - Handle WooCommerce-specific error codes — the API returns
rest_invalid_param,woocommerce_rest_cannot_create, etc. in the error body; parseresponse.data.codefor specific error handling - Use `after` and `before` date filters for incremental syncs — avoid full catalog re-scans by filtering orders/products modified since last sync using ISO 8601 timestamps
- Store the API URL without trailing slash — the WooCommerce client handles URL construction; a trailing slash in the base URL causes double-slash in endpoints
Common Pitfalls
| Problem | Solution |
|---|---|
401 Unauthorized despite correct keys | Verify the site uses HTTPS; over HTTP the client must use OAuth 1.0a, not Basic Auth — set isHttps: false in the client config |
| Products endpoint returns empty array | Check the user assigned to the API key has the correct capabilities; the default woocommerce_manage_products capability is required |
| Order creation fails with product ID error | Variable products require variation_id in line_items; passing only product_id for a variable product causes invalid_variation error |
| Pagination headers missing | The x-wp-total and x-wp-totalpages headers are only present on list endpoints, not single-resource endpoints |
| Batch operation partially fails | Batch responses include individual errors per item; iterate response.data.update array and check each item for error property |
| Slow response on product listing | Add ?_fields=id,name,price to reduce payload; also consider enabling persistent object cache (Redis) on the WordPress server |
Related Skills
- @woocommerce-plugin-development
- @woocommerce-subscriptions
- @woocommerce-performance
- @headless-commerce-architecture
- @rest-api-design
{
"context": "Tests whether the agent uses the correct WooCommerce Node.js client package, applies concurrency control with p-limit for bulk API calls, uses Promise.allSettled for resilient parallel execution, leverages the batch products endpoint for bulk updates, and correctly sets manage_stock when updating stock quantities.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct npm package",
"max_score": 10,
"description": "package.json dependencies include @woocommerce/woocommerce-rest-api (not a different woocommerce package)"
},
{
"name": "Credentials in env vars",
"max_score": 8,
"description": "Client is initialized using process.env values for URL, consumer key, and consumer secret — no hardcoded credential strings"
},
{
"name": "Client version wc/v3",
"max_score": 7,
"description": "WooCommerceRestApi is initialized with version: \"wc/v3\""
},
{
"name": "Concurrency limiting",
"max_score": 12,
"description": "p-limit (or equivalent) is imported and used to cap the number of concurrent API calls during the sync loop"
},
{
"name": "Promise.allSettled usage",
"max_score": 10,
"description": "Promise.allSettled is used (not Promise.all) so that individual product failures do not abort the entire sync"
},
{
"name": "Batch endpoint used",
"max_score": 12,
"description": "The products/batch endpoint is called via woo.post(\"products/batch\", ...) for bulk price/stock updates, OR a batchUpdateProducts helper using this endpoint is implemented"
},
{
"name": "manage_stock set to true",
"max_score": 10,
"description": "When updating stock_quantity, manage_stock: true is also included in the request payload"
},
{
"name": "SKU-based product lookup",
"max_score": 10,
"description": "Products are looked up in WooCommerce by SKU using woo.get(\"products\", { sku: ... }) before updating"
},
{
"name": "Failure counting reported",
"max_score": 8,
"description": "The script counts and logs/reports the number of failed updates (e.g., via results filtered by status === 'rejected')"
},
{
"name": "Axios timeout configured",
"max_score": 8,
"description": "The WooCommerceRestApi client is initialized with axiosConfig: { timeout: 15000 } (or similar numeric timeout)"
},
{
"name": "No trailing slash in URL",
"max_score": 5,
"description": "The README or code comments note that WOOCOMMERCE_URL must not end with a trailing slash, OR the code strips the trailing slash before use"
}
]
}
Supplier Catalog Sync to WooCommerce
Problem/Feature Description
A fashion retailer runs their storefront on WooCommerce and receives daily price and stock updates from their supplier in a JSON feed. Currently, a team member manually downloads the feed and updates products one by one in WordPress Admin, which takes hours and introduces errors. The operations team wants a Node.js script that reads the supplier feed and automatically updates prices and stock in WooCommerce.
The challenge is that the supplier catalog contains thousands of products, and naively firing off requests in parallel risks overwhelming the store's server — the hosting plan has limited PHP workers. The team also wants the script to handle partial failures gracefully: if a handful of updates fail, the rest should continue and a failure count should be reported at the end.
Additionally, for products where only prices and stock need changing across many items at once, the script should take advantage of WooCommerce's bulk endpoint to reduce the number of HTTP round-trips.
Output Specification
Produce a Node.js TypeScript project with the following structure:
package.jsonwith required dependenciessrc/sync.ts— the main sync script that:- Reads the supplier feed from
supplier-feed.json(provided below) - Updates prices and stock for each matching WooCommerce product (matched by SKU)
- Uses concurrency control to avoid overwhelming the server
- Reports how many products were updated, skipped (SKU not found), and failed
src/lib/woocommerce.ts— WooCommerce client initializationREADME.mddescribing how to set the required environment variables and run the script
Do not include any real credentials in the code. The script should read configuration from environment variables.
Input Files
The following files are provided as inputs. Extract them before beginning.
=============== FILE: supplier-feed.json =============== [ { "sku": "SHIRT-RED-M", "price": 29.99, "stock": 45 }, { "sku": "SHIRT-RED-L", "price": 29.99, "stock": 12 }, { "sku": "SHIRT-BLUE-M", "price": 27.99, "stock": 0 }, { "sku": "PANTS-BLK-32", "price": 59.99, "stock": 8 }, { "sku": "PANTS-BLK-34", "price": 59.99, "stock": 3 }, { "sku": "JACKET-GRN-L", "price": 149.99, "stock": 20 }, { "sku": "JACKET-GRN-XL", "price": 149.99, "stock": 7 }, { "sku": "HAT-ONE-SIZE", "price": 19.99, "stock": 100 } ]
{
"context": "Tests whether the agent correctly sets up the WooCommerce client, creates orders with the right default fields (status, payment_method, set_paid), properly includes variation_id in line_items for variable products, and separates order note creation from order status updates using the correct endpoint.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct npm package",
"max_score": 8,
"description": "package.json dependencies include @woocommerce/woocommerce-rest-api"
},
{
"name": "Credentials via env vars",
"max_score": 7,
"description": "WooCommerceRestApi is initialized with process.env references for URL, consumer key, and consumer secret — no hardcoded strings"
},
{
"name": "Client version wc/v3",
"max_score": 6,
"description": "WooCommerceRestApi is initialized with version: \"wc/v3\""
},
{
"name": "Axios timeout configured",
"max_score": 6,
"description": "The client is initialized with axiosConfig: { timeout: 15000 } or a numeric timeout value"
},
{
"name": "Order default status pending",
"max_score": 10,
"description": "New orders are created with status: \"pending\" (not \"processing\" or omitted)"
},
{
"name": "Default payment method stripe",
"max_score": 8,
"description": "createOrder sets payment_method to \"stripe\" when no payment method is specified"
},
{
"name": "Payment method title set",
"max_score": 7,
"description": "createOrder sets payment_method_title: \"Credit Card\""
},
{
"name": "set_paid false on creation",
"max_score": 8,
"description": "createOrder sets set_paid: false"
},
{
"name": "variation_id in line_items",
"max_score": 15,
"description": "The example order or createOrder interface includes variation_id as a field in line_items and the example-usage passes variation_id 312 for the variable product"
},
{
"name": "Order note via separate POST",
"max_score": 12,
"description": "When a note is provided to updateOrderStatus, it is posted to orders/{id}/notes endpoint separately (not merged into the PUT status update body)"
},
{
"name": "Error code handling",
"max_score": 8,
"description": "Code references response.data.code or catches WooCommerce-specific error codes (rest_invalid_param, woocommerce_rest_cannot_create, or similar) in error handling"
},
{
"name": "Order listing default status",
"max_score": 5,
"description": "getOrders defaults to fetching orders with status: \"processing\" when no status is specified"
}
]
}
WooCommerce Order Fulfillment Integration
Problem/Feature Description
A small warehouse team uses a third-party fulfillment software that exports completed shipments as a JSON file at the end of each day. Currently, staff manually log into WordPress Admin to mark orders as completed and add internal notes about which shipment batch they were packed in. This is tedious and error-prone when dealing with dozens of orders per day.
The development team has been asked to build a Node.js TypeScript module that bridges the gap: it should be able to create new orders programmatically (for orders that originate outside the storefront, such as phone orders), update the status of existing orders as they move through the fulfillment pipeline, and attach internal notes to orders when their status changes.
A known recurring issue: the WooCommerce store sells clothing items that come in different sizes — the previous ad-hoc scripts sometimes created orders that immediately failed in WordPress with a product-related error. Make sure the order module handles this scenario correctly.
Output Specification
Produce a TypeScript module structured as follows:
package.jsonwith required dependenciessrc/lib/woocommerce.ts— WooCommerce client setupsrc/orders.ts— order management functions including:createOrder(params)— creates a new WooCommerce orderupdateOrderStatus(orderId, status, note?)— updates order status and optionally attaches a notegetOrders(params?)— retrieves a list of orders with pagination infosrc/example-usage.ts— a runnable example demonstrating:
1. Creating a phone order from the customer data in phone-order.json (provided below). Item #2 is a size variant of product 205; its WooCommerce variant record has ID 312. 2. Updating that order's status to "processing" with a note "Packed in batch B-42"
Do not include any real credentials. Use environment variables for all API configuration.
Input Files
The following files are provided as inputs. Extract them before beginning.
=============== FILE: phone-order.json =============== { "customer": { "first_name": "Jane", "last_name": "Doe", "email": "jane.doe@example.com", "address_1": "42 Maple Street", "city": "Portland", "postcode": "97201", "country": "US" }, "items": [ { "product_id": 101, "quantity": 1 }, { "product_id": 205, "quantity": 2, "size": "L", "wc_variant_id": 312 } ] }
{
"context": "Tests whether the agent correctly initializes the WooCommerce client (correct package, version, timeout, no trailing slash warning), reads pagination metadata from the correct response headers, uses the _fields parameter to reduce payload, and implements incremental sync using the 'after' date filter with ISO 8601 timestamps.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct npm package",
"max_score": 8,
"description": "package.json dependencies include @woocommerce/woocommerce-rest-api"
},
{
"name": "Credentials via env vars",
"max_score": 7,
"description": "WooCommerceRestApi is initialized using process.env for WOOCOMMERCE_URL, consumer key, and consumer secret"
},
{
"name": "Client version wc/v3",
"max_score": 6,
"description": "WooCommerceRestApi is initialized with version: \"wc/v3\""
},
{
"name": "Axios timeout configured",
"max_score": 7,
"description": "The WooCommerceRestApi client is configured with axiosConfig: { timeout: 15000 } or equivalent"
},
{
"name": "No trailing slash note",
"max_score": 6,
"description": "README or code comments mention that the WooCommerce URL must NOT end with a trailing slash, OR the code trims it"
},
{
"name": "Pagination from headers",
"max_score": 12,
"description": "Total pages is read from response.headers[\"x-wp-totalpages\"] and total products from response.headers[\"x-wp-total\"] (not from response body)"
},
{
"name": "_fields parameter used",
"max_score": 12,
"description": "Product listing requests include a _fields parameter (e.g., _fields: \"id,name,price,stock_status\" or similar subset) to reduce payload"
},
{
"name": "Default status publish",
"max_score": 6,
"description": "getProducts defaults to fetching products with status: \"publish\" when no status is provided"
},
{
"name": "Default per_page 20",
"max_score": 6,
"description": "getProducts defaults per_page to 20 when no perPage is specified"
},
{
"name": "Incremental sync with after filter",
"max_score": 15,
"description": "getProductsSince (or equivalent) passes the timestamp as the 'after' parameter to the WooCommerce products endpoint"
},
{
"name": "ISO 8601 timestamp",
"max_score": 8,
"description": "The after parameter is documented or used as an ISO 8601 date string (e.g., new Date().toISOString() or similar)"
},
{
"name": "Variations endpoint correct",
"max_score": 7,
"description": "getProductVariations calls woo.get(\"products/{id}/variations\", ...) — uses the nested variations path"
}
]
}
Headless Product Catalog Service
Problem/Feature Description
A startup is building a React Native mobile app for a boutique that runs their inventory on WooCommerce. The app needs a lightweight Node.js backend service that exposes product data to the mobile client. Because the product catalog has over 2,000 items, the service must support pagination so the app can load products lazily as the user scrolls. Response times need to be fast, so the service should avoid fetching unnecessary fields from WooCommerce — the mobile app only needs product IDs, names, prices, and stock status.
The team also wants the service to support an incremental cache-refresh mode: instead of re-fetching the entire catalog on every refresh, it should be able to fetch only products that have been updated since a given point in time, specified as an ISO 8601 timestamp. This will allow a background job to keep a local cache fresh without hammering the WooCommerce server.
Output Specification
Produce a Node.js TypeScript project with:
package.jsonwith required dependenciessrc/lib/woocommerce.ts— WooCommerce client initializationsrc/catalog.ts— catalog functions including:getProducts(params)— paginated product listing that returns products along with total page count and total product countgetProductsSince(isoTimestamp, page?)— fetches products modified after the given ISO 8601 timestamp (for incremental refresh)getProductVariations(productId)— fetches all variations for a variable productsrc/example-usage.ts— demonstrates fetching page 2 of the catalog (10 items per page) and also fetching products updated since a specific timestamp
Do not hardcode any credentials. Use environment variables for WooCommerce configuration.
{
"name": "finsi/woocommerce-rest-api",
"version": "0.1.0",
"summary": "WooCommerce REST API for headless and integration use cases",
"skills": {
"woocommerce-rest-api": {
"path": "SKILL.md"
}
}
}