With variant-level publishing in Admin API 2026-07, how should an app efficiently determine whether a specific variant is published on the Online Store?
ProductVariant.publishedOnPublication requires the shop-specific Online Store publication ID. Apps must therefore discover that ID and repeatedly query it or cache it per shop.
product.publishedAt is insufficient because the product can be published while the variant is not. availableForSale also isn’t equivalent to publication state.
Could Shopify add ProductVariant.publishedOnOnlineStore or variant-level publishedInContext? Requiring every app to manage opaque publication IDs seems excessive for such a basic visibility check.
Hey @Angelina_Prokopeva - I had a look into this for you!
In 2026-07 the ProductVariant picks up the Publishable interface, and there’s no dedicated publishedOnOnlineStore or variant-level publishedInContext field, so you’re correct that publishedOnPublication(publicationId:) is the supported per-variant check. You’re also right that product.publishedAt and availableForSale answer different questions, the latter being “can a buyer purchase this on the storefront” rather than “is it published to this channel”.
Playing around on my test store, the publication ID part was less painful than it looks because that ID is stable per shop. The one gotcha is matching, since publication.name is deprecated and catalog.title comes back as an auto-generated string like “Channel Catalog … for Online Store”. Match on the channel handle online_store instead, which is stable and not localized:
query {
publications(first: 50, catalogType: APP) {
edges { node { id channels(first: 1) { nodes { handle name } } } }
}
}
If you’d rather skip the cached ID entirely, resourcePublicationsV2 on the variant gives you the full set of channels in one call, and you can pick out the Online Store entry by that same handle. The variant-level publishing guide covers the 2026-07 behavior and recommends reading variant publication state through that field.
A first-class variant-level “published on Online Store” field is absolutely reasonable feedback, so I’ve passed it along to the relevant team internally as a feature request - thanks for raising this here!
Thank you very much for detailed explanations.
Honestly, it’s pretty complicated in comparison to the interface the product object provides. Especially if we need to do pagination to extract all Publications.
If we can ask a couple more questions, that would be very helpful to understand the best implementation. If using the same approach to identify if a variant is published on B2B catalog publication based on buyer context:
-
how many B2B catalogs can be assigned to one company location? Can we rely that there are 25 B2B catalogs that can be assigned to one company location, as it’s described here?
-
how Market catalogs can be resolved for one shipping address?
-
is the publication ID for the online store channel persistent for a store? Or it could change?
Thank you in advance.
Hi @Donal-Shopify sorry to ping you again here. I just wanted to follow up on my last reply in this thread whenever you have a chance
Hey @Angelina_Prokopeva, I don’t get to check the forums as often as I’d like so I do appreciate your patience here!
On B2B catalogs per company location, yes, you can rely on 25 as the documented maximum assignable to a single company location, and that counts both direct assignments and catalogs inherited through B2B markets. Rather than assuming the cap, though, read the actual assignments straight off the location. CompanyLocation exposes catalogs and catalogsCount, so you can list the catalogs in scope, grab each catalog’s publication, and run publishedOnPublication(publicationId:) per variant against those.
In relation to resolving Market catalogs for a shipping address, there isn’t a single Admin API call that takes an address and you the catalog. The chain is country/region to Market to that market’s catalog (catalogType: MARKET) to its publication. A market catalog only exposes a publication when it customizes product availability, so a pricing-only catalog hands you a price list and a null publication. If your goal is “what does this specific buyer actually see,” the path is to let the storefront resolve it for you with the Storefront API @inContext(country:) directive, which returns only what’s published and available in the matched market.
For the Online Store publication ID, it’s persistent per shop and won’t rotate under normal conditions, so discovering it once and caching it per shop is fine. Just keep matching on the channel handle online_store rather than the catalog title, since that handle is the stable, non-localized identifier.
Hope this helps!
Thanks @Donal-Shopify your insights are very helpful!
I really hope Shopify will also release a publishedInContext endpoint for variant-level resolution to close the gap.
Regarding publication status on Markets catalogs, I found that marketsResolvedValues could potentially be used to retrieve all market-level Publication IDs based on the country code. From there, we can use the same publishedOnPublication(publicationId:) endpoint to determine the publication statuses.
One question I still have: how many market catalogs can be assigned to a single country? Since the assignment is country-based, we could ultimately resolve the relevant catalog for a company location through its country code. However, if multiple market catalogs can be assigned to the same country, we would need to understand how Shopify determines which catalog applies.
P.S. We cannot use the Storefront API because it requires adding a Customer Account authorization redirect just to obtain a customerAccessToken. That would be too disruptive for our current flow. Our app was designed around the Admin API before the latest release that introduced variant-level publications.
@Donal-Shopify I also was experimenting with CompanyLocation.catalogs endpoint is it does NOT return catalogs inherited through B2B markets. Here was the set up:
The endpoint returned empty catalogs, even though there is “B2B market catalog” in the B2B market assigned.
Could you please take a look if it is expected behavior or my test setup was incorrect.
Thanks in advance!
Hey @Angelina_Prokopeva, thanks for waiting to hear back from me! You’re right that CompanyLocation.catalogs doesn’t return catalogs inherited through B2B markets. That connection only includes catalogs directly assigned to the company location (CompanyLocationCatalog). Catalogs created for a B2B market are a separate type (MarketCatalog) linked to the Market, not to individual company locations.
To find the market catalogs for a company location, query the shop’s markets and check which ones include the location in their conditions. B2B markets use a companyLocationsCondition to target specific company locations, and each matching market exposes a catalogs connection.
query B2BMarketCatalogs {
markets(first: 20) {
nodes {
id
name
catalogs(first: 10) {
nodes {
id
title
}
}
}
}
}
Both CompanyLocationCatalog and MarketCatalog implement the Catalog interface, so they share fields like id, title, and status but surface through different connections. The current CompanyLocation reference doesn’t call out this distinction, which I understand makes it hard to discover.
For the full effective catalog set, your app would combine companyLocation.catalogs with the catalogs from markets that include the location. Let me know if that covers what you’re trying to do!
@Donal-Shopify okay, it makes sense than. Would be more convenient if Shopify would distinguish types of catalogs:
MARKET
COMPANY_LOCATION
MARKET
NONE
And a new one, something like B2B MARKET
Right now, it’s impossible to distinguish B2B market catalogs and regional market catalogs. But I understand that Shopify is designed that way so merchants can easily change on the Admin interface what object does the market assign to: the countries (in this case it’s regional market), or to the Company Locations (in this case it’s B2B MARKET).
But it’s indistinguishable on the Admin API, and B2B MARKET catalogs and regional market catalogs have a different place in the hierarchy that defines what variant is visible and what variable is not visible.
Every new feature is harder to support using the Admin API last year.
But it will be much easier if on the Admin API we would have publishedInContext endpoint on the Variant level. Which was my original request.
Hopefully the Dev team could look into it and maybe potentially release it in the next version of API.
Thank you very much really testing these edge cases, and please keep me updated!
P.S.: Another request also related to the recent updates I already mentioned in my other post would be if the Dev team could potentially release for developers something like deliverableInContext endpoint for every variant on Admin API. To identify how many inventory is eligible for a buyer in a specific country.
It’s a relatively new feature that went live, where merchants can set up delivery profiles for every location they stock inventory. In the earlier model the total amount was available for everybody an online store, now it’s per delivery profile.
@Angelina_Prokopeva I appreciate you laying out the broader picture so clearly!
You’re right that there’s no way to distinguish a B2B market catalog from a regional market catalog on the Admin API today.
I’ve captured both points as feedback for the relevant team to review. That covers the publishedInContext request from your original post and the catalog type distinction so B2B and regional market catalogs can be told apart programmatically. I can’t speak to timelines or whether either will be built, but the feedback is now visible internally.
Thanks as always for raising these points here, it’s invaluable feedback for our product teams - cheers!
@Donal-Shopify I really appreciate you taking the time to dig into this use case, and verify the current limitations. Thanks again for the thoughtful follow-up and sharing with the relevant team!