Documentation
Feedback
Guides

Migrating to Intelligent Search API v1

Step-by-step guide for migrating headless integrations from Intelligent Search API (Legacy) to Intelligent Search API v1.

This guide covers everything you need to migrate a headless integration from Intelligent Search API (Legacy) to Intelligent Search API v1.

All new headless integrations must use Intelligent Search API v1. The legacy API endpoints will be deprecated in a future announcement.

Migration benefits

Migrating to Intelligent Search API v1 offers the following improvements over Intelligent Search API (Legacy):

  • Lower latency: Average 60% reduction in response time for search endpoints.
  • HTTP caching: Most responses now include a Cache-Control header, enabling CDN and browser caching to reduce origin load and speed up storefronts. Always read this header at runtime. Don't hardcode cache durations in your integration.
  • New endpoint for single-product lookup: The new GET Get product endpoint skips the search pipeline for product detail pages, resulting in lower latency and better cache-hit rates than a full product search.
  • Explicit context: All inputs are passed as query parameters or path facets. Responses are deterministic and cache-friendly because they no longer vary by segment cookie.
  • Complete product item data: fields that previously returned incorrect or empty values now return correctly, including attachments[], estimatedDateArrival, kitItems, isKit, modalType, and images[].imageText.

Before you begin

  • You must have VTEX Intelligent Search installed in your account.
  • Identify all places in your codebase that call Intelligent Search API (Legacy) at /api/io/_v/api/intelligent-search/*.

Step 1 - Update the base URL

Change the base URL from the legacy IO path to the v1 path.

Before:


_10
https://{accountName}.vtexcommercestable.com.br/api/io/_v/api/intelligent-search

After:


_10
https://{accountName}.vtexcommercestable.com.br/api/intelligent-search/v1

Step 2 - Update endpoint paths

Rename paths to match the new URL structure. Underscores become hyphens, the IO prefix is removed, and /v1 is added.

Intelligent Search API (Legacy)Intelligent Search API v1
GET /api/io/_v/api/intelligent-search/top_searches
Get list of the 10 most searched terms
GET /api/intelligent-search/v1/top-searches
Get list of the 10 most searched terms (v1)
GET /api/io/_v/api/intelligent-search/autocomplete_suggestions
Get list of suggested terms and attributes similar to the search term
GET /api/intelligent-search/v1/autocomplete-suggestions
Get list of suggested terms and attributes similar to the search term (v1)
GET /api/io/_v/api/intelligent-search/search_suggestions
Get list of suggested terms similar to the search term
GET /api/intelligent-search/v1/search-suggestions
Get list of suggested terms similar to the search term (v1)
GET /api/io/_v/api/intelligent-search/correction_search
Get attempt of correction of a misspelled term
GET /api/intelligent-search/v1/correction-search
Get attempt of correction of a misspelled term (v1)
GET /api/io/_v/api/intelligent-search/banners/{facets}
Get list of banners registered for query
GET /api/intelligent-search/v1/banners/{facets}
Get list of banners registered for query (v1)
GET /api/io/_v/api/intelligent-search/product_search/{facets}
Get list of products for a query
GET /api/intelligent-search/v1/product-search/{facets}
Search products (v1)
GET /api/io/_v/api/intelligent-search/facets/{facets}
Get list of the possible facets for a given query
GET /api/intelligent-search/v1/facets/{facets}
List filters for a search (v1)
GET /api/io/_v/api/intelligent-search/pickup-point-availability/{facets}
Get pickup point availability for Delivery Promise
GET /api/intelligent-search/v1/pickup-point-availability/{facets}
Get pickup point availability for Delivery Promise (v1)
N/AGET /api/intelligent-search/v1/products
Get product (v1)

Intelligent Search API (Legacy) reads locale, sales channel, regionalization, marketing context, and Delivery Promise parameters from the VTEX segment cookie. Intelligent Search API v1 does not read the segment cookie. You must pass this context explicitly.

Read the current segment values from the VTEX segment cookie (or your session/context store) and map them to the corresponding query parameters:

Segment fieldIntelligent Search API v1 parameterAffected endpoints
cultureInfolocaleAll endpoints
channelscGET /product-search/{facets} Search products (v1)
GET /facets/{facets} List filters for a search (v1)
GET /pickup-point-availability/{facets} Get pickup point availability for Delivery Promise (v1)
GET /products Get product (v1)
regionIdregionIdGET /product-search/{facets} Search products (v1)
GET /facets/{facets} List filters for a search (v1)
GET /products Get product (v1)
countryCodecountryGET /product-search/{facets} Search products (v1)
GET /facets/{facets} List filters for a search (v1)
GET /pickup-point-availability/{facets} Get pickup point availability for Delivery Promise (v1)
GET /products Get product (v1)
utm_source
utm_campaign
utmi_campaign
campaigns
priceTables
utmSource
utmCampaign
utmiCampaign
campaigns (omit if null)
priceTables
GET /product-search/{facets} Search products (v1)
GET /products Get product (v1)
zip-code, coordinates, country, pickupPoint, deliveryZonesHash, pickupPointsHash keys in segment.facetsSame-named query parameters. If countryCode is present as a top-level segment field, it takes precedence over the country key in the facets string.GET /product-search/{facets} Search products (v1)
GET /facets/{facets} List filters for a search (v1)
GET /pickup-point-availability/{facets} Get pickup point availability for Delivery Promise (v1)
GET /products Get product (v1)
All other keys in segment.facetsURL path facets, appended as key/value pairsGET /product-search/{facets} Search products (v1)
GET /facets/{facets} List filters for a search (v1)

Before:


_10
curl 'https://{accountName}.vtexcommercestable.com.br/api/io/_v/api/intelligent-search/product_search/trade-policy/1' \
_10
--header 'Cookie: vtex_segment=...'
_10
_10
# segment = { channel: 1, cultureInfo: "en-US", facets: "zip-code=22250-040;country=BRA;brand=acme" }

After:


_10
curl 'https://{accountName}.vtexcommercestable.com.br/api/intelligent-search/v1/product-search/brand/acme?locale=en-US&sc=1&country=BRA&zip-code=22250-040'

Parsing the segment facets field

The last two rows of the table above reference keys inside segment.facets. Unlike the other segment fields, facets is a semicolon-separated key=value string that must be parsed before its values can be forwarded as query parameters or path facets. For example:


_10
zip-code=22250-040;country=BRA;brand=acme;category-1=tv

The following TypeScript snippets show the full translation for each affected endpoint.

GET Search products (v1)

_66
type Segment = {
_66
channel?: string | number
_66
regionId?: string
_66
countryCode?: string
_66
cultureInfo?: string
_66
// Semicolon-separated "key=value" string, e.g. "zip-code=22250-040;country=BRA"
_66
facets?: string
_66
utm_source?: string
_66
utm_campaign?: string
_66
utmi_campaign?: string
_66
campaigns?: string
_66
priceTables?: string
_66
}
_66
_66
const SHIPPING_KEYS = new Set([
_66
'zip-code', 'coordinates', 'country', 'pickupPoint',
_66
'deliveryZonesHash', 'pickupPointsHash',
_66
])
_66
_66
function segmentToProductSearchV1(segment: Segment, query?: string): string {
_66
const shipping: Record<string, string> = {}
_66
const pathFacets: Array<{ key: string; value: string }> = []
_66
_66
for (const pair of (segment.facets ?? '').split(';')) {
_66
const eq = pair.indexOf('=')
_66
if (eq < 0) continue
_66
const key = pair.slice(0, eq)
_66
const value = pair.slice(eq + 1)
_66
if (!key || !value) continue
_66
_66
if (SHIPPING_KEYS.has(key)) {
_66
shipping[key] = value
_66
} else {
_66
pathFacets.push({ key, value })
_66
}
_66
}
_66
_66
const params: Record<string, string> = {}
_66
const set = (name: string, value?: string | number) => {
_66
if (value !== undefined && value !== null && value !== '') {
_66
params[name] = String(value)
_66
}
_66
}
_66
_66
set('sc', segment.channel)
_66
set('locale', segment.cultureInfo)
_66
set('regionId', segment.regionId)
_66
set('country', segment.countryCode ?? shipping.country)
_66
set('zip-code', shipping['zip-code'])
_66
set('coordinates', shipping.coordinates)
_66
set('pickupPoint', shipping.pickupPoint)
_66
set('deliveryZonesHash', shipping.deliveryZonesHash)
_66
set('pickupPointsHash', shipping.pickupPointsHash)
_66
set('utmSource', segment.utm_source)
_66
set('utmCampaign', segment.utm_campaign)
_66
set('utmiCampaign', segment.utmi_campaign)
_66
set('campaigns', segment.campaigns)
_66
set('priceTables', segment.priceTables)
_66
_66
if (query) params.query = query
_66
_66
const facetPath = pathFacets.map(f => `$\{f.key\}/$\{f.value\}`).join('/')
_66
const search = new URLSearchParams(params).toString()
_66
_66
return `https://\{accountName\}.vtexcommercestable.com.br/api/intelligent-search/v1/product-search$\{facetPath ? `/${facetPath}` : ''\}?$\{search\}`
_66
}

GET List filters for a search (v1)

_56
type Segment = {
_56
channel?: string | number
_56
regionId?: string
_56
countryCode?: string
_56
cultureInfo?: string
_56
// Semicolon-separated "key=value" string, e.g. "zip-code=22250-040;country=BRA;brand=acme"
_56
facets?: string
_56
}
_56
_56
const SHIPPING_KEYS = new Set([
_56
'zip-code', 'coordinates', 'country', 'pickupPoint',
_56
'deliveryZonesHash', 'pickupPointsHash',
_56
])
_56
_56
function segmentToFacetsV1(segment: Segment, query?: string): string {
_56
const shipping: Record<string, string> = {}
_56
const pathFacets: Array<{ key: string; value: string }> = []
_56
_56
for (const pair of (segment.facets ?? '').split(';')) {
_56
const eq = pair.indexOf('=')
_56
if (eq < 0) continue
_56
const key = pair.slice(0, eq)
_56
const value = pair.slice(eq + 1)
_56
if (!key || !value) continue
_56
_56
if (SHIPPING_KEYS.has(key)) {
_56
shipping[key] = value
_56
} else {
_56
pathFacets.push({ key, value })
_56
}
_56
}
_56
_56
const params: Record<string, string> = {}
_56
const set = (name: string, value?: string | number) => {
_56
if (value !== undefined && value !== null && value !== '') {
_56
params[name] = String(value)
_56
}
_56
}
_56
_56
set('sc', segment.channel)
_56
set('locale', segment.cultureInfo)
_56
set('regionId', segment.regionId)
_56
set('country', segment.countryCode ?? shipping.country)
_56
set('zip-code', shipping['zip-code'])
_56
set('coordinates', shipping.coordinates)
_56
set('pickupPoint', shipping.pickupPoint)
_56
set('deliveryZonesHash', shipping.deliveryZonesHash)
_56
set('pickupPointsHash', shipping.pickupPointsHash)
_56
_56
if (query) params.query = query
_56
_56
const facetPath = pathFacets.map(f => `$\{f.key\}/$\{f.value\}`).join('/')
_56
const search = new URLSearchParams(params).toString()
_56
_56
return `https://\{accountName\}.vtexcommercestable.com.br/api/intelligent-search/v1/facets$\{facetPath ? `/${facetPath}` : ''\}?$\{search\}`
_56
}

GET Get product (v1)

_59
type Segment = {
_59
channel?: string | number
_59
regionId?: string
_59
countryCode?: string
_59
cultureInfo?: string
_59
// Semicolon-separated "key=value" string, e.g. "zip-code=22250-040;country=BRA"
_59
facets?: string
_59
utm_source?: string
_59
utm_campaign?: string
_59
utmi_campaign?: string
_59
campaigns?: string
_59
priceTables?: string
_59
}
_59
_59
const SHIPPING_KEYS = new Set([
_59
'zip-code', 'coordinates', 'country', 'pickupPoint',
_59
'deliveryZonesHash', 'pickupPointsHash',
_59
])
_59
_59
function segmentToProductsV1(segment: Segment, identifier: string): string {
_59
const shipping: Record<string, string> = {}
_59
_59
for (const pair of (segment.facets ?? '').split(';')) {
_59
const eq = pair.indexOf('=')
_59
if (eq < 0) continue
_59
const key = pair.slice(0, eq)
_59
const value = pair.slice(eq + 1)
_59
if (!key || !value) continue
_59
if (SHIPPING_KEYS.has(key)) shipping[key] = value
_59
}
_59
_59
const params: Record<string, string> = {}
_59
const set = (name: string, value?: string | number) => {
_59
if (value !== undefined && value !== null && value !== '') {
_59
params[name] = String(value)
_59
}
_59
}
_59
_59
set('sc', segment.channel)
_59
set('locale', segment.cultureInfo)
_59
set('regionId', segment.regionId)
_59
set('country', segment.countryCode ?? shipping.country)
_59
set('zip-code', shipping['zip-code'])
_59
set('coordinates', shipping.coordinates)
_59
set('pickupPoint', shipping.pickupPoint)
_59
set('deliveryZonesHash', shipping.deliveryZonesHash)
_59
set('pickupPointsHash', shipping.pickupPointsHash)
_59
set('utmSource', segment.utm_source)
_59
set('utmCampaign', segment.utm_campaign)
_59
set('utmiCampaign', segment.utmi_campaign)
_59
set('campaigns', segment.campaigns)
_59
set('priceTables', segment.priceTables)
_59
_59
params.identifier = identifier
_59
_59
const search = new URLSearchParams(params).toString()
_59
_59
return `https://\{accountName\}.vtexcommercestable.com.br/api/intelligent-search/v1/products?$\{search\}`
_59
}

GET Get pickup point availability for Delivery Promise (v1)

_45
type Segment = {
_45
channel?: string | number
_45
countryCode?: string
_45
cultureInfo?: string
_45
// Semicolon-separated "key=value" string, e.g. "zip-code=22250-040;country=BRA"
_45
facets?: string
_45
}
_45
_45
const SHIPPING_KEYS = new Set([
_45
'zip-code', 'coordinates', 'country', 'pickupPoint',
_45
'deliveryZonesHash', 'pickupPointsHash',
_45
])
_45
_45
function segmentToPickupPointAvailabilityV1(segment: Segment): string {
_45
const shipping: Record<string, string> = {}
_45
_45
for (const pair of (segment.facets ?? '').split(';')) {
_45
const eq = pair.indexOf('=')
_45
if (eq < 0) continue
_45
const key = pair.slice(0, eq)
_45
const value = pair.slice(eq + 1)
_45
if (!key || !value) continue
_45
if (SHIPPING_KEYS.has(key)) shipping[key] = value
_45
}
_45
_45
const params: Record<string, string> = {}
_45
const set = (name: string, value?: string | number) => {
_45
if (value !== undefined && value !== null && value !== '') {
_45
params[name] = String(value)
_45
}
_45
}
_45
_45
set('sc', segment.channel)
_45
set('locale', segment.cultureInfo)
_45
set('country', segment.countryCode ?? shipping.country)
_45
set('zip-code', shipping['zip-code'])
_45
set('coordinates', shipping.coordinates)
_45
set('pickupPoint', shipping.pickupPoint)
_45
set('deliveryZonesHash', shipping.deliveryZonesHash)
_45
set('pickupPointsHash', shipping.pickupPointsHash)
_45
_45
const search = new URLSearchParams(params).toString()
_45
_45
return `https://\{accountName\}.vtexcommercestable.com.br/api/intelligent-search/v1/pickup-point-availability?$\{search\}`
_45
}

Regionalization parameter (if applicable)

If your store uses regionalization, regionId was a top-level segment field in the Intelligent Search API (Legacy). In Intelligent Search API v1, pass it explicitly on GET Search products (v1) and GET List filters for a search (v1):


_10
?regionId=v2.C0FC2DE04D6A7C9E1DC22C0D7EBD939B

Delivery Promise parameters (if applicable)

If your store uses Delivery Promise for headless stores, pass the buyer's location as explicit query parameters on GET Search products (v1), GET List filters for a search (v1), and GET Get pickup point availability for Delivery Promise (v1).

The fundamental parameters are the buyer's address: country, zip-code, and optionally coordinates:


_10
?country=BRA&zip-code=22271020&coordinates=-43.19532775878906,-22.955032348632812

As an alternative, deliveryZonesHash and pickupPointsHash can be passed for faster lookup. They are pre-computed in POST Search delivery zones and POST Search pickup points, respectively. Since hashes expire and require a specific renewal flow, always have the buyer's address available as a fallback.

If your integration reads a VTEX segment cookie, all of these values may already be present in the segment facets string (country, zip-code, coordinates, pickupPoint, deliveryZonesHash, pickupPointsHash). Extract and forward them as explicit query parameters.

Step 4 - Replace single-product search with the new Get product endpoint

If you use GET Search products to fetch a single known product (for example, to render a product detail page from a URL slug), replace it with GET Get product.

This endpoint accepts a value and a field parameter, skips the search pipeline, and responds faster with a higher cache-hit rate.

Before: Fetching a single product via product search


_10
curl 'https://{accountName}.vtexcommercestable.com.br/api/io/_v/api/intelligent-search/product_search/trade-policy/1?query=product.link:blue-shirt'

After: Fetching a single product by slug


_10
curl 'https://{accountName}.vtexcommercestable.com.br/api/intelligent-search/v1/products?sc=1&field=slug&value=blue-shirt'

Supported field values:

ValueIdentifier type
id (default)Product ID (fastest, skips search pipeline entirely)
slugProduct slug (link text)
eanSKU EAN
skuSKU ID
referenceSKU reference ID

Caching behavior

Responses from most endpoints now include a Cache-Control header, enabling CDN and browser caching for public sales channels. This reduces origin load and improves storefront response times.

Always read the Cache-Control header at runtime to determine cacheability. Don't hardcode cache durations in your integration.

Exceptions:

  • Responses containing sponsored products (VTEX Ads) are not cached, preventing ad impressions from being served from a shared cache.
  • Private sales channel responses: not cached.

Migration checklist

TaskStepRequired
Updated base URL to /api/intelligent-search/v1Step 1Yes
Renamed all endpoint paths (underscores to hyphens)Step 2Yes
Passing locale explicitlyStep 3Yes
Passing sc query parameter explicitlyStep 3Yes
Passing regionId explicitlyStep 3If applicable
Passing Delivery Promise parameters explicitly (country, zip-code, coordinates, pickupPoint, deliveryZonesHash, pickupPointsHash)Step 3If applicable
Passing UTM and marketing parameters explicitly (utmSource, utmCampaign, utmiCampaign, campaigns, priceTables)Step 3If applicable
Replaced single-product search calls with GET Get product (v1)Step 4If applicable
Contributors
1
Photo of the contributor
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
Was this helpful?
Suggest edits (GitHub)
On this page