Skip to main content

Contextual product feeds

Contextual product feeds allow sales channels to sync merchant catalogs to their platforms. You can configure distinct feeds for the different language and country pairs that a merchant supports.


In this tutorial, you'll learn how to do the following tasks:

  • Identify a merchant's supported markets
  • Create product feeds for specific country/language contexts
  • Subscribe to product feed webhooks
  • Initiate a full product sync


Anchor to Step 1: Identify the merchant's supported marketsStep 1: Identify the merchant's supported markets

Query the merchant's markets to obtain localization details, including each country's supported languages and currencies.

Note

Definitions for "primary region" might differ for each channel. Shopify exposes the following fields to help channels identify which regions and markets the merchant is based in:

The following example shows how to retrieve language, currency and country details from a shop's markets. Inline fragments are used to simply the GraphQL query.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

Markets query

{
markets(first: 10, enabled: true) {
edges {
node {
name
primary
enabled
currencySettings {
baseCurrency {
currencyCode
enabled
}
localCurrencies
}
webPresence {
subfolderSuffix
domain {
host
}
defaultLocale {
...LocaleFields
}
alternateLocales {
...LocaleFields
}
}
regions(first: 20) {
edges {
node {
id
name
... on MarketRegionCountry {
code
}
}
}
}
}
}
}
}

fragment LocaleFields on ShopLocale {
locale
name
primary
published
}

JSON response

{
"data": {
"markets": {
"edges": [
{
"node": {
"name": "Canada",
"primary": true,
"enabled": true,
"currencySettings": {
"baseCurrency": {
"currencyCode": "CAD",
"enabled": false
},
"localCurrencies": true
},
"webPresence": {
"subfolderSuffix": null,
"domain": {
"host": "my-store.myshopify.com"
},
"defaultLocale": {
"locale": "en",
"name": "English",
"primary": true,
"published": true
},
"alternateLocales": [
{
"locale": "fr",
"name": "French",
"primary": false,
"published": true
}
]
},
"regions": {
"edges": [
{
"node": {
"id": "gid://shopify/MarketRegionCountry/144848879672",
"name": "Canada",
"code": "CA"
}
}
]
}
}
},
{
"node": {
"name": "USA",
"primary": false,
"enabled": true,
"currencySettings": {
"baseCurrency": {
"currencyCode": "USD",
"enabled": false
},
"localCurrencies": false
},
"webPresence": {
"subfolderSuffix": "us",
"domain": null,
"defaultLocale": {
"locale": "en",
"name": "English",
"primary": true,
"published": true
},
"alternateLocales": []
},
"regions": {
"edges": [
{
"node": {
"id": "gid://shopify/MarketRegionCountry/145364320312",
"name": "United States",
"code": "US"
}
}
]
}
}
}
]
}
}
}

Anchor to Subscribe to market updatesSubscribe to market updates

Market configurations might change after onboarding, to which your app may need to respond. Stay updated on any market changes by using the webhookSubscriptionCreate mutation to subscribe to the MARKETS_CREATE, MARKETS_DELETE and MARKETS_UPDATE webhook topics. Events will be triggered whenever key changes occur, such as when:

  • A new market is created.
  • An existing market is updated.
  • An existing market is deleted.

Anchor to Step 2: Create product feedsStep 2: Create product feeds

Use the productFeedCreate mutation to create a ProductFeed for each country/language pair that both the channel and the merchant support.

Save the productFeed's id value. You'll use it it initiate and identify webhook payloads in a subsequent step when you synchronize products.

The following example shows how to create a product feed for the US-EN context.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

Create product feed

mutation {
productFeedCreate(input: {country: US, language: EN}) {
productFeed {
id
country
language
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"productFeedCreate": {
"productFeed": {
"id": "gid://shopify/ProductFeed/1343510",
"country": "US",
"language": "EN"
},
"userErrors": []
}
}
}

Anchor to (Optional): Use the primary context only(Optional): Use the primary context only

If your app only requires access to the merchant's default localization, then use the productFeedCreate mutation without its input argument. This creates a feed in the merchant's primary context, similar to how the ProductListings reference on the REST Admin API behaves.


Anchor to Step 3: Subscribe to product feed webhooksStep 3: Subscribe to product feed webhooks

Subscribe to product feed webhooks to receive initial full sync payloads and async events whenever products are updated.

Anchor to Subscribe to full sync eventsSubscribe to full sync events

Full sync events are triggered when a product feed full sync is initiated. Full sync events contain individual product payloads and allow you to sync a merchant's entire catalog during channel setup.

Use the webhookSubscriptionCreate mutation to subscribe to the PRODUCT_FEEDS_FULL_SYNC webhook topic.

Subscribe to full sync topic

mutation {
webhookSubscriptionCreate(
topic: PRODUCT_FEEDS_FULL_SYNC
webhookSubscription: {
uri: "{your-callback-uri}"
format: JSON
}
) {
webhookSubscription {
id
topic
uri
createdAt
}
userErrors {
field
message
}
}
}

Individual product listing payloads are sent when a full sync is initiated.

product_feeds/full_sync

{
"metadata": {
"action": "CREATE",
"type": "FULL",
"resource": "PRODUCT",
"fullSyncId": "gid://shopify/ProductFullSync/1123511235",
"truncatedFields": [

],
"occurred_at": "2024-01-01T00:00:00.000Z"
},
"productFeed": {
"id": "gid://shopify/ProductFeed/12345",
"shop_id": "gid://shopify/Shop/12345",
"country": "CA",
"language": "EN"
},
"product": {
"id": "gid://shopify/Product/12345",
"title": "Coffee",
"description": "The best coffee in the world",
"onlineStoreUrl": "https://example.com/products/coffee",
"createdAt": "2024-12-31T19:00:00-05:00",
"updatedAt": "2024-12-31T19:00:00-05:00",
"isPublished": true,
"publishedAt": "2024-12-31T19:00:00-05:00",
"productType": "Coffee",
"vendor": "Cawfee Inc",
"handle": "",
"images": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductImage/394",
"url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331",
"height": 3024,
"width": 4032
}
}
]
},
"options": [
{
"name": "Title",
"values": [
"151cm",
"155cm",
"158cm"
]
}
],
"seo": {
"title": "seo title",
"description": "seo description"
},
"tags": [
"tag1",
"tag2"
],
"variants": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductVariant/1",
"title": "151cm",
"price": {
"amount": "100.00",
"currencyCode": "CAD"
},
"compareAtPrice": null,
"sku": "12345",
"barcode": null,
"quantityAvailable": 10,
"availableForSale": true,
"weight": 2.3,
"weightUnit": "KILOGRAMS",
"requireShipping": true,
"inventoryPolicy": "DENY",
"createdAt": "2024-12-31T19:00:00-05:00",
"updatedAt": "2024-12-31T19:00:00-05:00",
"image": {
"id": "gid://shopify/ProductImage/394",
"url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331",
"height": 3024,
"width": 4032
},
"selectedOptions": [
{
"name": "Title",
"value": "151cm"
}
]
}
}
]
}
},
"products": null
}

Anchor to (Optional): Subscribe to full sync completion events(Optional): Subscribe to full sync completion events

You can subscribe to the PRODUCT_FEEDS_FULL_SYNC_FINISH webhook topic to be notified whenever a full sync finishes. This is a useful signal for scenarios where the merchant hasn't published any products to the channel.

{
"metadata": {
"action": "CREATE",
"type": "FULL",
"resource": "PRODUCT",
"fullSyncId": "gid://shopify/ProductFullSync/1123511235",
"truncatedFields": [

],
"occurred_at": "2024-01-01T00:00:00.000Z"
},
"productFeed": {
"id": "gid://shopify/ProductFeed/12345",
"shop_id": "gid://shopify/Shop/12345",
"country": "CA",
"language": "EN"
},
"fullSync": {
"createdAt": "2024-12-31 19:00:00 -0500",
"errorCode": null,
"status": "completed",
"count": 12,
"url": null
}
}

Anchor to Subscribe to incremental sync eventsSubscribe to incremental sync events

Incremental sync events are triggered whenever changes occur relative to app's feeds, such as when:

  • Product fields are updated.
  • Product variant fields are added, updated, or deleted.
  • Product translations are updated.
  • Product market price is updated.
  • Products are published to the app.
  • Products are unpublished from app.

Use the webhookSubscriptionCreate mutation to subscribe to the PRODUCT_FEEDS_INCREMENTAL_SYNC webhook topic.

Subscribe to incremental sync topic

mutation {
webhookSubscriptionCreate(
topic: PRODUCT_FEEDS_INCREMENTAL_SYNC
webhookSubscription: {
uri
format: JSON
}
) {
webhookSubscription {
id
topic
uri
createdAt
}
userErrors {
field
message
}
}
}

product_feeds/incremental_sync

{
"metadata": {
"action": "CREATE",
"type": "INCREMENTAL",
"resource": "PRODUCT",
"truncatedFields": [

],
"occured_at": "2024-12-31T19:00:00-05:00"
},
"productFeed": {
"id": "gid://shopify/ProductFeed/12345",
"shop_id": "gid://shopify/Shop/12345",
"country": "CA",
"language": "EN"
},
"product": {
"id": "gid://shopify/Product/12345",
"title": "Coffee",
"description": "The best coffee in the world",
"onlineStoreUrl": "https://example.com/products/coffee",
"createdAt": "2024-12-31T19:00:00-05:00",
"updatedAt": "2024-12-31T19:00:00-05:00",
"isPublished": true,
"publishedAt": "2024-12-31T19:00:00-05:00",
"productType": "Coffee",
"vendor": "Cawfee Inc",
"handle": "",
"images": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductImage/394",
"url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331",
"height": 3024,
"width": 4032
}
}
]
},
"options": [
{
"name": "Title",
"values": [
"151cm",
"155cm",
"158cm"
]
}
],
"seo": {
"title": "seo title",
"description": "seo description"
},
"tags": [
"tag1",
"tag2"
],
"variants": {
"edges": [
{
"node": {
"id": "gid://shopify/ProductVariant/1",
"title": "151cm",
"price": {
"amount": "100.00",
"currencyCode": "CAD"
},
"compareAtPrice": null,
"sku": "12345",
"barcode": null,
"quantityAvailable": 10,
"availableForSale": true,
"weight": 2.3,
"weightUnit": "KILOGRAMS",
"requireShipping": true,
"inventoryPolicy": "DENY",
"createdAt": "2024-12-31T19:00:00-05:00",
"updatedAt": "2024-12-31T19:00:00-05:00",
"image": {
"id": "gid://shopify/ProductImage/394",
"url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331",
"height": 3024,
"width": 4032
},
"selectedOptions": [
{
"name": "Title",
"value": "151cm"
}
]
}
}
]
}
},
"products": null
}

Anchor to Subscribe to product feed updatesSubscribe to product feed updates

Product feeds might change as the merchant modifies their market settings. For example, if a merchant un-publishes a language, then any dependant feeds will become inactive. Subscribe to the PRODUCT_FEEDS_UPDATE webhook topic to stay updated on feed statuses.

product_feeds/update

{
"id": "gid://shopify/ProductFeed/1285521464",
"country": "CA",
"language": "FR",
"status": "inactive"
}

Anchor to Step 4: Initiate a full syncStep 4: Initiate a full sync

After all relevant feeds are created and your app has subscribed to the necessary webhook topics, you can initiate full syncs for each feed using the productFullSync. The id returned can be used to attribute the webhooks and identify the sync completion event.

Your app should trigger a full sync once during initialization to fetch the store's entire catalog. You can trigger additional full syncs on a regular schedule for reconciliation.

When a full sync is initiated, individual PRODUCT_FEEDS_FULL_SYNC webhook payloads are fired for each of the merchant's published products. The webhook payloads contain localized data according the feed's context. A confirmation event is sent to the PRODUCT_FEEDS_FULL_SYNC_FINISH subscription once the full sync has completed. The payload's fullSyncId is the feed's identifier.

An initial full sync should be triggered during app onboarding, and regular reconciliation syncs should be scheduled after. The optional beforeUpdatedAt and updatedAtSince parameters allow you to specify a time range for the sync.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

start a product full sync

mutation {
productFullSync(id: "gid://shopify/ProductFeed/1164017720")
{
id
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"productFullSync": {
"id": "gid://shopify/ProductFullSync/2523324778944713391",
"userErrors": []
}
}
}

Anchor to (Optional): Full Sync by download(Optional): Full Sync by download

Developer preview

Full sync by download is in an invite-only developer preview. Access to this functionality is currently invite-only. To learn more, contact your Shopify solutions representative or Shopify Partner Support

An alternative solution is to download a single JSONL file containing all products that would normally be sent through individual PRODUCT_FEEDS_FULL_SYNC payloads. This can be useful in scenarios where the amount of webhook traffic is difficult to manage. When full sync by download is enabled, the PRODUCT_FEEDS_FULL_SYNC_FINISH webhook payload contains a JSONL file URL. This functionality is similar to bulk operations.

product_feeds/full_sync_finish

{
"metadata": {
"action": "CREATE",
"type": "FULL",
"resource": "FULL_SYNC",
"fullSyncId": "gid://shopify/ProductFullSync/23104717021717166290741",
"truncatedFields": []
},
"productFeed": {
"id": "gid://shopify/ProductFeed/2310471702",
"shop_id": "gid://shopify/Shop/59810447382",
"country": "US",
"language": "EN"
},
"fullSync": {
"createdAt": "2024-05-31T14:38:10Z",
"errorCode": "",
"status": "completed",
"count": 16,
"url": "https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/bulk-operation-outputs/dyfkl3g72empyyoenvmtidlm9o4g?<params />bulk-4258087174166.jsonl&response-content-type=application%2Fjsonl"
}
}


Was this page helpful?