Predictive search

As-you-type search suggestions: the /search/suggest endpoint, its response shape, and how to build the dropdown.

Themes get as-you-type search suggestions from one JSON endpoint. It reads the same channel-scoped index as the search page — every word in the query must match, the last one matches as a prefix, and the store's synonyms apply — so the dropdown and the results page never disagree.

The endpoint#

http
GET /search/suggest?q=<query>

Same-origin, no token, GET only. Queries under 3 characters return empty groups. When the merchant has search disabled the endpoint returns 404 — hide the search UI when the search page itself is off. Each visitor may send a short burst of requests and about two per second after that; past it the endpoint answers 429 with a Retry-After header in seconds, so debounce input and treat a 429 like an empty response. Search engine, merchant and ads crawlers are never limited.

Response#

json
{
  "query": "vanta",
  "groups": [
    {
      "type": "product",
      "has_more": true,
      "items": [
        {
          "title": "Vantage Aero 11 Tablet",
          "url": "/products/vantage-aero-11-tablet",
          "image": "https://…/….w160.webp",
          "price": "₹1,199.00",
          "compare_at_price": "₹1,499.00",
          "vendor": { "name": "Vantage", "url": "/vendors/vantage" },
          "rating": { "average": 4.6, "count": 38 }
        }
      ]
    }
  ],
  "search_url": "/search?q=vanta"
}

Groups arrive in the store's enabled result-type order (product, collection, category, page, policy, blog_post, blog_category, vendor, brand). The product group holds up to 10 items and every other group up to 5, the most relevant first; has_more is true when the group has more results than it shows — link to search_url for them. Groups carry no total: the search page counts results, the dropdown does not. Every url is permalink-correct for the storefront — render it as-is, never rebuild routes client-side. image is present only when the resource has one: a small cached CDN variant sized for a dropdown row. Product items also carry price, formatted for the visitor's currency and language exactly as product cards show it; compare_at_price when the product is on sale; price_from: true when variants have different prices (show it as "From …"); vendor, the product's vendor name and its page url, when it has one; and rating, the average (one decimal) and count of approved reviews, when it has reviews and reviews are on. search_url is the full results page for the query — use it for a "see all results" row.

Building the dropdown#

Debounce input (200–300ms) and drop out-of-order responses — keep a request sequence number and ignore any response older than the latest. Render titles as text nodes, not injected HTML. Close on Escape and on clicks outside the panel. On small screens let the field take the full header row while open.

Canvas, the default theme, ships this end to end in its header: grouped results with thumbnails and prices, keyboard navigation and a search-for row.