Constructor is upgrading the Facets and Searchabilities API from /v1 to /v2. This guide covers what's changing and how to migrate your integration.
Key dates:
- Accounts that only use the dashboard have already been migrated automatically.
- All new indexes, sections, and accounts created after February 17, 2026 use v2 by default.
- Existing accounts using the API directly will be migrated within the next few months. You will be contacted with your specific migration date.
Version access:
- Calling
/v2endpoints before your account is migrated returns404. - Calling
/v1endpoints after migration returns410 Gone. - Only one API version is active per index at a time.
API reference:
What are the benefits of a version 2?
In v1, creating a facet was a two-step process:
- create the searchability
- create the facet configuration
The facet configuration did not directly reference the metadata field path. Instead, the system matched facets to searchabilities behind the scenes using the field name. This coupling meant both objects had to be kept in sync, and the relationship between them was implicit. It also forced the creation of a searchability even when the field only needed to be a facet (i.e., not searchable or displayable).
v1 flow: Searchability (facetable=true, name="facets.brand") ←→ Facet Configuration (name="brand")
In v2, we are decoupling facets from searchabilities. Each facet configuration includes a new path_in_metadata field that directly specifies the metadata field path. This eliminates the need to set facetable=true on a searchability to define a facet, and makes each facet configuration self-contained.
v2 flow: Facet Configuration (name="brand", path_in_metadata="facets.brand")
What's changing
Below is the changelog for the Facets API and Searchabilities API.
Facets API v2 changes
| Endpoint | Change | Breaking? |
|---|---|---|
| All | Added path_in_metadata field in request and response bodies. Required on POST and PUT. | Yes |
| All | options removed from request and response bodies. Use the separate /v1/facets/{name}/options endpoint to manage options. | Yes |
| All | Facet renaming is forbidden. Sending a name that differs from the URL returns 409. | Yes |
POST, PUT | position must be >= 1. | Yes |
PATCH /v2/facets | Request body changed from bare array to {"facets": [...]} wrapper. | Yes |
PATCH /v2/facets | Response body changed from bare array to {"facets": [...]} wrapper. | Yes |
PUT /v2/facets/{name} | Replacing a facet no longer resets facet options. Options are unaffected. | Behavior change |
Searchabilities API v2 changes
| Endpoint | Change | Breaking? |
|---|---|---|
| All | facetable removed from request and response bodies. | Yes |
| All | type removed from response body. Use /v1/items_fields_stats instead. | Yes |
| All | percentage_presence removed from response body. Use /v1/items_fields_stats instead. | Yes |
| All | example_items removed from response body. Use /v1/items_fields_stats instead. | Yes |
| All | default removed from response body. | Yes |
GET /v2/searchabilities | filters, includes, excludes query parameters replaced with flat query parameters. See parameter mapping below. | Yes |
GET /v2/searchabilities | name filter is now case-sensitive and uses * wildcard syntax instead of implicit substring match. See filtering by name in v2 below. | Yes |
GET /v2/searchabilities | search_configurable query parameter removed. | Yes |
GET /v2/searchabilities | searchable query parameter removed. | Yes |
GET /v2/searchabilities | sort_by=percentage_presence removed. Only sort_by=name is supported. | Yes |
PATCH /v2/searchabilities/{name} | New searchabilities inherit displayable from the nearest parent searchability when not explicitly set. | Behavior change |
Searchabilities filter parameter mapping
The nested filters, includes, and excludes query parameters on GET /searchabilities have been replaced with flat query parameters:
| v1 parameter | v2 parameter | Notes |
|---|---|---|
filters[fuzzy_searchable]=true | fuzzy_searchable=true | match_type=and is the default |
filters[exact_searchable]=true | exact_searchable=true | |
filters[displayable]=true | displayable=true | |
filters[name]=value | name=value | Now case-sensitive. See filtering by name in v2 below. |
includes[fuzzy_searchable]=true&includes[displayable]=true | fuzzy_searchable=true&displayable=true&match_type=or | Use match_type=or to OR filters together |
excludes[...] | (removed) | |
search_configurable=true | (removed) | |
searchable=true | (removed) | |
sort_by=percentage_presence | (removed) | Only sort_by=name is supported in v2 |
Filtering by name (wildcard syntax)
| Pattern | Behavior | Example |
|---|---|---|
name=value | Exact match | name=brand matches only brand |
name=value* | Starts with | name=brand* matches brand, brand_name |
name=*value | Ends with | name=*color matches color, shirt_color |
name=*value* | Contains (equivalent to v1 default) | name=*brand* matches brand, my_brand_name |
Note: In v1,
filters[name]=brandwas implicitly treated as a case-insensitive substring match (equivalent to*brand*). In v2,name=brandis an exact, case-sensitive match. To replicate v1 behavior, usename=*brand*.
How to migrate your integration
We recommend supporting both /v1 and /v2 API versions in your code, so your code can seamlessly switch from /v1 to /v2 to avoid downtime.
How migration works
What exactly happens when your index is being migrated?
- A brief maintenance window occurs (~5 minutes) where
/v1endpoints return503. - The system automatically populates the
path_in_metadatafield for all existing facets by matching them to their corresponding facetable searchabilities. - Facet configurations that have no matching facetable searchability are deleted (along with their options).
- Searchabilities whose only purpose was to enable faceting (i.e., they are not fuzzy searchable, not exact searchable, and not displayable) are removed automatically, as they no longer serve a function in v2.
- After an index rebuild,
/v2endpoints become active./v1endpoints return410 Gone.
Warning: Review your facet and searchability configurations before migration. Facet configurations without a corresponding facetable searchability will be deleted. Facet-only searchabilities (not searchable or displayable) will be removed. These were already inactive during index builds, so search results should not be affected.
Support both /v1 and /v2 APIs
/v1 and /v2 APIsThe /v1 and /v2 API responses follow a predictable pattern depending on the migration phase:
| Migration phase | /v1 response | /v2 response | Action |
|---|---|---|---|
| Before migration | 200 Normal response | 404 | Use /v1 |
| During migration (~5 min) | 503 | 404 | Retry with backoff for up to 10 minutes |
| After migration | 410 | 200 Normal response | Switch to /v2 |
Response body reference:
| Status | Response body |
|---|---|
404 | {"message": "/v2/facets API is not enabled for your account yet; please contact customer manager for migration steps"} |
503 | {"message": "We cannot process your request due to maintenance, please try again later."} |
410 | {"message": "/v1/facets API is deprecated; use /v2/facets"} |
The suggested way to support both API versions:
- Send request to the
/v1endpoint. - If you receive
503with the maintenance message, retry with exponential backoff. The maintenance window typically lasts under 5 minutes but allow up to 10 minutes before treating it as an error. - If you receive
410 Gone, switch all subsequent requests for that index to/v2endpoints. - Once you confirm all your indexes have been migrated, remove the v1 fallback logic entirely.
Testing
- Populate your development index with production data (or create a development index).
- Support both
/v1and/v2API in your code, as described above. - Let us migrate development index to
/v2.- During migration, observe how your code handles
503 Maintenanceand410 Goneresponse codes.
- During migration, observe how your code handles
- Once you have tested the
/v2API and are happy with it, deploy this code (supporting/v1and/v2) to production. It won't cause any changes as only the/v1branch of the code will be executed. - Let us migrate prod index to
/v2. - Once everything works as expected, you can remove the
/v1branch from your code.
Migration checklist
Use this checklist to verify your migration is complete.
Facets API
- Add
path_in_metadatato all facet create (POST) and replace (PUT) requests - Remove
optionsfrom all facet request bodies - Update code that reads
optionsfrom facet responses. UseGET /v1/facets/{name}/optionsinstead - Remove any facet rename logic (v2 returns
409on rename attempts) - Ensure
positionvalues are >= 1 in POST and PUT requests - Update batch PATCH
/facetsrequest format to use{"facets": [...]}wrapper - Update batch PATCH
/facetsresponse parsing to expect{"facets": [...]}wrapper
Searchabilities API
- Remove
facetablefrom all request bodies - Remove references to
facetable,type,percentage_presence,example_items, anddefaultfrom response parsing. Use /v1/items_fields_stats endpoint to retrievetype,percentage_presenceandexample_items. - Replace
filters[...],includes[...], andexcludes[...]with flat query parameters (fuzzy_searchable,exact_searchable,displayable,name,match_type) - Update
namefilter to use explicit*wildcards for substring matching - Account for case-sensitive
namefiltering - Remove usage of
search_configurable,searchable, andsort_by=percentage_presenceparameters
General
- Replace
/v1/with/v2/in facets and searchabilities endpoint URLs (note: facet options endpoints remain at/v1/facets/{name}/options) - Handle
410 Goneresponses from v1 endpoints as an indicator that migration has occurred - Test your updated integration against your test index before production migration
SDK support
The official Constructor SDKs have been updated with new v2 methods alongside the existing v1 methods. V1 methods remain unchanged, and facet option methods are unaffected.
| SDK | Facets v2 | Searchabilities v2 |
|---|---|---|
| Node.js | addFacetConfigurationV2, getFacetConfigurationsV2, getFacetConfigurationV2, modifyFacetConfigurationsV2, modifyFacetConfigurationV2, replaceFacetConfigurationV2, createOrReplaceFacetConfigurationsV2, removeFacetConfigurationV2 | retrieveSearchabilitiesV2, getSearchabilityV2, patchSearchabilitiesV2, patchSearchabilityV2, deleteSearchabilitiesV2, deleteSearchabilityV2 |
| Java | createFacetConfigurationV2, retrieveFacetConfigurationsV2, retrieveFacetConfigurationV2, updateFacetConfigurationsV2, updateFacetConfigurationV2, replaceFacetConfigurationV2, replaceFacetConfigurationsV2, deleteFacetConfigurationV2 | retrieveSearchabilitiesV2, retrieveSearchabilityV2, createOrUpdateSearchabilitiesV2, createOrUpdateSearchabilityV2, deleteSearchabilitiesV2, deleteSearchabilityV2 |
| .NET | CreateFacetConfigV2, GetAllFacetConfigsV2, GetFacetConfigV2, CreateOrReplaceFacetConfigsV2, BatchPartiallyUpdateFacetConfigsV2, ReplaceFacetConfigV2, PartiallyUpdateFacetConfigV2, DeleteFacetConfigV2 | RetrieveSearchabilitiesV2, GetSearchabilityV2, PatchSearchabilitiesV2, PatchSearchabilityV2, DeleteSearchabilitiesV2, DeleteSearchabilityV2 |
To migrate:
- Update to the latest SDK version
- Replace v1 method calls with their
V2equivalents - Add
pathInMetadata(orpath_in_metadatain Java/.NET) to facet create/replace calls - Remove
facetablefrom searchability parameters.
Migration examples by endpoint
Facets: Creating a facet
The key change: add path_in_metadata, remove options from the request body. You no longer need to create a searchability with facetable=true first.
Show before/after example
Before (v1): two separate calls required:
# 1. Create a searchability with facetable=true
curl -X PATCH "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"searchabilities": [
{"name": "facets.brand", "facetable": true, "displayable": true}
]
}'
# 2. Create the facet configuration (with inline options)
curl -X POST "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
"display_name": "Brand",
"options": [
{"value": "Nike", "display_name": "Nike", "position": 1}
]
}'After (v2): facet configuration with path_in_metadata, options managed separately:
# 1. Create the facet configuration
curl -X POST "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
+ "path_in_metadata": "facets.brand",
"display_name": "Brand"
}'
# 2. (Optional) Configure facet options separately
curl -X PATCH "https://ac.cnstrc.com/v1/facets/brand/options?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"options": [
{"value": "Nike", "display_name": "Nike", "position": 1}
]
}'Facets: Retrieving a facet
Response now includes path_in_metadata but no longer includes options. Retrieve options via GET /v1/facets/{name}/options.
Show before/after example
Before (v1):
curl "https://ac.cnstrc.com/v1/facets/brand?key=YOUR_KEY"Response includes options array inline.
After (v2):
curl "https://ac.cnstrc.com/v2/facets/brand?key=YOUR_KEY"Response includes path_in_metadata but no options. To retrieve options:
curl "https://ac.cnstrc.com/v1/facets/brand/options?key=YOUR_KEY"Facets: Updating a facet (PATCH)
Remove options from the body. Renaming is no longer allowed. Sending a different name returns 409.
Show before/after example
Before (v1):
curl -X PATCH "https://ac.cnstrc.com/v1/facets/brand?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
- "name": "Brand",
"display_name": "Brand Name",
- "options": [{"value": "Nike", "position": 1}]
}'After (v2):
curl -X PATCH "https://ac.cnstrc.com/v2/facets/brand?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Brand Name"
}'Warning: To rename a facet, delete it and create a new one. Options must be managed via
/v1/facets/{name}/options.
Facets: Replacing a facet (PUT)
Add path_in_metadata, remove options. In v2, PUT no longer resets facet options.
Show before/after example
Before (v1):
curl -X PUT "https://ac.cnstrc.com/v1/facets/brand?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
"display_name": "Brand",
- "options": [{"value": "Nike", "position": 1}]
}'After (v2):
curl -X PUT "https://ac.cnstrc.com/v2/facets/brand?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
+ "path_in_metadata": "facets.brand",
"display_name": "Brand"
}'Note:
namein the body must match the URL parameter.
Facets: Batch update (PATCH /facets)
Request and response body changed from a bare array to an object with a facets key.
Show before/after example
Before (v1) bare array:
curl -X PATCH "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '[
{"name": "brand", "display_name": "Brand Name"},
{"name": "color", "display_name": "Color"}
]'
# Response: [{facet_1}, {facet_2}]After (v2) wrapped in {"facets": [...]}:
curl -X PATCH "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "facets": [
{"name": "brand", "display_name": "Brand Name"},
{"name": "color", "display_name": "Color"}
+ ]
}'
# Response: {"facets": [{facet_1}, {facet_2}]}Facets: Bulk create/replace (PUT /facets)
Add path_in_metadata to each facet. options are no longer accepted inline. Configure them separately via /v1/facets/{name}/options.
Show before/after example
Before (v1):
curl -X PUT "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"facets": [
- {"name": "Brand", "type": "multiple", "display_name": "Brand Name", "options": [{"value": "Nike"}]},
- {"name": "Price", "type": "range", "range_format": "boundaries"}
]
}'After (v2):
curl -X PUT "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"facets": [
+ {"name": "Brand", "type": "multiple", "path_in_metadata": "facets.brand", "display_name": "Brand Name"},
+ {"name": "Price", "type": "range", "path_in_metadata": "facets.price", "range_format": "boundaries"}
]
}'Searchabilities: Remove facetable from all requests
facetable from all requestsThe facetable field is no longer accepted or returned. Facet definitions are managed entirely through the Facets API.
Show before/after example
Before (v1):
curl -X PATCH "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"searchabilities": [
- {"name": "brand", "facetable": true, "displayable": true, "fuzzy_searchable": true}
]
}'After (v2):
curl -X PATCH "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"searchabilities": [
+ {"name": "brand", "displayable": true, "fuzzy_searchable": true}
]
}'Searchabilities: removed response fields
The following fields are no longer returned. If your code reads any of these, remove those references:
| Removed field | Replacement |
|---|---|
facetable | Managed via Facets API (path_in_metadata on facet configuration) |
type | Use /v1/items_fields_stats |
percentage_presence | Use /v1/items_fields_stats |
example_items | Use /v1/items_fields_stats |
default | (No replacement. This field was deprecated) |
Searchabilities: update list filtering
The nested filters, includes, and excludes parameters have been replaced with flat query parameters. See the parameter mapping table above for the full reference.
Show before/after examples
AND filter before (v1):
curl "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY&filters[fuzzy_searchable]=true&filters[displayable]=true"AND filter after (v2), flat parameters, AND by default:
curl "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY&fuzzy_searchable=true&displayable=true"OR filter before (v1):
curl "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY&includes[fuzzy_searchable]=true&includes[displayable]=true"OR filter after (v2), usematch_type=or:
curl "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY&fuzzy_searchable=true&displayable=true&match_type=or"Name filter before (v1), implicit substring, case-insensitive:
curl "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY&filters[name]=brand"Name filter after (v2), explicit wildcard, case-sensitive:
# To replicate v1 behavior (substring, but now case-sensitive):
curl "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY&name=*brand*"
# Exact match:
curl "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY&name=brand"End-to-end examples
Range facet (price slider)
Before (v1):
# Step 1: Make "price" facetable
curl -X PATCH "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"searchabilities": [{"name": "facets.price", "facetable": true}]}'
# Step 2: Configure facet
curl -X POST "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "price", "type": "range", "range_format": "boundaries", "display_name": "Price"}'After (v2):
# Single call, no searchability needed for faceting
curl -X POST "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "price",
"type": "range",
"path_in_metadata": "facets.price",
"range_format": "boundaries",
"display_name": "Price"
}'Note: You still need a searchability for
priceif you want it to be searchable or displayable in search results, but you no longer needfacetable=trueon it.
Nested metadata field as a facet
Before (v1):
curl -X PATCH "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"searchabilities": [{"name": "facets.attributes.color", "facetable": true, "displayable": true}]}'
curl -X POST "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "color", "type": "multiple"}'After (v2):
curl -X POST "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "color",
"type": "multiple",
"path_in_metadata": "facets.attributes.color"
}'The path_in_metadata field directly references the nested path, removing the implicit name-based matching.
Full facet setup with options
Before (v1):
# Step 1: Make "brand" facetable via searchabilities
curl -X PATCH "https://ac.cnstrc.com/v1/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"searchabilities": [
{"name": "facets.brand", "facetable": true, "displayable": true}
]
}'
# Step 2: Configure facet with inline options
curl -X POST "https://ac.cnstrc.com/v1/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
"display_name": "Brand",
"sort_order": "num_matches",
"sort_descending": true,
"options": [
{"value": "Nike", "display_name": "Nike", "position": 1},
{"value": "Adidas", "display_name": "Adidas", "position": 2}
]
}'After (v2):
# Step 1: Configure searchability (without facetable)
curl -X PATCH "https://ac.cnstrc.com/v2/searchabilities?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"searchabilities": [{"name": "facets.brand", "displayable": true}]
}'
# Step 2: Create the facet with path_in_metadata
curl -X POST "https://ac.cnstrc.com/v2/facets?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "brand",
"type": "multiple",
"path_in_metadata": "facets.brand",
"display_name": "Brand",
"sort_order": "num_matches",
"sort_descending": true
}'
# Step 3: Configure facet options separately
curl -X PATCH "https://ac.cnstrc.com/v1/facets/brand/options?key=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"options": [
{"value": "Nike", "display_name": "Nike", "position": 1},
{"value": "Adidas", "display_name": "Adidas", "position": 2}
]
}'FAQ
Do I need to create a searchability before creating a facet in v2?
No. In v2, the facet configuration is self-contained: path_in_metadata directly references the metadata field. However, you still need searchabilities if you want a field to be searchable (fuzzy or exact) or displayable in search results.
What happens to my existing facet options during migration?
Facet options are preserved. They are just no longer returned inline with facet configurations. Use the /v1/facets/{name}/options endpoint to manage them.
What if I have facet configurations that don't match any searchability?
During migration, facet configurations without a corresponding facetable searchability will be deleted along with their options. These configurations were already being ignored during index builds, so this should not affect your search results.
Will any searchabilities be removed during migration?
Yes. Searchabilities that existed solely to enable faceting (i.e., they are not fuzzy searchable, not exact searchable, and not displayable) will be removed automatically. Since the facetable property no longer exists in v2, these searchabilities would have no remaining function.
Can I test v2 before my production index is migrated?
Yes. Contact your customer manager to migrate a test index first. You can run your updated pipelines against the test index to verify everything works before migrating production.
How do I know which
path_in_metadatato use for my facets?
The path_in_metadata value corresponds to the path in the metadata of each item where the facet data is present. It should map to metadata_name value of /v1 searchability. For example, if your items have {"facets": {"brand": "Nike"}}, then path_in_metadata would be facets.brand. During automatic migration, these paths are filled based on your existing facetable searchabilities.
Is the
path_in_metadatafield unique?
Yes. Each path_in_metadata value must be unique within your index. Attempting to create or update a facet with a duplicate path returns 409 Conflict.
Why can't I rename facets in v2?
Facet names serve as the resource identifier in the URL (e.g., /v2/facets/brand). Renaming would break this contract. If you need to rename a facet, delete it and create a new one with the desired name.
Is there a way to revert index back to
v1?
Yes, it's supported. Please ask your Customer Success representative.