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-Controlheader, 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
GETGet 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, andimages[].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:
_10https://{accountName}.vtexcommercestable.com.br/api/io/_v/api/intelligent-search
After:
_10https://{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_searchesGet list of the 10 most searched terms | GET /api/intelligent-search/v1/top-searchesGet list of the 10 most searched terms (v1) |
GET /api/io/_v/api/intelligent-search/autocomplete_suggestionsGet list of suggested terms and attributes similar to the search term | GET /api/intelligent-search/v1/autocomplete-suggestionsGet list of suggested terms and attributes similar to the search term (v1) |
GET /api/io/_v/api/intelligent-search/search_suggestionsGet list of suggested terms similar to the search term | GET /api/intelligent-search/v1/search-suggestionsGet list of suggested terms similar to the search term (v1) |
GET /api/io/_v/api/intelligent-search/correction_searchGet attempt of correction of a misspelled term | GET /api/intelligent-search/v1/correction-searchGet 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/A | GET /api/intelligent-search/v1/productsGet product (v1) |
Step 3 - Replace segment cookie context with explicit parameters
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 field | Intelligent Search API v1 parameter | Affected endpoints |
|---|---|---|
cultureInfo | locale | All endpoints |
channel | sc | 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) |
regionId | regionId | GET /product-search/{facets} Search products (v1)GET /facets/{facets} List filters for a search (v1)GET /products Get product (v1) |
countryCode | country | 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) |
utm_sourceutm_campaignutmi_campaigncampaignspriceTables | utmSourceutmCampaignutmiCampaigncampaigns (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.facets | Same-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.facets | URL path facets, appended as key/value pairs | GET /product-search/{facets} Search products (v1)GET /facets/{facets} List filters for a search (v1) |
Before:
_10curl '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:
_10curl '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:
_10zip-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)
_66type 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_66const SHIPPING_KEYS = new Set([_66 'zip-code', 'coordinates', 'country', 'pickupPoint',_66 'deliveryZonesHash', 'pickupPointsHash',_66])_66_66function 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)
_56type 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_56const SHIPPING_KEYS = new Set([_56 'zip-code', 'coordinates', 'country', 'pickupPoint',_56 'deliveryZonesHash', 'pickupPointsHash',_56])_56_56function 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)
_59type 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_59const SHIPPING_KEYS = new Set([_59 'zip-code', 'coordinates', 'country', 'pickupPoint',_59 'deliveryZonesHash', 'pickupPointsHash',_59])_59_59function 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)
_45type 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_45const SHIPPING_KEYS = new Set([_45 'zip-code', 'coordinates', 'country', 'pickupPoint',_45 'deliveryZonesHash', 'pickupPointsHash',_45])_45_45function 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
_10curl '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
_10curl 'https://{accountName}.vtexcommercestable.com.br/api/intelligent-search/v1/products?sc=1&field=slug&value=blue-shirt'
Supported field values:
| Value | Identifier type |
|---|---|
id (default) | Product ID (fastest, skips search pipeline entirely) |
slug | Product slug (link text) |
ean | SKU EAN |
sku | SKU ID |
reference | SKU 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-Controlheader 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
| Task | Step | Required |
|---|---|---|
Updated base URL to /api/intelligent-search/v1 | Step 1 | Yes |
| Renamed all endpoint paths (underscores to hyphens) | Step 2 | Yes |
Passing locale explicitly | Step 3 | Yes |
Passing sc query parameter explicitly | Step 3 | Yes |
Passing regionId explicitly | Step 3 | If applicable |
Passing Delivery Promise parameters explicitly (country, zip-code, coordinates, pickupPoint, deliveryZonesHash, pickupPointsHash) | Step 3 | If applicable |
Passing UTM and marketing parameters explicitly (utmSource, utmCampaign, utmiCampaign, campaigns, priceTables) | Step 3 | If applicable |
Replaced single-product search calls with GET Get product (v1) | Step 4 | If applicable |