---
title: Product and variant publishing
description: >-
  Learn how to publish and unpublish products and variants across sales channels
  and catalogs using the GraphQL Admin API.
source_url:
  html: 'https://shopify.dev/docs/apps/build/sales-channels/product-publishing'
  md: 'https://shopify.dev/docs/apps/build/sales-channels/product-publishing.md'
api_name: admin
---

# 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`](https://shopify.dev/docs/api/admin-graphql/latest/interfaces/Publishable) interface is implemented by `Product`, `ProductVariant`, and `Collection`, giving you the same publishing controls across all three resource types.

***

## Requirements

* Your app can make authenticated requests to the [GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql).
* Your app has the `read_publications` and `write_publications` [access scopes](https://shopify.dev/docs/api/usage/access-scopes#authenticated-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.

***

## Discovering 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.

### Query 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

##### GraphQL query

```graphql
query Publications {
  publications(first: 20) {
    nodes {
      id
      autoPublish
      supportsFuturePublishing
      catalog {
        id
        title
      }
    }
  }
}
```

##### JSON response

```json
{
  "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.

### Query 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

##### GraphQL query

```graphql
query {
  catalogs(first: 3) {
    nodes {
      id
      title
      status
    }
  }
}
```

##### JSON response

```json
{
  "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"
        }
      ]
    }
  }
}
```

***

## Publishing products to sales channels and catalogs

Use the [`publishablePublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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

##### GraphQL mutation

```graphql
mutation PublishProduct($id: ID!, $input: [PublicationInput!]!) {
  publishablePublish(id: $id, input: $input) {
    publishable {
      ... on Product {
        id
        title
      }
      availablePublicationsCount {
        count
      }
      resourcePublicationsCount {
        count
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Product/1",
  "input": [
    {
      "publicationId": "gid://shopify/Publication/123"
    },
    {
      "publicationId": "gid://shopify/Publication/456"
    }
  ]
}
```

##### JSON response

```json
{
  "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.

### Unpublish a product

Use the [`publishableUnpublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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

##### GraphQL mutation

```graphql
mutation UnpublishProduct($id: ID!, $input: [PublicationInput!]!) {
  publishableUnpublish(id: $id, input: $input) {
    publishable {
      ... on Product {
        id
        title
      }
      availablePublicationsCount {
        count
      }
      resourcePublicationsCount {
        count
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Product/1",
  "input": [
    {
      "publicationId": "gid://shopify/Publication/456"
    }
  ]
}
```

##### JSON response

```json
{
  "data": {
    "publishableUnpublish": {
      "publishable": {
        "id": "gid://shopify/Product/1",
        "title": "Classic Snowboard",
        "availablePublicationsCount": {
          "count": 3
        },
        "resourcePublicationsCount": {
          "count": 2
        }
      },
      "userErrors": []
    }
  }
}
```

**Info:**

For sales channel apps, publish to your own channel with the same [`publishablePublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publishablePublish) and [`publishableUnpublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publishableUnpublish) mutations, passing your app's own publication ID. You can find that ID with the [`publications` query](#query-publications-directly). The `publishablePublishToCurrentChannel` and `publishableUnpublishToCurrentChannel` convenience mutations, which took no publication ID, are deprecated.

***

## Reading publication state

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

### Check a specific publication

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

## POST /api/2026-07/graphql.json

##### GraphQL query

```graphql
query ProductPublicationCheck($id: ID!, $publicationId: ID!) {
  product(id: $id) {
    title
    publishedOnPublication(publicationId: $publicationId)
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Product/1",
  "publicationId": "gid://shopify/Publication/123"
}
```

##### JSON response

```json
{
  "data": {
    "product": {
      "title": "Classic Snowboard",
      "publishedOnPublication": true
    }
  }
}
```

### List 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

##### GraphQL query

```graphql
query ProductPublications($id: ID!) {
  product(id: $id) {
    title
    resourcePublicationsV2(first: 20) {
      nodes {
        publication {
          id
        }
        isPublished
        publishDate
      }
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Product/1"
}
```

##### JSON response

```json
{
  "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"
          }
        ]
      }
    }
  }
}
```

### List 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

##### GraphQL query

```graphql
query ProductUnpublished($id: ID!) {
  product(id: $id) {
    title
    unpublishedPublications(first: 20) {
      nodes {
        id
      }
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Product/1"
}
```

##### JSON response

```json
{
  "data": {
    "product": {
      "title": "Classic Snowboard",
      "unpublishedPublications": {
        "nodes": [
          {
            "id": "gid://shopify/Publication/789"
          }
        ]
      }
    }
  }
}
```

***

## Managing 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`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publicationUpdate) mutation.

## POST /api/2026-07/graphql.json

##### GraphQL mutation

```graphql
mutation UpdatePublication($id: ID!, $input: PublicationUpdateInput!) {
  publicationUpdate(id: $id, input: $input) {
    publication {
      id
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/Publication/123",
  "input": {
    "publishablesToAdd": [
      "gid://shopify/Product/1",
      "gid://shopify/Product/2"
    ],
    "publishablesToRemove": [
      "gid://shopify/Product/3"
    ]
  }
}
```

##### JSON response

```json
{
  "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.

***

## Variant-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.

### How it works

The [`ProductVariant`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ProductVariant) object implements the [`Publishable`](https://shopify.dev/docs/api/admin-graphql/latest/interfaces/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.

### Visibility rules

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 state | Variant state | Visible to buyers |
| - | - | - |
| Published | Published | Yes |
| Published | Unpublished | No |
| Unpublished | Published | No |
| Unpublished | Unpublished | No |

### Independent state

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.

### Query 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

##### GraphQL query

```graphql
query VariantPublicationStatus {
  productVariant(id: "gid://shopify/ProductVariant/123") {
    title
    publishedOnPublication(
      publicationId: "gid://shopify/Publication/456"
    )
    resourcePublicationsCount {
      count
    }
  }
}
```

##### JSON response

```json
{
  "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

##### GraphQL query

```graphql
query VariantPublications {
  productVariant(id: "gid://shopify/ProductVariant/123") {
    title
    resourcePublicationsV2(first: 10) {
      nodes {
        publication {
          id
        }
        isPublished
        publishDate
      }
    }
    unpublishedPublications(first: 10) {
      nodes {
        id
      }
    }
  }
}
```

##### JSON response

```json
{
  "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"
          }
        ]
      }
    }
  }
}
```

### Publish a variant to a sales channel

Use the [`publishablePublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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

##### GraphQL mutation

```graphql
mutation PublishVariant($id: ID!, $input: [PublicationInput!]!) {
  publishablePublish(id: $id, input: $input) {
    publishable {
      ... on ProductVariant {
        id
        title
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/ProductVariant/123",
  "input": [
    {
      "publicationId": "gid://shopify/Publication/456"
    }
  ]
}
```

##### JSON response

```json
{
  "data": {
    "publishablePublish": {
      "publishable": {
        "id": "gid://shopify/ProductVariant/123",
        "title": "Red / Small"
      },
      "userErrors": []
    }
  }
}
```

### Unpublish a variant from a sales channel

Use the [`publishableUnpublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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

##### GraphQL mutation

```graphql
mutation UnpublishVariant($id: ID!, $input: [PublicationInput!]!) {
  publishableUnpublish(id: $id, input: $input) {
    publishable {
      ... on ProductVariant {
        id
        title
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "id": "gid://shopify/ProductVariant/123",
  "input": [
    {
      "publicationId": "gid://shopify/Publication/456"
    }
  ]
}
```

##### JSON response

```json
{
  "data": {
    "publishableUnpublish": {
      "publishable": {
        "id": "gid://shopify/ProductVariant/123",
        "title": "Red / Small"
      },
      "userErrors": []
    }
  }
}
```

### Create 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`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productVariantsBulkCreate) or [`productSet`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productSet).

## POST /api/2026-07/graphql.json

##### GraphQL mutation

```graphql
mutation CreateUnpublishedVariant(
  $productId: ID!
  $variants: [ProductVariantsBulkInput!]!
) {
  productVariantsBulkCreate(
    productId: $productId
    variants: $variants
  ) {
    productVariants {
      id
      title
    }
    userErrors {
      field
      message
    }
  }
}
```

##### Variables

```json
{
  "productId": "gid://shopify/Product/1",
  "variants": [
    {
      "price": "29.99",
      "optionValues": [
        {
          "optionName": "Color",
          "name": "Midnight Blue"
        }
      ],
      "published": false
    }
  ]
}
```

##### JSON response

```json
{
  "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`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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.

***

## Other publishable resources

`Collection` also implements the [`Publishable`](https://shopify.dev/docs/api/admin-graphql/latest/interfaces/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.

***

## Next steps

* Learn about [scheduled product publishing](https://shopify.dev/docs/apps/build/sales-channels/scheduled-product-publishing)
* Explore the [`Publishable` interface](https://shopify.dev/docs/api/admin-graphql/latest/interfaces/Publishable) reference
* Explore the [`publishablePublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publishablePublish) mutation reference
* Explore the [`publishableUnpublish`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publishableUnpublish) mutation reference
* Explore the [`publicationUpdate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/publicationUpdate) mutation reference
* Learn about [catalogs for different markets](https://shopify.dev/docs/apps/build/markets/catalogs-different-markets)
* Learn about [building sales channels](https://shopify.dev/docs/apps/build/sales-channels)

***
