Write metafields with metafieldsSet
Create or update up to 25 metafields on any resource in one atomic call with metafieldsSet, with compareDigest to avoid overwriting newer values.
- API
- Admin GraphQL API
- Operation
- metafieldsSet
- Access scope
- Write access to the owner resource, such as products or customers
- Replaces (REST)
- POST /admin/api/products/{id}/metafields.json and PUT /admin/api/metafields/{id}.json
- API version
- 2026-10
- Last checked
In short
To write metafields, call the metafieldsSet mutation with a list of inputs: ownerId, namespace, key, type and value. It creates the metafield if it doesn’t exist and updates it if it does. One call takes up to 25 metafields across any owners and is atomic: if one fails, none are saved. Pass compareDigest to update only when the stored value hasn’t changed.
How to use it
- Build one input per value: ownerId (any resource with metafields), namespace, key, type and value. The value is always a string, also for JSON, numbers and lists.
- If a metafield definition exists for that namespace and key, you can leave out type; otherwise it’s required.
- Send SetProductMetafields and check userErrors; nothing is saved when it isn’t empty.
- For safe updates, read compareDigest first and send it back. Send compareDigest: null to write only if the metafield doesn’t exist yet.
Code
metafields-set.graphqlmutation SetProductMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
namespace
key
type
value
compareDigest
}
userErrors {
code
field
message
}
}
}
metafields-set.variables.json{
"metafields": [
{
"ownerId": "gid://shopify/Product/1234567890",
"namespace": "custom",
"key": "material",
"type": "single_line_text_field",
"value": "100% linen"
},
{
"ownerId": "gid://shopify/Product/1234567890",
"namespace": "custom",
"key": "care_guide",
"type": "multi_line_text_field",
"value": "Wash at 30°C.\nDo not tumble dry.",
"compareDigest": null
}
]
}
metafields-set.response.json{
"data": {
"metafieldsSet": {
"metafields": [
{
"id": "gid://shopify/Metafield/1111111111",
"namespace": "custom",
"key": "material",
"type": "single_line_text_field",
"value": "100% linen",
"compareDigest": "<digest from the response>"
},
{
"id": "gid://shopify/Metafield/1111111112",
"namespace": "custom",
"key": "care_guide",
"type": "multi_line_text_field",
"value": "Wash at 30°C.\nDo not tumble dry.",
"compareDigest": "<digest from the response>"
}
],
"userErrors": []
}
}
}
Written by Lintel. MIT license: free to use in your own store and in client work.
Limits and edge cases
- At most 25 metafields per call and a 10 MB request.
- Leaving out namespace uses your app’s reserved namespace, not custom.
- Values must match the type: list types take a JSON array as a string, references take GIDs, and the request fails when one doesn’t parse.
- To remove a metafield, use the metafieldsDelete mutation.
Questions
- How do I update a product metafield with GraphQL?
- Call metafieldsSet with the product’s GID as ownerId, plus namespace, key, type and the new value. It updates the existing metafield in place.
- Can I set metafields on different products in one call?
- Yes. Each input has its own ownerId, so one call can write up to 25 metafields across many resources.
- What is compareDigest for?
- It stops you from overwriting a value someone else changed after you read it. If the stored value no longer matches the digest, the mutation returns an error instead of saving.
Shopify documentation
- Validated against the Admin or Storefront API schema for version 2026-10 on the date shown. Check the version in your request URL.
- IDs and store addresses in the examples are placeholders. Replace them with your own.
- Admin API calls need an access token from your app. Never put an Admin API token in a storefront or theme.
- MIT license. Use the code in your own store or in client work.
Need this built, tested and maintained for your store? See API integrations.
