curl --request GET \
--url https://www.closient.com/search/api/v1/searchimport requests
url = "https://www.closient.com/search/api/v1/search"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://www.closient.com/search/api/v1/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://www.closient.com/search/api/v1/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://www.closient.com/search/api/v1/search"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://www.closient.com/search/api/v1/search")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/search/api/v1/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"query": "organic trail mix",
"results": [
{
"allergen_data_confidence": "high",
"certifications": [
"organic",
"non_gmo"
],
"contains_allergens": [
"tree_nuts"
],
"description": "Premium trail mix with almonds and cranberries.",
"id": "00012345678905",
"metadata": {
"brand": "Acme Snacks",
"gtin": "00012345678905",
"image_url": "https://cdn.closient.com/products/00012345678905/hero.jpg"
},
"name": "Acme Trail Mix 12oz",
"rank": 0.8752,
"substance_flags": [],
"type": "product"
},
{
"allergen_data_confidence": "low",
"certifications": [],
"contains_allergens": [],
"description": "Family-owned snack brand based in Vermont.",
"id": "keATfB8VP2gSjcnTbsMNQL",
"metadata": {
"website": "https://acmesnacks.example"
},
"name": "Acme Snacks",
"rank": 0.6231,
"substance_flags": [],
"type": "brand"
},
{
"allergen_data_confidence": "low",
"certifications": [],
"contains_allergens": [],
"description": "",
"id": "qrTwx49y7Pa3hjkM2pNfRb",
"metadata": {
"is_online": false,
"is_physical": true
},
"name": "Greenleaf Market",
"rank": 0.5104,
"substance_flags": [],
"type": "retailer"
}
],
"total_count": 3
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}Search the catalog
Full-text search across products, brands, retailers, substances, locations (physical stores), certifications, and traits. Uses PostgreSQL websearch parsing (the same syntax as a Google query box — quoted phrases, -exclusions, and OR are honored) with ts_rank for relevance. Falls back to substring (icontains) matching when full-text returns no rows, so partial words like trai still surface relevant items.
Pass ?type= (repeatable) to limit the result set to a subset of resource collections. Results are merged across collections, ranked together, and truncated to limit.
Auth: unauthenticated (anonymous IP-throttled at 60 requests/minute).
curl --request GET \
--url https://www.closient.com/search/api/v1/searchimport requests
url = "https://www.closient.com/search/api/v1/search"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://www.closient.com/search/api/v1/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://www.closient.com/search/api/v1/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://www.closient.com/search/api/v1/search"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://www.closient.com/search/api/v1/search")
.asString();require 'uri'
require 'net/http'
url = URI("https://www.closient.com/search/api/v1/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"query": "organic trail mix",
"results": [
{
"allergen_data_confidence": "high",
"certifications": [
"organic",
"non_gmo"
],
"contains_allergens": [
"tree_nuts"
],
"description": "Premium trail mix with almonds and cranberries.",
"id": "00012345678905",
"metadata": {
"brand": "Acme Snacks",
"gtin": "00012345678905",
"image_url": "https://cdn.closient.com/products/00012345678905/hero.jpg"
},
"name": "Acme Trail Mix 12oz",
"rank": 0.8752,
"substance_flags": [],
"type": "product"
},
{
"allergen_data_confidence": "low",
"certifications": [],
"contains_allergens": [],
"description": "Family-owned snack brand based in Vermont.",
"id": "keATfB8VP2gSjcnTbsMNQL",
"metadata": {
"website": "https://acmesnacks.example"
},
"name": "Acme Snacks",
"rank": 0.6231,
"substance_flags": [],
"type": "brand"
},
{
"allergen_data_confidence": "low",
"certifications": [],
"contains_allergens": [],
"description": "",
"id": "qrTwx49y7Pa3hjkM2pNfRb",
"metadata": {
"is_online": false,
"is_physical": true
},
"name": "Greenleaf Market",
"rank": 0.5104,
"substance_flags": [],
"type": "retailer"
}
],
"total_count": 3
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}{
"detail": "The requested resource was not found.",
"error_code": "not_found",
"retryable": false,
"status": 404,
"timestamp": "2026-03-31T12:00:00+00:00",
"title": "Not Found",
"type": "https://closient.com/docs/errors/not_found"
}Query Parameters
Search query text. Supports quoted phrases ("trail mix"), exclusion (trail -mix), and OR operators. A NUL (0x00) byte is rejected with a 422: the database cannot store one, so no search can be run against it.
1 - 255^[^\x00]{1,255}$Filter results to one or more resource collections. Repeat the parameter to include multiple (e.g. ?type=product&type=certification). Omit to search all seven.
Resource types returned by the catalog search endpoint.
Each result row carries one of these in its type field. The
GET /search endpoint accepts the same values via the repeated
?type= query parameter to filter results to a subset.
product, brand, retailer, substance, location, certification, trait Maximum number of results to return. Capped at 100.
1 <= x <= 100Allergen slugs to drop (C-2961). Products whose contains_allergens intersect any value here are removed from the response. Repeat the parameter for multiple slugs (e.g. ?exclude_allergens=peanuts&exclude_allergens=tree_nuts). Accepts the same vocabulary as the session-based constraint extractor.
^[^\x00]{0,}$Certification slugs the product must hold (C-2961). A product is dropped if any required slug is missing from its certifications. Repeat the parameter for multiple slugs. Brand/retailer rows are passed through unchanged.
^[^\x00]{0,}$Substance slugs to drop (C-2961). Matched substances surface inline in each remaining row's substance_flags when present, so callers can build 'flagged because X' UI affordances. Repeat for multiple.
^[^\x00]{0,}$Response
OK
Response envelope for GET /search.
Wraps the matched rows with the original query and a result count.
Results are pre-sorted by rank descending. total_count reports
the size of the returned list, not the size of the corpus — this
endpoint truncates to limit and does not paginate.
The search query that was executed (echoed back for client-side correlation).
Number of results in this response. Always equals len(results).
x >= 0Matched rows sorted by rank descending.
Show child attributes
Show child attributes