WooCommerce REST API Authentication: API Keys, Permissions and Common Errors
Almost every WooCommerce REST API problem starts with authentication: a key that only has Read permission, a server that strips the Authorization header, or a request sent over plain HTTP. This guide explains how WooCommerce authenticates API requests, how key permissions work, and how to fix the errors you are most likely to see.
Table of Contents
- Generating API keys
- Read, Write and Read/Write permissions
- Authenticating over HTTPS
- Authenticating over HTTP (OAuth 1.0a)
- Application Passwords and cookie auth
- Common errors and fixes
- Auditing and revoking keys
- Best practices
Generating API Keys
- Go to WooCommerce → Settings → Advanced → REST API and click Add key.
- Enter a Description that identifies the integration (for example "ERP sync – production").
- Choose the User the key acts as. Requests made with the key run with that user's capabilities, so pick a user who can manage WooCommerce, or a dedicated shop-manager account.
- Choose Permissions: Read, Write or Read/Write.
- Click Generate API key and copy the Consumer key (
ck_…) and Consumer secret (cs_…).
The full consumer key is only shown once. WooCommerce stores a hash of it plus the last seven characters (the "truncated key") for display, so if you lose the key you have to generate a new one.
Read, Write and Read/Write Permissions
Key permissions are checked against the HTTP method of each request:
| Permission | Allowed methods | Typical use |
|---|---|---|
| Read | GET, HEAD | Reporting, product feeds, read-only dashboards |
| Write | POST, PUT, PATCH, DELETE | Push-only integrations (rare) |
| Read/Write | All of the above | Sync tools, order management, creating webhooks |
If the method is not allowed you get 401 with the message "The API key provided does not have write permissions." (or read permissions). Creating products, updating orders, running batch requests and creating webhooks through /wc/v3/webhooks all need write access.
The key permission is only the first gate. The endpoint then checks the capabilities of the key's user (for example edit_shop_orders or manage_woocommerce). A Read/Write key owned by a Customer account will still be refused with errors such as woocommerce_rest_cannot_view or woocommerce_rest_cannot_create.
Authenticating over HTTPS
Over HTTPS, send the consumer key as the username and the consumer secret as the password using HTTP Basic Auth:
curl https://example.com/wp-json/wc/v3/orders?per_page=5 \
-u ck_your_consumer_key:cs_your_consumer_secret
In JavaScript (Node 18+ or any modern runtime with fetch):
const auth = Buffer.from(`${process.env.WC_KEY}:${process.env.WC_SECRET}`).toString('base64');
const res = await fetch('https://example.com/wp-json/wc/v3/products?per_page=20', {
headers: { Authorization: `Basic ${auth}` },
});
if (!res.ok) {
const err = await res.json();
throw new Error(`${res.status} ${err.code}: ${err.message}`);
}
const products = await res.json();
console.log(res.headers.get('X-WP-Total'), 'products in total');
If your server does not pass the Authorization header to PHP, WooCommerce also accepts the credentials as query-string parameters over HTTPS:
curl "https://example.com/wp-json/wc/v3/orders?consumer_key=ck_xxx&consumer_secret=cs_xxx"
Query-string credentials end up in server and proxy logs, so treat them as a fallback and fix the header problem when you can (see errors below).
Authenticating over HTTP (OAuth 1.0a)
Basic Auth is refused on non-SSL connections because the secret would travel in clear text. On plain HTTP, WooCommerce requires OAuth 1.0a "one-legged" authentication: each request carries oauth_consumer_key, oauth_timestamp, oauth_nonce, oauth_signature_method (HMAC-SHA1 or HMAC-SHA256) and an oauth_signature computed over the method, URL and sorted parameters, using the consumer secret as the key. There is no token exchange step.
Most WooCommerce client libraries (for example the official @woocommerce/woocommerce-rest-api package for Node) sign requests automatically when the store URL starts with http://. In practice, the better fix is to enable HTTPS – it is required for anything that handles customer data anyway.
Application Passwords and Cookie Authentication
WooCommerce endpoints are ordinary WordPress REST routes, so the core authentication methods work too:
- Application Passwords (WordPress 5.6+): create one under Users → Profile → Application Passwords and send
username:application-passwordas Basic Auth over HTTPS. Access is then governed purely by the user's capabilities – there is no separate read/write scope. - Cookie authentication: for JavaScript running inside wp-admin or on the same site, send the logged-in cookie plus a nonce created with
wp_create_nonce( 'wp_rest' )in theX-WP-Nonceheader.
Prefer WooCommerce API keys for server-to-server integrations: they can be scoped to read-only, show a last-access date, and can be revoked without touching the user's password.
Common Errors and Fixes
| Error | Likely cause | Fix |
|---|---|---|
woocommerce_rest_cannot_view / "Sorry, you cannot list resources." (401) | No credentials reached WooCommerce, or the key's user lacks the capability | Check the header is sent; check the key's user role |
| "Consumer key is missing" over HTTPS | The server strips the Authorization header (common with Apache + CGI/FastCGI) | Pass the header through (below) or use query-string credentials |
| "Consumer key is invalid." / "Consumer secret is invalid." (401) | Typo, revoked key, or key from another site/environment | Regenerate the key and update your secrets store |
| "The API key provided does not have write permissions." | Read-only key used for POST/PUT/DELETE | Edit the key and set Read/Write |
rest_no_route (404) | Wrong namespace or route (e.g. /wc/v1, typo), or plugin inactive | Call GET /wp-json/wc/v3 to list routes |
HTML instead of JSON / 404 on /wp-json/ | Permalinks set to "Plain" or a security plugin blocking the REST API | Use pretty permalinks or call /?rest_route=/wc/v3/products |
On Apache, this .htaccess rule passes the header to PHP:
# Before the WordPress rules
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Alternatively SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 works on many hosts. On Nginx with PHP-FPM the header is normally passed through; if not, add fastcgi_param HTTP_AUTHORIZATION $http_authorization;.
Auditing and Revoking Keys
The REST API settings screen lists every key with its description, truncated key, permissions and Last access date. Revoke keys that are no longer used. If you prefer SQL, keys live in the wp_woocommerce_api_keys table (your prefix may differ):
SELECT key_id, user_id, description, permissions, truncated_key, last_access
FROM wp_woocommerce_api_keys
ORDER BY last_access DESC;
The consumer_key column holds a hash, not the original key, so a database dump does not reveal usable keys – but the consumer_secret column is stored as-is, which is one more reason to protect backups. WordPress Application Passwords are stored separately, hashed, in the _application_passwords user meta.
Best Practices
- One key per integration and environment, with a descriptive name.
- Read-only keys wherever the integration does not need to write.
- A dedicated user (e.g. Shop Manager) as key owner instead of an administrator, so a leaked key cannot install plugins or edit users.
- HTTPS only; never commit keys to Git – load them from environment variables or a secrets manager.
- Rotate keys when staff or vendors change, and check Last access to find stale ones.
- Log the
codefield of error responses; it is far more useful than the HTTP status alone.
Next, see the wc/v3 endpoints reference for the routes and parameters, or the complete WooCommerce REST API guide for a broader overview. Official reference: WooCommerce REST API docs.