Skip to main content

Product and variant publishing

Publishing controls which products and variants are visible to buyers on a given sales channel, market, or B2B context. The publishing framework is built on catalogs and publications.

Every selling context on a Shopify store has a catalog:

  • AppCatalog represents sales channels like the Online Store, Point of Sale, or custom channels.
  • MarketCatalog represents international markets such as North America or Europe.
  • CompanyLocationCatalog represents B2B company locations.

Each catalog can have an associated publication, which is the group of products and collections visible to buyers in that context. To make a product visible, you publish it to that catalog's publication. To hide it, you unpublish it.

The Publishable interface is implemented by Product, ProductVariant, and Collection, giving you the same publishing controls across all three resource types.


  • Your app can make authenticated requests to the GraphQL Admin API.
  • Your app has the read_publications and write_publications access scopes. You also need read_products to query products.
  • Your store has existing products.
  • Your store has at least one active sales channel. Most stores have the Online Store by default.

Anchor to Discovering catalogs and publicationsDiscovering catalogs and publications

Before you can publish products, you need to know what publications exist on the shop. You can discover them in two ways: by querying catalogs, or by querying publications directly.

Anchor to Query publications directlyQuery publications directly

Use the publications query for a flat list of all publications on the shop. This is simpler when you just need publication IDs for mutations.

POST /api/2026-07/graphql.json

query Publications {
publications(first: 20) {
nodes {
id
autoPublish
supportsFuturePublishing
catalog {
id
title
}
}
}
}
{
"data": {
"publications": {
"nodes": [
{
"id": "gid://shopify/Publication/123",
"autoPublish": true,
"supportsFuturePublishing": true,
"catalog": {
"id": "gid://shopify/AppCatalog/1",
"title": "Online Store"
}
},
{
"id": "gid://shopify/Publication/456",
"autoPublish": false,
"supportsFuturePublishing": false,
"catalog": {
"id": "gid://shopify/AppCatalog/2",
"title": "Point of Sale"
}
}
]
}
}
}
Info

You can filter publications by catalog type using the catalogType argument. For example, publications(first: 20, catalogType: MARKET) returns only publications associated with market catalogs.

Use the catalogs query to discover what selling contexts exist on the shop. The response can include app catalogs, market catalogs, and B2B company location catalogs.

POST /api/2026-07/graphql.json

query {
catalogs(first: 3) {
nodes {
id
title
status
}
}
}
{
"data": {
"catalogs": {
"nodes": [
{
"id": "gid://shopify/MarketCatalog/1068177679",
"title": "NA Market",
"status": "ACTIVE"
},
{
"id": "gid://shopify/CompanyLocationCatalog/1068177680",
"title": "B2B Example 1",
"status": "DRAFT"
},
{
"id": "gid://shopify/AppCatalog/1068177681",
"title": "App Catalog Example",
"status": "ARCHIVED"
}
]
}
}
}

Anchor to Publishing products to sales channels and catalogsPublishing products to sales channels and catalogs

Use the publishablePublish mutation to publish a product to one or more publications. Pass the product's GID and an array of publication IDs. If the product is already published to a given publication, the mutation succeeds with no change.

POST /api/2026-07/graphql.json

mutation PublishProduct($id: ID!, $input: [PublicationInput!]!) {
publishablePublish(id: $id, input: $input) {
publishable {
... on Product {
id
title
}
availablePublicationsCount {
count
}
resourcePublicationsCount {
count
}
}
userErrors {
field
message
}
}
}
{
"id": "gid://shopify/Product/1",
"input": [
{
"publicationId": "gid://shopify/Publication/123"
},
{
"publicationId": "gid://shopify/Publication/456"
}
]
}
{
"data": {
"publishablePublish": {
"publishable": {
"id": "gid://shopify/Product/1",
"title": "Classic Snowboard",
"availablePublicationsCount": {
"count": 3
},
"resourcePublicationsCount": {
"count": 3
}
},
"userErrors": []
}
}
}

The input field accepts an array, so you can publish to multiple publications in a single mutation call.

Use the publishableUnpublish mutation to remove a product from one or more publications. The product isn't deleted. It remains in your store but becomes unavailable to buyers on those channels.

POST /api/2026-07/graphql.json

mutation UnpublishProduct($id: ID!, $input: [PublicationInput!]!) {
publishableUnpublish(id: $id, input: $input) {
publishable {
... on Product {
id
title
}
availablePublicationsCount {
count
}
resourcePublicationsCount {
count
}
}
userErrors {
field
message
}
}
}
{
"id": "gid://shopify/Product/1",
"input": [
{
"publicationId": "gid://shopify/Publication/456"
}
]
}
{
"data": {
"publishableUnpublish": {
"publishable": {
"id": "gid://shopify/Product/1",
"title": "Classic Snowboard",
"availablePublicationsCount": {
"count": 3
},
"resourcePublicationsCount": {
"count": 2
}
},
"userErrors": []
}
}
}
Info

If your app is itself a sales channel, you can use the publishablePublishToCurrentChannel and publishableUnpublishToCurrentChannel convenience mutations to publish resources to your app's own channel without specifying a publication ID. This guide focuses on publishing to any publication using explicit IDs.


Anchor to Reading publication stateReading publication state

After publishing or unpublishing products, you can verify their publication state using several fields on the Product object.

Anchor to Check a specific publicationCheck a specific publication

Use publishedOnPublication to check whether a product is published to a specific publication.

POST /api/2026-07/graphql.json

query ProductPublicationCheck($id: ID!, $publicationId: ID!) {
product(id: $id) {
title
publishedOnPublication(publicationId: $publicationId)
}
}
{
"id": "gid://shopify/Product/1",
"publicationId": "gid://shopify/Publication/123"
}
{
"data": {
"product": {
"title": "Classic Snowboard",
"publishedOnPublication": true
}
}
}

Anchor to List all publications for a productList all publications for a product

Use resourcePublicationsV2 to get a list of all publications a product is published to, along with the publication date.

POST /api/2026-07/graphql.json

query ProductPublications($id: ID!) {
product(id: $id) {
title
resourcePublicationsV2(first: 20) {
nodes {
publication {
id
}
isPublished
publishDate
}
}
}
}
{
"id": "gid://shopify/Product/1"
}
{
"data": {
"product": {
"title": "Classic Snowboard",
"resourcePublicationsV2": {
"nodes": [
{
"publication": {
"id": "gid://shopify/Publication/123"
},
"isPublished": true,
"publishDate": "2026-01-15T10:00:00Z"
},
{
"publication": {
"id": "gid://shopify/Publication/456"
},
"isPublished": true,
"publishDate": "2026-01-15T10:00:00Z"
}
]
}
}
}
}

Anchor to List publications where a product is not publishedList publications where a product is not published

Use unpublishedPublications to find publications where the product could be published but currently isn't.

POST /api/2026-07/graphql.json

query ProductUnpublished($id: ID!) {
product(id: $id) {
title
unpublishedPublications(first: 20) {
nodes {
id
}
}
}
}
{
"id": "gid://shopify/Product/1"
}
{
"data": {
"product": {
"title": "Classic Snowboard",
"unpublishedPublications": {
"nodes": [
{
"id": "gid://shopify/Publication/789"
}
]
}
}
}
}

Anchor to Managing publications at scaleManaging publications at scale

The mutations above are resource-centric: you pick a product and manage which publications it appears in. For the inverse pattern, picking a publication and managing which products it contains, use the publicationUpdate mutation.

POST /api/2026-07/graphql.json

mutation UpdatePublication($id: ID!, $input: PublicationUpdateInput!) {
publicationUpdate(id: $id, input: $input) {
publication {
id
}
userErrors {
field
message
}
}
}
{
"id": "gid://shopify/Publication/123",
"input": {
"publishablesToAdd": [
"gid://shopify/Product/1",
"gid://shopify/Product/2"
],
"publishablesToRemove": [
"gid://shopify/Product/3"
]
}
}
{
"data": {
"publicationUpdate": {
"publication": {
"id": "gid://shopify/Publication/123"
},
"userErrors": []
}
}
}

Use publishablePublish when managing a single product's channel presence. Use publicationUpdate when managing a publication's product catalog.


Anchor to Variant-level publishingVariant-level publishing

Variant-level publishing lets you control visibility at the individual variant level. By default, when a product is published to a sales channel or catalog, all of its variants are visible to buyers. With variant-level publishing, you can publish or unpublish specific variants independently.

This is useful for scenarios like:

  • Product launches: Prepare new colorways or sizes without exposing them to buyers until you're ready.
  • Region-based restrictions: Hide variants from certain markets due to regulatory or shipping constraints.
  • Channel-specific catalogs: Show different variant selections on different sales channels.
  • Lifecycle management: Retire variants from storefronts without deleting them, preserving historical data and analytics.

The ProductVariant object implements the Publishable interface, giving it the same publishing controls that exist for products and collections.

By default, all variants are published on all channels and catalogs where their parent product is published. This ensures apps and merchants can continue their current product creation and publishing flow without implementing variant publishing support.

A variant is visible to buyers on a given sales channel or catalog when the following are all true:

  1. The product has an Active status (or Unlisted on supported channels).
  2. The product is published to that channel or catalog.
  3. The variant is published to that channel or catalog.
Product stateVariant stateVisible to buyers
PublishedPublishedYes
PublishedUnpublishedNo
UnpublishedPublishedNo
UnpublishedUnpublishedNo

Variant and product publishing states are independent:

  • Publishing or unpublishing a variant doesn't affect the parent product's publication state.
  • Publishing or unpublishing a product doesn't change any variant's publication state.

Variant publishing state persists across product publishing changes, so you can configure variant visibility before publishing the product.

Anchor to Query variant publication statusQuery variant publication status

The ProductVariant object includes all fields from the Publishable interface. You can check whether a variant is published to a specific channel, list all of its publications, or count its available publications.

POST /api/2026-07/graphql.json

query VariantPublicationStatus {
productVariant(id: "gid://shopify/ProductVariant/123") {
title
publishedOnPublication(
publicationId: "gid://shopify/Publication/456"
)
resourcePublicationsCount {
count
}
}
}
{
"data": {
"productVariant": {
"title": "Red / Small",
"publishedOnPublication": true,
"resourcePublicationsCount": {
"count": 3
}
}
}
}

Use resourcePublicationsV2 and unpublishedPublications to get the full picture of where a variant is and isn't published:

POST /api/2026-07/graphql.json

query VariantPublications {
productVariant(id: "gid://shopify/ProductVariant/123") {
title
resourcePublicationsV2(first: 10) {
nodes {
publication {
id
}
isPublished
publishDate
}
}
unpublishedPublications(first: 10) {
nodes {
id
}
}
}
}
{
"data": {
"productVariant": {
"title": "Red / Small",
"resourcePublicationsV2": {
"nodes": [
{
"publication": {
"id": "gid://shopify/Publication/123"
},
"isPublished": true,
"publishDate": "2026-01-15T10:00:00Z"
}
]
},
"unpublishedPublications": {
"nodes": [
{
"id": "gid://shopify/Publication/789"
}
]
}
}
}
}

Anchor to Publish a variant to a sales channelPublish a variant to a sales channel

Use the publishablePublish mutation with a ProductVariant GID to publish a variant to one or more publications. If the variant is already published to that publication, the mutation succeeds with no change.

POST /api/2026-07/graphql.json

mutation PublishVariant($id: ID!, $input: [PublicationInput!]!) {
publishablePublish(id: $id, input: $input) {
publishable {
... on ProductVariant {
id
title
}
}
userErrors {
field
message
}
}
}
{
"id": "gid://shopify/ProductVariant/123",
"input": [
{
"publicationId": "gid://shopify/Publication/456"
}
]
}
{
"data": {
"publishablePublish": {
"publishable": {
"id": "gid://shopify/ProductVariant/123",
"title": "Red / Small"
},
"userErrors": []
}
}
}

Anchor to Unpublish a variant from a sales channelUnpublish a variant from a sales channel

Use the publishableUnpublish mutation to remove a variant from a specific publication. The variant data isn't deleted. You can republish the variant at any time.

POST /api/2026-07/graphql.json

mutation UnpublishVariant($id: ID!, $input: [PublicationInput!]!) {
publishableUnpublish(id: $id, input: $input) {
publishable {
... on ProductVariant {
id
title
}
}
userErrors {
field
message
}
}
}
{
"id": "gid://shopify/ProductVariant/123",
"input": [
{
"publicationId": "gid://shopify/Publication/456"
}
]
}
{
"data": {
"publishableUnpublish": {
"publishable": {
"id": "gid://shopify/ProductVariant/123",
"title": "Red / Small"
},
"userErrors": []
}
}
}

Anchor to Create a variant in an unpublished stateCreate a variant in an unpublished state

You can create a variant that's unpublished from all publications by setting published to false. This prevents any buyer exposure before you explicitly publish the variant.

Use the published field on the variant input when calling productVariantsBulkCreate or productSet.

POST /api/2026-07/graphql.json

mutation CreateUnpublishedVariant(
$productId: ID!
$variants: [ProductVariantsBulkInput!]!
) {
productVariantsBulkCreate(
productId: $productId
variants: $variants
) {
productVariants {
id
title
}
userErrors {
field
message
}
}
}
{
"productId": "gid://shopify/Product/1",
"variants": [
{
"price": "29.99",
"optionValues": [
{
"optionName": "Color",
"name": "Midnight Blue"
}
],
"published": false
}
]
}
{
"data": {
"productVariantsBulkCreate": {
"productVariants": [
{
"id": "gid://shopify/ProductVariant/999",
"title": "Midnight Blue"
}
],
"userErrors": []
}
}
}

After creating an unpublished variant, you can selectively publish it to specific channels using publishablePublish.

Note

If you don't set published, or set it to true, the variant is created with the default state of published to all channels and catalogs where the parent product is published.


Anchor to Other publishable resourcesOther publishable resources

Collection also implements the Publishable interface. The same publishablePublish, publishableUnpublish, and publicationUpdate mutations shown in this guide work with Collection GIDs to control which sales channels and catalogs a collection is visible in.



Was this page helpful?