🇹🇷 Türkçe: Bu yazının Türkçesini oku →

The WooCommerce REST API is the backbone of every headless WooCommerce implementation I have shipped. WPGraphQL gets the attention for its flexibility, and it deserves some of it, but the REST API remains the most battle-tested, best-documented, and most widely-supported option on the table. If you are building your first production headless store, this is where I would start. Here is what actually matters, in the order it will matter to you.

Authentication

WooCommerce REST API supports three authentication methods, and the mistake I see most often is picking the wrong one for the wrong surface. Match the method to who is calling.

Consumer Key/Secret (Server-to-Server)

Best for server-side rendering and backend operations. Generate keys in WooCommerce > Settings > REST API. Keep these on the server. If a consumer secret ever reaches the browser, treat it as burned and rotate it.

import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";

const api = new WooCommerceRestApi({

url: "https://your-store.com",

consumerKey: process.env.WC_CONSUMER_KEY,

consumerSecret: process.env.WC_CONSUMER_SECRET,

version: "wc/v3"

});

JWT Authentication (Client-Side)

For customer-facing operations (viewing orders, managing account), use JWT. Install the JWT Authentication plugin on WordPress.

// Login and get token

const auth = await fetch('https://your-store.com/wp-json/jwt-auth/v1/token', {

method: 'POST',

body: JSON.stringify({ username, password })

});

const { token } = await auth.json();

// Use token for authenticated requests

const orders = await fetch('https://your-store.com/wp-json/wc/v3/orders', {

headers: { 'Authorization': Bearer ${token} }

});

Application Passwords (WordPress 5.6+)

Built into WordPress core. Simple but less suitable for public-facing apps.

Essential Endpoints for Headless Stores

Products

GET    /wp-json/wc/v3/products              # List products

GET /wp-json/wc/v3/products/{id} # Single product

GET /wp-json/wc/v3/products/{id}/variations # Product variations

GET /wp-json/wc/v3/products/categories # Categories

GET /wp-json/wc/v3/products/tags # Tags

GET /wp-json/wc/v3/products/attributes # Attributes (Size, Color, etc.)

Key query parameters:

  • per_page (max 100), page — pagination
  • category — filter by category ID
  • tag — filter by tag ID
  • search — keyword search
  • orderby — date, title, price, popularity, rating
  • min_price, max_price — price range filter
  • stock_status — instock, outofstock, onbackorder

Cart (via CoCart or Store API)

WooCommerce’s native REST API doesn’t include cart endpoints. This is the single fact that trips up most first-time headless builds, so plan for it up front. Use one of these:

WooCommerce Store API (Block-based):

GET    /wp-json/wc/store/v1/cart

POST /wp-json/wc/store/v1/cart/add-item

POST /wp-json/wc/store/v1/cart/remove-item

POST /wp-json/wc/store/v1/cart/update-item

POST /wp-json/wc/store/v1/cart/apply-coupon

CoCart Plugin:

GET    /wp-json/cocart/v2/cart

POST /wp-json/cocart/v2/cart/add-item

DELETE /wp-json/cocart/v2/cart/item/{item_key}

Orders

POST   /wp-json/wc/v3/orders      # Create order (checkout)

GET /wp-json/wc/v3/orders/{id} # Order details

PUT /wp-json/wc/v3/orders/{id} # Update order

Customers

POST   /wp-json/wc/v3/customers        # Register

GET /wp-json/wc/v3/customers/{id} # Profile

PUT /wp-json/wc/v3/customers/{id} # Update profile

Building the Checkout Flow

The headless checkout is the most complex part, and it is where I would spend most of your engineering budget. Everything before it is reads; this is where money moves and where a bug becomes a support ticket. Here’s the typical flow:

1. Cart (Store API) → Collect items
  • Shipping zones → Calculate shipping
  • Payment → Process via gateway
  • Create order → POST /orders with all data
  • Confirmation → Display order details

Creating an order with payment:

const order = await api.post('orders', {

payment_method: 'stripe',

payment_method_title: 'Credit Card',

set_paid: false, // Payment gateway handles this

billing: {

first_name: 'John', last_name: 'Doe',

address_1: '123 Main St', city: 'New York',

state: 'NY', postcode: '10001', country: 'US',

email: '[email protected]', phone: '555-0123'

},

shipping: { / same structure / },

line_items: [

{ product_id: 93, quantity: 2 },

{ variation_id: 1234, quantity: 1 }

],

shipping_lines: [

{ method_id: 'flat_rate', method_title: 'Standard', total: '5.00' }

]

});

Performance Optimization

1. Field Filtering

Don’t fetch entire product objects when you only need title, price, and image:

GET /wp-json/wc/v3/products?_fields=id,name,price,images

2. Batch Requests

Update multiple products in one call:

await api.post('products/batch', {

update: [

{ id: 1, stock_quantity: 50 },

{ id: 2, stock_quantity: 30 }

]

});

3. Caching Strategy

  • Product listings: Cache for 5-10 minutes (ISR in Next.js)
  • Single products: Cache for 1-5 minutes
  • Cart: Never cache
  • Stock levels: Cache for 60 seconds max

4. Pagination

Always paginate. Never fetch all products at once. The response header X-WP-TotalPages tells you how many pages exist.

Common Pitfalls

CORS: WordPress doesn’t enable CORS by default. Add to your theme’s functions.php or use a plugin:

add_action('init', function() {

header('Access-Control-Allow-Origin: https://your-frontend.com');

header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

header('Access-Control-Allow-Headers: Content-Type, Authorization');

});

Rate Limiting: WooCommerce doesn’t have built-in rate limiting, but your hosting provider might. Test under load.

Image URLs: Product images return full WordPress URLs. If your frontend is on a different domain, consider proxying images through your CDN.

REST, Store API, or WPGraphQL: how I would actually choose

People ask me to settle the REST-versus-GraphQL argument as if one has to win. It does not. On a real headless WooCommerce build you almost always end up using two of them together, and the useful question is which does what.

  • Reach for the core REST API for catalog reads, admin sync, and server-to-server work — inventory pushes, order back-office, product imports. It is stable, it is boring, and boring is exactly what you want on the pipe that keeps stock levels honest.
  • Reach for the Store API for the cart and checkout on the customer’s device. It is session-aware and built for exactly the block-driven flow shoppers touch, which is why the native REST API deliberately leaves carts out.
  • Reach for WPGraphQL when your front-end team is drowning in over-fetching and wants one typed query per screen. The cost is another moving part to keep patched. If your data needs are simple, _fields filtering on REST gets you most of the win without it.

My default recommendation for a first production build: REST for catalog and back-office, Store API for cart and checkout, and leave GraphQL out until a real query problem justifies it. Add tools when they solve a pain you can name, not because the architecture diagram looks more modern with three boxes.

The mental model that keeps headless builds sane

Long before REST, I spent years building multi-user games and interactive shows, back in the Macromedia Director and Flash days. The architecture that survived contact with real users was always the same: the server was the single source of truth, and every client on the screen was a disposable view of that truth. If a machine crashed, you did not lose the game — you reconnected and re-rendered, because the client never owned any state worth protecting.

Headless WooCommerce is the same contract wearing modern clothes. WordPress and WooCommerce are the source of truth: products, prices, stock, orders. Your Next.js or Astro front-end is a view — fast, pretty, and completely replaceable. The teams that get into trouble are the ones who quietly let the front-end become the source of truth for something — a cached price that outlives a sale, a stock number the client “knows better” than the server. Keep the authority where the money is, treat the front-end as a rendering of it, and you can rebuild the entire storefront on a new framework in two years without touching a single order. That disposability is the whole point of going headless, and it is worth protecting on purpose.

Conclusion

The WooCommerce REST API is comprehensive enough to power a full headless storefront. The key gaps — cart management and real-time features — are filled by the Store API and CoCart. Focus on field filtering and caching for performance, and plan your checkout flow carefully. The API is stable, well-documented, and ready for production headless commerce. Start there, add GraphQL only when you can name the problem it solves, and keep the server as the source of truth. Do that and the build stays boring in the best possible way.

Leave a Reply

Your email address will not be published. Required fields are marked *

Close Search Window