Consuming `get_wc_prices` from a Custom / Headless Front End
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.
Table of Contents
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-priceplaceholders 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, actionget_wc_prices(works for logged-in and logged-out visitors). - Send:
action,nonce, andproduct_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-
successbody 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_updatedevents 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.