Consuming `get_wc_prices` from a Custom / Headless Front End

June 5, 2026

A worked example of calling the plugin’s pricing endpoint yourself — for a headless build, a custom theme component, or a queueing layer — instead of relying on the bundled script. For the formal contract, see Developer Reference → AJAX request / response contract.

When you’d do this

  • A headless front end (React/Vue/Next) that renders catalogue cards from cached data and wants live, per-visitor prices.
  • A custom component (e.g. a bespoke product carousel) that the default .ajax-price placeholders don’t cover.
  • A batching/queueing layer that fetches prices for many products on a schedule.

If you just want the normal behaviour on a standard WooCommerce theme, you don’t need any of this — the bundled script already does it.

The essentials

  • Endpoint: admin-ajax.php, action get_wc_prices (works for logged-in and logged-out visitors).
  • Send: action, nonce, and product_ids[].
  • Get back: { success, data: { prices, stock? } }, keyed by product ID.
  • Same-origin & cookies: send the request with credentials so WooCommerce resolves the visitor’s session (geolocation, currency, cart, VAT). A cross-origin headless app must proxy through the same domain or otherwise carry the Woo session — otherwise prices resolve for an anonymous default location.

Getting a nonce

The bundled script reads the nonce from the wc_ajax_pricing_params object that the plugin localizes onto standard front-end pages. If your custom surface loads on a normal WP page, that global is already present:

const { ajax_url, nonce } = window.wc_ajax_pricing_params;

For a fully headless front end that never loads the WP-rendered page, expose a nonce another way — e.g. a tiny mu-plugin endpoint that returns wp_create_nonce('wc-ajax-pricing-nonce') for the current session. (Note the nonce is weak-by-design on cached pages — the endpoint is safe because it only returns public, published-product data and changes nothing. Don’t treat the nonce as an access control.)

Example: fetch and swap

async function refreshPrices(productIds) {
  const { ajax_url, nonce } = window.wc_ajax_pricing_params;

  const body = new URLSearchParams();
  body.set('action', 'get_wc_prices');
  body.set('nonce', nonce);
  productIds.forEach(id => body.append('product_ids[]', id));

  const res = await fetch(ajax_url, {
    method: 'POST',
    credentials: 'same-origin',   // critical: carries the Woo session
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body,
  });

  const json = await res.json();
  if (!json.success) return;                     // {success:false} → leave cached prices

  for (const [id, data] of Object.entries(json.data.prices)) {
    const el = document.querySelector(`[data-product-id="${id}"] .price-target`);
    if (el) el.innerHTML = data.price_html;       // server-rendered, safe HTML
  }
}

Example: with stock

If Refresh stock via AJAX is enabled, each data.stock[id] carries availability_html, is_in_stock, is_purchasable, add_to_cart_html, and any adapter fields (bundled_items, components):

if (json.data.stock) {
  for (const [id, s] of Object.entries(json.data.stock)) {
    // s.is_in_stock, s.availability_html, s.add_to_cart_html, s.bundled_items, …
  }
}

Practical notes

  • Batch, don’t loop. Send all IDs in one request — that’s the whole performance point. The server caps a single request at ssap_max_products_per_request (default 200); for more products, chunk into batches of ≤200.
  • Only published products return data. Draft/private/scheduled IDs are silently omitted — don’t expect every requested ID back.
  • De-dupe your IDs before sending; the server does too, but it saves payload.
  • Failure handling: on a non-success body or a network error, keep whatever price you already showed (that’s what the bundled script does via its cached-HTML fallback).
  • React to updates: on a standard page you can also just listen for the wc_ajax_pricing_updated / wc_ajax_inventory_updated events the bundled script fires, rather than calling the endpoint yourself (Developer Reference → JavaScript events).

Server-side alternative

If you’re rendering server-side (PHP) rather than via the browser, you don’t need the endpoint at all — call WooCommerce directly: wc_get_product($id)->get_price_html() runs the same pricing stack the endpoint uses. The AJAX endpoint exists specifically to move that computation to after a cached page has been served to the browser.

×
1/1