Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
FREEDOM251

Freedom251 › Blog › Custom Metadata

Using update_meta_data() in WooCommerce: Examples and Best Practices

By Freedom251 Team · · 11 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

update_meta_data() is WooCommerce’s CRUD-safe way to change custom metadata on products, orders, customers, and other WC_Data objects. Load the object with wc_get_product() or wc_get_order(), validate the value, call update_meta_data(), and finish with save(). Without the final save, the in-memory change is discarded.

This guide shows the complete pattern, explains duplicate metadata rows and metadata IDs, compares the CRUD API with direct WordPress metadata functions, and covers REST requests, security, data stores, and the failure modes that make an update appear not to work.

As an Amazon Associate I earn from qualifying purchases.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What update_meta_data() does

WooCommerce stores many records through CRUD (create, read, update, delete) objects. A product is represented by WC_Product, an order by WC_Order, and a customer by WC_Customer. These classes expose getters and setters and hand persistence to a configured data store.

WC_Data::update_meta_data() changes a metadata value on the object currently held in PHP. It accepts a metadata key, a scalar or array value, and optionally a metadata row ID. The method does not, by itself, write the pending change to the database. Call $object->save() after all mutations. WooCommerce introduced CRUD objects in version 3.0 and recommends using them whenever possible instead of directly updating metadata or WordPress post objects.

The lifecycle in four operations

  1. Load: Retrieve the record through its WooCommerce CRUD function.
  2. Validate: Check permissions and convert the input to the expected type.
  3. Mutate: Call update_meta_data() (or a dedicated CRUD setter for a core property).
  4. Persist: Call save(), then reload and verify when the write is important.

The data-store layer decides how metadata is read, added, updated, or deleted. This abstraction matters for compatibility with WooCommerce’s storage changes and with extensions that install a custom data store.

Requirements and the canonical PHP pattern

Your code must run in a context where WooCommerce is loaded and the target ID belongs to the expected object type. The object may be a product, variation, order, customer, coupon, or another class derived from WC_Data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Updating product metadata from an admin form

<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
    return;
}

$value = sanitize_text_field(
    wp_unslash( $_POST['external_ref'] ?? '' )
);

$product->update_meta_data( '_acme_external_ref', $value );
$product->save();

wc_get_product() returns a product object or a false-like result when the ID is invalid or the record cannot be loaded. The guard prevents a fatal error and avoids writing against an unintended record. The leading underscore is conventional for metadata that should remain an implementation detail, but it is not a security control; choose a stable, plugin-specific key to avoid collisions.

Updating order metadata

<?php
$order = wc_get_order( $order_id );
if ( ! $order ) {
    return;
}

$note = sanitize_textarea_field(
    wp_unslash( $_POST['fulfillment_note'] ?? '' )
);

$order->update_meta_data( '_fulfillment_note', $note );
$order->save();

Use a sanitizer that matches the field. A short reference can use sanitize_text_field(); a multiline note can use sanitize_textarea_field(). For numbers, booleans, dates, URLs, or structured arrays, perform type-specific validation instead of treating every value as text.

Mutating several values in one save

<?php
$order = wc_get_order( $order_id );
if ( ! $order ) {
    return;
}

$order->update_meta_data( '_carrier_code', $carrier_code );
$order->update_meta_data( '_dispatch_batch', $batch_id );
$order->update_meta_data( '_dispatch_at', $iso_timestamp );
$order->save();

Queue all related metadata changes on the same object and save once. This keeps the operation easier to reason about and avoids unnecessary writes. If a later hook changes a value before the save, the final value seen by the data store is the one that is persisted.

Understanding the method signature

The method is available on WC_Data objects:

$object->update_meta_data( $key, $value, $meta_id = 0 );
  • $key is the exact metadata key, including any leading underscore.
  • $value can be a scalar or an array. The consuming code must know which representation it expects.
  • $meta_id is optional. Supply it when you must update one particular metadata row among duplicates.

When no metadata row matches the key, the data-store operation can create a row when the object is saved. When a matching row exists, WooCommerce applies its internal key-based metadata handling. Do not rely on an update call as an implicit “append another value” operation; model multi-value behavior explicitly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Updating one duplicate metadata row by ID

WordPress metadata permits multiple rows with the same key. That can happen because a legacy importer wrote duplicates, an extension intentionally stores repeated values, or an earlier implementation did not enforce uniqueness. A key-only update cannot express which duplicate you mean.

Use the metadata row ID

<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
    return;
}

$meta_id = 123; // Obtain the real metadata row ID for this product.
$product->update_meta_data( '_legacy_code', 'NEW-VALUE', $meta_id );
$product->save();

The ID must be the real metadata row ID belonging to that product. Do not hard-code an example ID in production and do not accept an arbitrary ID from a request without checking ownership. Retrieve the metadata records, identify the row associated with the current object, then pass that ID to the setter. Reload the product and inspect the row again if the update is part of a migration.

When key-based updating is sufficient

Use only the key when the design treats the key as one current value, such as _acme_external_ref or _fulfillment_note. This is the simplest and most interoperable design: other extensions can read one predictable value and your code does not need to track row IDs.

Single-value and multi-value metadata designs

Single current value

For a field that has one meaning at a time, update the same key on every change. Examples include an external reference, a fulfillment note, or the identifier of the current export batch. Document the expected type and whether an empty value means “clear” or “unknown.” If clearing should remove the row entirely, use the object’s metadata deletion method rather than relying on an empty string to have special meaning.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Several values under one key

If a key intentionally represents a collection, choose a deliberate representation:

  • Store one serialized array and always read and validate the array as a unit.
  • Store separate rows and manage each row by metadata ID.
  • Use distinct keys when each value has a different semantic role.

Test how the consuming code reads the values. A generic update call should not be assumed to append a new row, preserve ordering, or update every duplicate. Those details belong to the data-store behavior and to your data model.

Naming, validation, and security

Choose collision-resistant keys

Prefix keys with a plugin or company identifier, for example _acme_external_ref. Avoid generic names such as status, code, or note, which are more likely to collide with another extension. Keep the key stable once other code depends on it; renaming it later requires a migration or a compatibility read.

Validate before mutation

  • Text: Unsplash request data, then use an appropriate text sanitizer.
  • Integer: Convert with a strict integer routine and reject values outside the permitted range.
  • Decimal: Validate the decimal format and precision required by the business rule.
  • Boolean: Accept only documented true and false representations.
  • Date/time: Parse and normalize to the format your integrations expect.
  • Array: Validate each member and reject unexpected keys before storing.

Sanitizing is not authorization. For values submitted from an administrator form, check the user’s capability and verify a nonce before accepting the request. For integrations, authenticate the caller and authorize access to the specific product or order. Never let a client choose an arbitrary metadata row ID without confirming that it belongs to the object being edited.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not shadow core WooCommerce properties

Price, stock, status, tax class, SKU, and other supported properties have dedicated CRUD setters and validation. Use those setters instead of creating custom metadata that imitates a core field. A shadow value can diverge from the value used by checkout, reports, inventory, or another extension.

Why update_meta_data() appears not to save

Most failures are lifecycle or key mismatches rather than a defect in the method. Work through the checks in this order.

  1. Confirm the object loaded. Check the return value of wc_get_product(), wc_get_order(), or the relevant CRUD loader. An invalid ID means there is no object to mutate.
  2. Compare keys character for character. _external_ref and external_ref are different keys. Leading underscores, capitalization, and invisible whitespace matter.
  3. Check the value before saving. Log or inspect the validated value, not the raw request. A sanitizer may legitimately return an empty string if the input is invalid.
  4. Call save(). A setter changes the in-memory object. Unsaved changes are discarded when the request ends.
  5. Look for later mutations. Hooks, imports, synchronization jobs, and checkout code can overwrite the value before or after your save. Trace the complete request and hook order.
  6. Reload and read back. Load a fresh object after saving and call get_meta() or the relevant getter. This distinguishes a persistence problem from a stale in-memory object.
  7. Handle duplicates deliberately. If several rows share a key, obtain the row ID and update the intended row by ID.
  8. Inspect the data store and hooks. A custom data store or extension may change persistence behavior. Verify that its read, update, and save methods are being invoked.

A minimal read-back check

<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
    return;
}

$product->update_meta_data( '_acme_external_ref', $value );
$product->save();

$reloaded = wc_get_product( $product_id );
$stored   = $reloaded ? $reloaded->get_meta( '_acme_external_ref', true ) : null;

Use this pattern in a test or diagnostic path, not as a reason to reload repeatedly in a high-volume loop. In bulk jobs, batch work carefully and record failures with the object ID and key.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

CRUD objects versus direct WordPress metadata functions

update_post_meta() can still be useful for WordPress data that is not owned by WooCommerce, but it is not the preferred path for WooCommerce entities. CRUD objects provide structured properties, route writes through the configured data store, and give WooCommerce and extensions a consistent place to apply validation and hooks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Choice Best fit Persistence and compatibility Main risk
WooCommerce CRUD object plus update_meta_data() Custom metadata on products, orders, customers, and other WC_Data records Requires an explicit save(); honors the WooCommerce data-store abstraction Forgetting to save or using the wrong key
Direct update_post_meta() Non-WooCommerce post metadata or narrowly scoped legacy code Writes directly through WordPress post metadata and can bypass WooCommerce object behavior Less compatible with alternate WooCommerce storage and extension hooks
Dedicated CRUD setter Core properties such as price, stock, SKU, and status Uses WooCommerce’s structure and validation for that property Using custom metadata instead can create conflicting values

If an existing legacy integration uses direct metadata calls, migrate deliberately: read the old key, write through the CRUD object, verify the result, and retain a compatibility read only for as long as older records require it.

Updating metadata through the WooCommerce REST API

REST writes carry metadata as JSON in a meta_data array. An authenticated WooCommerce REST endpoint can receive a payload such as:

{
  "meta_data": [
    {"key": "_external_ref", "value": "ABC-123"}
  ]
}

WooCommerce’s REST metadata utility normalizes entries before applying them to the target WC_Data object. Entries without a key are skipped, defaults are applied for missing fields, and valid entries are passed to update_meta_data(). The endpoint then persists the object as part of the REST update flow.

REST implementation decisions

  • Use authenticated WooCommerce REST endpoints for writes; do not expose an unauthenticated write route.
  • Keep the key and value schema identical between REST clients and PHP code.
  • Validate the value at the endpoint boundary and again in domain code when multiple clients can write it.
  • Use the metadata ID in the payload when the operation must target one duplicate row, and verify that the ID belongs to the requested resource.
  • Do not confuse the Store API with an administrative write API. The Store API is public and intended for customer-facing cart, checkout, and product functionality.

Key-based updates, metadata-ID updates, and REST: choosing the right operation

Decision Use it when What you must guarantee
Key-based PHP update The key represents one current value Stable key naming, validated value, and a final save()
Metadata-ID PHP update Several rows share a key and exactly one row must change The row ID belongs to the loaded object and the intended legacy or repeated entry
REST JSON update An authenticated external client owns the write workflow Authentication, authorization, JSON validation, and correct meta_data entries
Dedicated CRUD setter The value is a WooCommerce core property Use the property’s supported type and save the object

Working safely with hooks and custom data stores

WooCommerce and extensions can attach behavior to object reads, metadata changes, and saves. A value that looks correct immediately after the setter may be changed by another callback before persistence, or a later process may replace it after the request. When diagnosing this, identify every writer of the key and record the order in which callbacks run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The data store is the bridge between a CRUD class and database storage. A custom data store can implement different storage details while preserving the object API. That is a reason to prefer the CRUD method over a direct database or post-meta write: your code states its intent at the object layer instead of assuming a particular table or column.

Testing an update implementation

A useful test covers both the write and the read path. Create or select a fixture product or order, write a known value, save it, reload a fresh object, and assert the exact key and value. Add cases for an invalid object ID, an empty or malformed value, a duplicate key with a selected metadata ID, and a later hook that attempts to overwrite the value.

  • Test that a missing nonce or capability blocks an administrator-form write.
  • Test that the leading underscore is preserved.
  • Test scalar and array values separately; do not assume their retrieval shape is identical.
  • Test that a key-only update does not silently implement an append operation your design did not specify.
  • For REST, test missing keys, missing values, invalid IDs, unauthorized callers, and a valid authenticated payload.

A production checklist

  • Load the record with the correct WooCommerce CRUD function.
  • Use a stable, plugin-specific metadata key.
  • Check capability and nonce for administrator forms; authenticate and authorize REST callers.
  • Validate and normalize the value for its real type.
  • Use a dedicated CRUD setter for supported WooCommerce properties.
  • Use a metadata ID when duplicate rows make the key ambiguous.
  • Call save() after all changes.
  • Reload and verify important writes.
  • Trace hooks and custom data-store behavior when values are overwritten or missing.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Custom MetadataPHPREST APIWooCommerceWordPress

Leave a Reply

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

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.