Developer reference

Shopify Metafield API for Personalization Options

Metafields are Shopify's typed key-value store attached to any resource (product, variant, order, customer, shop, market, company). For personalization apps, they're the correct place to store per-variant DPI requirements, POD partner routing, character limits, brand-approved fonts, and any custom personalization schema. Here is the complete 2026 API reference — definitions, types, validation, metaobjects, and how to expose them to the Storefront.

Last updated: August 23, 2026~11 min readBy the Print It My Way team

Why metafields, not tags

Product tags used to be the go-to way to attach custom data to Shopify products. They still exist, but metafields have surpassed them for structured data: metafields are typed (integer vs string vs date vs file), validated (min/max, allowed values), and namespaced (no collisions across apps). Tags are unstructured strings, easy to overwrite, and impossible to query efficiently at scale. Migrate all personalization data to metafields.

Metafield definitions (the schema layer)

Define a metafield once, then set values against that definition on many resources. Definitions enforce type + validation + visibility across every resource of the owner type:

mutation CreateDefinition {
  metafieldDefinitionCreate(definition: {
    namespace: "personalization"
    key: "engraving_max_chars"
    name: "Engraving max characters"
    description: "Maximum characters allowed in the engraving field"
    type: "number_integer"
    ownerType: PRODUCTVARIANT
    validations: [
      { name: "min", value: "1" }
      { name: "max", value: "100" }
    ]
    access: {
      admin: MERCHANT_READ_WRITE
      storefront: PUBLIC_READ
    }
  }) {
    createdDefinition { id }
    userErrors { field message }
  }
}

Metafield types

TypeUse forExample value
single_line_text_fieldShort labels, IDs"engraving_v2"
multi_line_text_fieldDescriptions, print instructions"Print in center, 300 DPI..."
number_integerCharacter limits, DPI, quantity300
number_decimalPrices, weights (precise)5.99
booleanFeature flagstrue
date_timeLead time, cutoff dates"2026-09-01T12:00:00Z"
colorHex swatches"#008060"
file_referenceBrand logos, print templatesgid://shopify/GenericFile/...
list.single_line_text_fieldAllowed fonts, allowed positions["Inter", "Playfair"]
metaobject_referenceReference a structured objectgid://shopify/Metaobject/...
jsonNested/complex data{"positions": [{...}]}

Setting metafield values

mutation SetMetafields {
  metafieldsSet(metafields: [
    {
      ownerId: "gid://shopify/ProductVariant/40123"
      namespace: "personalization"
      key: "engraving_max_chars"
      value: "20"
      type: "number_integer"
    }
    {
      ownerId: "gid://shopify/ProductVariant/40123"
      namespace: "personalization"
      key: "allowed_fonts"
      value: "[\"Inter\", \"Playfair Display\", \"Roboto\"]"
      type: "list.single_line_text_field"
    }
  ]) {
    metafields { id key value }
    userErrors { field message }
  }
}

Metaobjects — structured personalization schemas

When a single metafield type isn't enough — e.g., a "personalization option" needs fields like name + type + max_chars + required + price_adjustment — use a metaobject. A metaobject is a Shopify-native custom type with its own fields:

mutation CreateMetaobjectDefinition {
  metaobjectDefinitionCreate(definition: {
    type: "personalization_option"
    name: "Personalization option"
    fieldDefinitions: [
      { key: "name", type: "single_line_text_field", validations: [{name:"required", value:"true"}] }
      { key: "field_type", type: "single_line_text_field" }  // text | dropdown | upload
      { key: "max_chars", type: "number_integer" }
      { key: "required", type: "boolean" }
      { key: "price_adjustment", type: "number_decimal" }
    ]
    access: { storefront: PUBLIC_READ }
  }) {
    metaobjectDefinition { id }
    userErrors { field message }
  }
}

Exposing metafields to the Storefront

Metafields with storefront: PUBLIC_READ access are queryable via the Storefront API. Without this flag, they're admin-only:

query VariantMetafields($id: ID!) {
  productVariant(id: $id) {
    id
    metafield(namespace: "personalization", key: "engraving_max_chars") {
      value
    }
    allowed: metafield(namespace: "personalization", key: "allowed_fonts") {
      value  // JSON-encoded array; parse client-side
    }
  }
}

Reading metafields in Liquid (themes)

<input type="text"
       maxlength="{{ product.selected_variant.metafields.personalization.engraving_max_chars }}">

{% assign allowed = product.selected_variant.metafields.personalization.allowed_fonts %}
{% for font in allowed.value %}
  <option value="{{ font }}">{{ font }}</option>
{% endfor %}

Rate limits + bulk operations

Metafield writes cost 10 points per call (of the ~1000 pt/min GraphQL budget). Bulk-updating 500 variants' metafields one at a time takes 5+ minutes. Use metafieldsSet with up to 25 metafields per call, or bulkOperationRunMutation for large-scale updates.

Namespace conventions

Reserve namespaces per feature: personalization, pod_routing, brand_assets, compliance. Never use the custom namespace for app data — that's for merchant-defined fields in the admin. Apps that pollute custom break the merchant's admin UX.

Frequently asked questions

What are metafields on Shopify?

Typed key-value pairs attached to Shopify resources (product, variant, order, customer, shop, market, company). Used for custom data that doesn't fit the built-in schema. Since 2024, metafields have full definitions with type validation, storefront access controls, and namespace organization.

Should I use tags or metafields for personalization data?

Metafields — always. Tags are unstructured strings prone to overwrites and typos. Metafields are typed, validated, and namespaced. Migrate any personalization data currently in tags to metafields.

How do I make a metafield readable from the Storefront API?

Set the metafield definition's access to storefront: PUBLIC_READ. Metafields without this flag are admin-only. Existing metafields without a definition require creating a definition first, then linking existing values.

What's the difference between metafields and metaobjects?

Metafields are single typed key-value pairs attached to a resource. Metaobjects are structured multi-field custom types you define — like a mini-schema. Use metaobjects when a personalization option has multiple related fields (name + type + max_chars + price).

What namespace should my personalization app use for metafields?

Reserve your own — e.g., my_app_personalization. Never use custom (reserved for merchant admin fields) or common names like options (collision risk with other apps).

How many metafields can I set at once via API?

metafieldsSet accepts up to 25 metafields per call. Beyond that, use bulkOperationRunMutation for staged imports of thousands of metafield values.

Do metafields have search/filter capability?

Yes — Shopify's admin search and Storefront API's metafieldValueFilter both support metafield-based filtering. Filterable metafields need to be defined with the appropriate index setting.

Best pattern for storing per-variant DPI requirements?

Metafield definition: namespace: "personalization", key: "dpi_min", type: "number_integer", owner: PRODUCTVARIANT, storefront: PUBLIC_READ. Set 300 (default), 600 (high-res), 1200 (special finishes) per variant.

Related reading

Try it free on Shopify

Print It My Way uses typed metafield definitions for all personalization data — DPI, char limits, allowed fonts, POD routing — exposed via Storefront API for headless-compatible themes.

Install Print It My Way