The Citation Report / The Optimization Playbook

Mapping Shopify local pickup schema to win AI shopping recommendations

Pendium visibility research indicates that conversational AI systems bypass brick-and-mortar retail locations when store inventory is missing from page-level structured data. When local consumers ask ChatGPT, Claude, or Google AI Overviews where to buy a specific product today, default Shopify themes fail to surface physical stores because local stock availability remains trapped inside administrative databases. To capture same-day retail recommendations, merchants must connect backend POS data to product JSON-LD by declaring LocalBusiness nodes and nesting availableAtOrFrom references inside their Offer declarations. This guide details how to extract store pickup status through Shopify's Storefront API and encode it into clean Schema.org markup so conversational engines verify your retail shelves.

The structured data gap in default Shopify themes

Standard Shopify themes handle online transactions and in-person retail through separate technical pipelines. You enter your physical retail addresses, store hours, and point-of-sale hardware configurations inside the Shopify Admin under Settings, then Locations. This information manages inventory pooling, assigns local pickup permissions, and routes orders for packing slips. None of that location data automatically transfers into the public JSON-LD scripts rendered by default storefront templates like Dawn.

Instead, default theme liquid templates group product inventory under a single, nondescript online catalog entity. When an AI crawler inspects a product page, it finds an Offer object with basic properties: price, priceCurrency, and a generic availability status set to https://schema.org/InStock. To a conversational search engine processing local purchase queries, a multi-location brand with storefronts in Manhattan and Brooklyn looks identical to a drop-shipping business operating out of an unstaffed warehouse.

Conversational shopping engines evaluate websites differently than legacy web scrapers. While traditional search engines parse broad keyword density and directory listings, language models rely on direct semantic assertions. If an AI agent running a recommendation query cannot confirm that a specific Stock Keeping Unit (SKU) sits on a shelf at a verifiable street address, it skips that merchant to prevent hallucinating inventory. Our analysis of the Pendium AI visibility platform shows that AI crawlers do not initiate browser checkout sessions, populate shipping forms, or trigger client-side geolocation modals. If your store pickup data only surfaces after a user selects a variant and opens the cart drawer, automated shopping agents remain completely blind to it.

The issue multiplies for stores running complex catalog structures. As discussed in our technical guide on fixing Shopify combined listings so AI recommends your product variants, splitting or combining options across separate URLs often severs variant-level data from location signals. Technical documentation from CatalogScan on Shopify POS and online store structured data confirms that without custom schema markup, point-of-sale data stays locked in the admin interface rather than serving as crawlable data for conversational assistants.

Two women making a payment at a convenience store using a touch screen POS system.

Extracting in-store availability via the Storefront API

To expose store-level inventory to web scrapers, you must query your store's physical fulfillment state and expose it directly within server-rendered HTML. Shopify exposes local pickup status on the storefront through the store_availability Liquid object, as well as via the Storefront GraphQL API.

The API uses three primary resources to resolve localized stock:

  • The variant entity representing the distinct SKU selected by the buyer
  • The location entity containing the administrative name and physical address of the retail shop
  • The storeAvailability connection containing true or false availability markers alongside estimated pickup windows

Merchants building custom headless frontends or writing custom Liquid sections can query these fields directly. The following GraphQL query extracts availability for an individual product variant across your retail footprint:

query GetStoreAvailability($handle: String!, $selectedOptions: [SelectedOptionInput!]!) {
  product(handle: $handle) {
    variantBySelectedOptions(selectedOptions: $selectedOptions) {
      id
      storeAvailability(first: 10) {
        edges {
          node {
            available
            pickUpTime
            location {
              name
              address {
                address1
                city
                provinceCode
                zip
                country
              }
            }
          }
        }
      }
    }
  }
}

As outlined in Shopify's developer documentation on supporting local pickup on storefronts, the available boolean accounts for store transfers. If an item is not physically present at your Soho location but an active internal logistics rule can route it from your Brooklyn store within an acceptable fulfillment window, the platform marks the item as available while updating pickUpTime to reflect the extended wait.

In standard Liquid templates, this data is accessed through product.selected_or_first_available_variant.store_availabilities. Iterating over this collection allows you to assemble location-specific parameters directly inside your server-rendered template:

{% assign current_variant = product.selected_or_first_available_variant %}
{% for availability in current_variant.store_availabilities %}
  {% if availability.available %}
    {% comment %} Store pickup data is confirmed for this location {% endcomment %}
    <!-- Location: {{ availability.location.name }} - Pickup: {{ availability.pick_up_time }} -->
  {% endif %}
{% endfor %}

Extracting this data on the server side avoids the latency issues associated with client-side JavaScript execution. When an automated crawler retrieves the document, the raw payload already contains verified fulfillment parameters.

Mapping pickup data to the Offer and LocalBusiness entities

Exposing the data within the Document Object Model is only half the battle. Conversational engines require that local inventory tie directly to Schema.org standards. You must construct a graph that binds individual product offers to specific physical locations using the availableAtOrFrom property.

Declaring your physical locations

Before connecting inventory to your products, you must declare each physical retail store as an independent LocalBusiness or Store entity within your site architecture. This is accomplished by rendering location entities inside layout/theme.liquid or on a dedicated retail locations page.

Each store declaration requires a deterministic @id URI, precise geographic coordinates, and defined operating hours. AI engines use these coordinates to calculate physical distance when answering buyer questions like "where can I pick up this item near me."

{
  "@context": "https://schema.org",
  "@type": "Store",
  "@id": "https://example.com/#store-soho",
  "name": "Acme Goods - Soho Flagship",
  "url": "https://example.com/pages/locations/soho",
  "telephone": "+1-212-555-0144",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "420 Broadway",
    "addressLocality": "New York",
    "addressRegion": "NY",
    "postalCode": "10013",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 40.7193,
    "longitude": -74.0004
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
      "opens": "10:00",
      "closes": "20:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Sunday",
      "opens": "11:00",
      "closes": "18:00"
    }
  ]
}

The @id token acts as an anchor across your entire site. Once defined, any product offer can reference https://example.com/#store-soho without needing to duplicate raw address properties on every page load.

Elegant storefront display with mannequins in fashionable attire, seen in Wuhan, China.

Attaching local availability to the product offer

To support Buy Online, Pick Up In Store (BOPIS) requests, your product schema should define multiple Offer nodes within an offers array. One offer defines national parcel delivery, while separate offers define local store pickup at specific physical retail branches.

Following specifications from CatalogScan's guide to Shopify local pickup schema, in-store fulfillment requires configuring OfferShippingDetails with a shippingLabel set to "local pickup" and a deliveryMethod pointing to https://schema.org/OnSitePickup.

The table below contrasts standard theme output with a fully realized local pickup schema configuration:

Schema Property Default Shopify Theme Local Pickup Configuration
Offer.@type Single global Offer Array of fulfillment-specific Offer nodes
availableAtOrFrom Omitted entirely Linked @id reference to Store entity
deliveryMethod Unspecified or standard courier Set to https://schema.org/OnSitePickup
shippingRate Calculated at checkout Set to 0.00 for zero-cost store collection
deliveryTime Omitted or defaults to shipping days Scaled to pickup readiness hours

Here is how you structure the JSON-LD snippet inside your product template to link inventory to the declared location:

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Waxed Canvas Field Jacket",
  "sku": "WC-JCK-001-OLV",
  "offers": [
    {
      "@type": "Offer",
      "price": "285.00",
      "priceCurrency": "USD",
      "availability": "https://schema.org/InStock",
      "url": "https://example.com/products/waxed-canvas-field-jacket?variant=4012",
      "shippingDetails": {
        "@type": "OfferShippingDetails",
        "shippingLabel": "Standard Ground Shipping",
        "shippingRate": {
          "@type": "MonetaryAmount",
          "value": "12.00",
          "currency": "USD"
        }
      }
    },
    {
      "@type": "Offer",
      "price": "285.00",
      "priceCurrency": "USD",
      "availability": "https://schema.org/InStock",
      "availableAtOrFrom": {
        "@id": "https://example.com/#store-soho"
      },
      "shippingDetails": {
        "@type": "OfferShippingDetails",
        "shippingLabel": "Local Pickup",
        "shippingRate": {
          "@type": "MonetaryAmount",
          "value": "0.00",
          "currency": "USD"
        },
        "deliveryMethod": "https://schema.org/OnSitePickup"
      }
    }
  ]
}

When conversational platforms scan this schema, they extract two critical facts: standard home delivery costs $12.00, but a shopper in Manhattan can collect the item at 420 Broadway with no fulfillment surcharge.

Encoding pickup readiness to capture "today" queries

The most common implementation mistake involves leaving pickup readiness parameters set to standard shipping intervals. When a user asks an AI model "where can I pick up a field jacket in New York today," the agent inspects the deliveryTime entity. If your store defaults to standard carrier timelines (such as 2 to 5 business days), the engine discards your retail shop from immediate local consideration.

Conversational agents assess pickup immediacy using the ShippingDeliveryTime specification, paying close attention to handlingTime. For local pickup, handling time must be formatted using ISO 8601 duration intervals that denote hours rather than days:

  • PT1H indicates fulfillment preparation within one hour
  • PT2H indicates readiness within two hours
  • PT24H indicates readiness by the following business day

A retail location that offers same-day collection within two hours should encode the handling window directly within the pickup shippingDetails object:

"shippingDetails": {
  "@type": "OfferShippingDetails",
  "shippingLabel": "Same-Day In-Store Pickup",
  "shippingRate": {
    "@type": "MonetaryAmount",
    "value": "0.00",
    "currency": "USD"
  },
  "deliveryMethod": "https://schema.org/OnSitePickup",
  "deliveryTime": {
    "@type": "ShippingDeliveryTime",
    "handlingTime": {
      "@type": "QuantitativeValue",
      "minValue": 0,
      "maxValue": 2,
      "unitCode": "HUR"
    }
  }
}

You can programmatically set these duration values using Shopify Liquid. By interrogating the pick_up_time string provided by the store_availability object, you can dynamically output the corresponding unit code:

{% assign pickup_hours = 24 %}
{% if availability.pick_up_time contains 'usually ready in 1 hour' %}
  {% assign pickup_hours = 1 %}
{% elsif availability.pick_up_time contains 'usually ready in 2 hours' %}
  {% assign pickup_hours = 2 %}
{% elsif availability.pick_up_time contains 'usually ready in 4 hours' %}
  {% assign pickup_hours = 4 %}
{% endif %}

"deliveryTime": {
  "@type": "ShippingDeliveryTime",
  "handlingTime": {
    "@type": "QuantitativeValue",
    "minValue": 0,
    "maxValue": {{ pickup_hours }},
    "unitCode": "HUR"
  }
}

This Liquid logic converts generic theme text strings into structured operational thresholds that automated crawlers parse without error. For deeper analysis on how conversational engines bypass unverified checkout code, review the Pendium guide on fixing Shopify local pickup schema for AI shopping recommendations.

Crop anonymous female employee with application on cellphone screen interacting with partner using tablet at counter in cafeteria

Auditing and monitoring your local AI visibility

After injecting location entities and linking your variant offers, validate the output using the Schema.org validator and Google's Rich Results Test. Verify that your JSON-LD does not emit disconnected graph fragments. The @id string assigned to your Store node must match the URI referenced inside each variant's availableAtOrFrom attribute.

Validating code syntax confirms that search spiders can crawl your data, but it does not tell you whether conversational assistants actually recommend your catalog. Conversational engines pull from diverse training sets, live scraping runs, and retrieval-augmented pipelines across multiple LLM ecosystems.

You can track how ChatGPT, Claude, Gemini, Grok, Perplexity, DeepSeek, and Google AI Overviews interpret your updated store pages using the Pendium Agent Analytics platform. The platform monitors over 50 real customer query types across 7 AI engines, measuring whether your physical stores appear when local shoppers search for in-stock products nearby.

To identify remaining structured data gaps across your catalog, run your product and collection pages through the free Pendium AI Visibility Scan. The scan evaluates your public storefront markup against live AI retrieval systems, delivering a clear assessment of your local recommendation footprint within two minutes.