--- title: Contextual product feeds description: Learn how to use product feeds to synchronize contextualized product listings to your platform. source_url: html: https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds md: https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds.md --- ExpandOn this page * [What you'll learn](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#what-youll-learn) * [Requirements](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#requirements) * [Step 1: Identify the merchant's supported markets](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-1-identify-the-merchants-supported-markets) * [Step 2: Create product feeds](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-2-create-product-feeds) * [Step 3: Subscribe to product feed webhooks](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-3-subscribe-to-product-feed-webhooks) * [Step 4: Initiate a full sync](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-4-initiate-a-full-sync) * [Next steps](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#next-steps) # Contextual product feeds Contextual product feeds allow [sales channels](https://shopify.dev/docs/apps/build/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. *** ## What you'll learn 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 *** ## Requirements * Your app has the `read_product_listings` [access scope](https://shopify.dev/docs/api/usage/access-scopes). Learn more about requesting access scopes when your app is installed using [authorization code grant](https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/authorization-code-grant). *** ## Step 1: Identify the merchant's supported markets Query the merchant's [markets](https://shopify.dev/docs/api/admin-graphql/latest/queries/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 merchant's [primary market](https://shopify.dev/docs/api/admin-graphql/latest/objects/Market#field-market-primary). * The `country` of the merchant's [primary fulfillment location](https://shopify.dev/docs/api/admin-graphql/latest/queries/location)'s [`address`](https://shopify.dev/docs/api/admin-graphql/2024-07/objects/Location#field-address). * The merchant's [default currency](https://shopify.dev/docs/api/admin-graphql/latest/objects/Shop#field-shop-currencycode). The following example shows how to retrieve language, currency and country details from a shop's markets. [Inline fragments](https://shopify.dev/docs/apps/build/graphql/basics/advanced#inline-fragments) are used to simply the GraphQL query. ## POST https\://{shop}.myshopify.com/api/{api\_version}/graphql.json ## Markets query ```graphql { 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 ```json { "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" } } ] } } } ] } } } ``` ### Subscribe to market updates Market configurations might change after [onboarding](https://shopify.dev/docs/apps/build/sales-channels#sales-channel-app-flow), to which your app may need to respond. Stay updated on any market changes by using the [`webhookSubscriptionCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) to subscribe to the [`MARKETS_CREATE`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-marketscreate), [`MARKETS_DELETE`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-marketsdelete) and [`MARKETS_UPDATE`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-marketsupdate) 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. *** ## Step 2: Create product feeds Use the [`productFeedCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFeedCreate) 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](#step-4-initiate-a-full-sync) 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 ```graphql mutation { productFeedCreate(input: {country: US, language: EN}) { productFeed { id country language } userErrors { field message } } } ``` ## JSON response ```json { "data": { "productFeedCreate": { "productFeed": { "id": "gid://shopify/ProductFeed/1343510", "country": "US", "language": "EN" }, "userErrors": [] } } } ``` ### (Optional): Use the primary context only If your app only requires access to the merchant's default localization, then use the [`productFeedCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFeedCreate#argument-input) without its `input` argument. This creates a feed in the merchant's primary context, similar to how the [`ProductListings`](https://shopify.dev/docs/api/admin-rest/latest/resources/productlisting) reference on the REST Admin API behaves. *** ## Step 3: Subscribe to product feed webhooks Subscribe to product feed webhooks to receive initial [full sync payloads](#step-4-initiate-a-full-sync) and [async events](#subscribe-to-incremental-sync-events) whenever products are updated. ### Subscribe 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](https://shopify.dev/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) to subscribe to the [`PRODUCT_FEEDS_FULL_SYNC`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsfullsync) webhook topic. ## Subscribe to full sync topic ```graphql 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](#step-4-initiate-a-full-sync). ## product\_feeds/full\_sync ```json { "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 } ``` ### (Optional): Subscribe to full sync completion events You can subscribe to the [`PRODUCT_FEEDS_FULL_SYNC_FINISH`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsfullsyncfinish) 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. ```json { "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 } } ``` ### Subscribe 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](https://shopify.dev/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) to subscribe to the [`PRODUCT_FEEDS_INCREMENTAL_SYNC`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsincrementalsync) webhook topic. ## Subscribe to incremental sync topic ```graphql mutation { webhookSubscriptionCreate( topic: PRODUCT_FEEDS_INCREMENTAL_SYNC webhookSubscription: { uri format: JSON } ) { webhookSubscription { id topic uri createdAt } userErrors { field message } } } ``` ## product\_feeds/incremental\_sync ```json { "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 } ``` ### Subscribe 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`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsupdate) webhook topic to stay updated on [feed `statuses`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ProductFeed#field-status). ## product\_feeds/update ```json { "id": "gid://shopify/ProductFeed/1285521464", "country": "CA", "language": "FR", "status": "inactive" } ``` *** ## Step 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`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFullSync). The [`id`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFullSync#field-productfullsyncpayload-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](https://shopify.dev/docs/apps/build/webhooks/best-practices#implement-reconciliation-jobs). When a full sync is initiated, individual [`PRODUCT_FEEDS_FULL_SYNC` webhook payloads](#subscribe-to-full-sync-events) 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](#optional-subscribe-to-full-sync-completion-events) 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 ```graphql mutation { productFullSync(id: "gid://shopify/ProductFeed/1164017720") { id userErrors { field message } } } ``` ## JSON response ```json { "data": { "productFullSync": { "id": "gid://shopify/ProductFullSync/2523324778944713391", "userErrors": [] } } } ``` ### (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](https://help.shopify.com/en/support/partners) An alternative solution is to download a single [`JSONL` file](https://jsonlines.org/) containing all products that would normally be sent through individual [`PRODUCT_FEEDS_FULL_SYNC`](#subscribe-to-full-sync-events) 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](https://shopify.dev/docs/api/usage/bulk-operations/queries). ## product\_feeds/full\_sync\_finish ```json { "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?bulk-4258087174166.jsonl&response-content-type=application%2Fjsonl" } } ``` *** ## Next steps * For other channel product sync solutions, refer to the [unidirectional product synchronization](https://shopify.dev/docs/apps/build/sales-channels/product-sync) guide. *** * [What you'll learn](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#what-youll-learn) * [Requirements](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#requirements) * [Step 1: Identify the merchant's supported markets](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-1-identify-the-merchants-supported-markets) * [Step 2: Create product feeds](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-2-create-product-feeds) * [Step 3: Subscribe to product feed webhooks](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-3-subscribe-to-product-feed-webhooks) * [Step 4: Initiate a full sync](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#step-4-initiate-a-full-sync) * [Next steps](https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds#next-steps)