curl --request POST \
--url https://search.glood.ai/api/storefront/v1/headless/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'x-shop: <x-shop>' \
--data '
{
"query": "running shoe",
"target_page": "search",
"target_id": 123,
"locale": "en-US",
"market": 123,
"facets": [
{
"key": "<string>",
"value": [
"<string>"
],
"operator": "or"
}
],
"view": "product_details",
"pagination": {
"page": 1,
"limit": 20
},
"options": {
"include_aggregations": false,
"include_highlights": false,
"include_facets": false,
"boost_in_stock": true,
"personalize": false
},
"visitor_id": "<string>",
"client_id": "<string>",
"customer_id": "<string>"
}
'import requests
url = "https://search.glood.ai/api/storefront/v1/headless/search"
payload = {
"query": "running shoe",
"target_page": "search",
"target_id": 123,
"locale": "en-US",
"market": 123,
"facets": [
{
"key": "<string>",
"value": ["<string>"],
"operator": "or"
}
],
"view": "product_details",
"pagination": {
"page": 1,
"limit": 20
},
"options": {
"include_aggregations": False,
"include_highlights": False,
"include_facets": False,
"boost_in_stock": True,
"personalize": False
},
"visitor_id": "<string>",
"client_id": "<string>",
"customer_id": "<string>"
}
headers = {
"x-shop": "<x-shop>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-shop': '<x-shop>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: 'running shoe',
target_page: 'search',
target_id: 123,
locale: 'en-US',
market: 123,
facets: [{key: '<string>', value: ['<string>'], operator: 'or'}],
view: 'product_details',
pagination: {page: 1, limit: 20},
options: {
include_aggregations: false,
include_highlights: false,
include_facets: false,
boost_in_stock: true,
personalize: false
},
visitor_id: '<string>',
client_id: '<string>',
customer_id: '<string>'
})
};
fetch('https://search.glood.ai/api/storefront/v1/headless/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://search.glood.ai/api/storefront/v1/headless/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'running shoe',
'target_page' => 'search',
'target_id' => 123,
'locale' => 'en-US',
'market' => 123,
'facets' => [
[
'key' => '<string>',
'value' => [
'<string>'
],
'operator' => 'or'
]
],
'view' => 'product_details',
'pagination' => [
'page' => 1,
'limit' => 20
],
'options' => [
'include_aggregations' => false,
'include_highlights' => false,
'include_facets' => false,
'boost_in_stock' => true,
'personalize' => false
],
'visitor_id' => '<string>',
'client_id' => '<string>',
'customer_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"x-shop: <x-shop>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://search.glood.ai/api/storefront/v1/headless/search"
payload := strings.NewReader("{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-shop", "<x-shop>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://search.glood.ai/api/storefront/v1/headless/search")
.header("x-shop", "<x-shop>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://search.glood.ai/api/storefront/v1/headless/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-shop"] = '<x-shop>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [
{
"product_id": 9311227838783,
"title": "Running Shoe",
"handle": "running-shoe",
"product_type": "Shoes",
"vendor": "Acme",
"tags": ["sale", "summer"],
"price": 49.99,
"compare_at_price": 69.99,
"display_price": 49.99,
"display_compare_at_price": 69.99,
"display_currency": "USD",
"original_currency": "USD",
"currency_rate": 1,
"available_for_sale": true,
"is_in_stock": true,
"total_inventory": 42,
"image": { "id": "gid://shopify/ProductImage/1", "url": "https://cdn.shopify.com/s/files/1/shoe.jpg", "width": 800, "height": 800, "alt_text": "Running Shoe" },
"featured_media": null,
"media": [],
"options": [ { "id": 111, "name": "Size", "position": 1, "values": ["S", "M", "L"] } ],
"variants": [
{
"variant_id": 456,
"title": "M",
"display_name": "Running Shoe - M",
"sku": "RS-M",
"position": 1,
"price": 49.99,
"compare_at_price": 69.99,
"available_for_sale": true,
"is_in_stock": true,
"sellable_online_quantity": 10,
"selected_options": [ { "name": "Size", "value": "M" } ],
"image": null
}
],
"metafields": [ { "namespace": "custom", "key": "material", "value": "leather" } ]
}
],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [
{
"key": "color",
"type": "terms",
"aggregation": { "buckets": [ { "value": "red", "count": 42, "selected": true } ] }
}
],
"metadata": { "query_time": 40, "total_time": 55, "region": "toronto", "search_type": "hybrid" }
}
{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [ { "product_id": 9311227838783 }, { "product_id": 9311227838784 } ],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [],
"metadata": { "query_time": 38, "total_time": 41, "region": "toronto", "search_type": "hybrid" }
}
{
"error": [
{
"message": "\"sort\" must be one of [relevance, title_asc, title_desc, price_asc, price_desc, best_selling, oldest, newest, collection_default]",
"path": ["sort"],
"type": "any.only",
"context": { "label": "sort", "key": "sort" }
}
],
"ok": false
}
Search
Full product search (hybrid text + vector) with applied facets, sort, page-based pagination and optional inline aggregations. Auth: Authorization: Bearer <gl_sf_...> + x-shop.
curl --request POST \
--url https://search.glood.ai/api/storefront/v1/headless/search \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'x-shop: <x-shop>' \
--data '
{
"query": "running shoe",
"target_page": "search",
"target_id": 123,
"locale": "en-US",
"market": 123,
"facets": [
{
"key": "<string>",
"value": [
"<string>"
],
"operator": "or"
}
],
"view": "product_details",
"pagination": {
"page": 1,
"limit": 20
},
"options": {
"include_aggregations": false,
"include_highlights": false,
"include_facets": false,
"boost_in_stock": true,
"personalize": false
},
"visitor_id": "<string>",
"client_id": "<string>",
"customer_id": "<string>"
}
'import requests
url = "https://search.glood.ai/api/storefront/v1/headless/search"
payload = {
"query": "running shoe",
"target_page": "search",
"target_id": 123,
"locale": "en-US",
"market": 123,
"facets": [
{
"key": "<string>",
"value": ["<string>"],
"operator": "or"
}
],
"view": "product_details",
"pagination": {
"page": 1,
"limit": 20
},
"options": {
"include_aggregations": False,
"include_highlights": False,
"include_facets": False,
"boost_in_stock": True,
"personalize": False
},
"visitor_id": "<string>",
"client_id": "<string>",
"customer_id": "<string>"
}
headers = {
"x-shop": "<x-shop>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-shop': '<x-shop>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: 'running shoe',
target_page: 'search',
target_id: 123,
locale: 'en-US',
market: 123,
facets: [{key: '<string>', value: ['<string>'], operator: 'or'}],
view: 'product_details',
pagination: {page: 1, limit: 20},
options: {
include_aggregations: false,
include_highlights: false,
include_facets: false,
boost_in_stock: true,
personalize: false
},
visitor_id: '<string>',
client_id: '<string>',
customer_id: '<string>'
})
};
fetch('https://search.glood.ai/api/storefront/v1/headless/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://search.glood.ai/api/storefront/v1/headless/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'running shoe',
'target_page' => 'search',
'target_id' => 123,
'locale' => 'en-US',
'market' => 123,
'facets' => [
[
'key' => '<string>',
'value' => [
'<string>'
],
'operator' => 'or'
]
],
'view' => 'product_details',
'pagination' => [
'page' => 1,
'limit' => 20
],
'options' => [
'include_aggregations' => false,
'include_highlights' => false,
'include_facets' => false,
'boost_in_stock' => true,
'personalize' => false
],
'visitor_id' => '<string>',
'client_id' => '<string>',
'customer_id' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"x-shop: <x-shop>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://search.glood.ai/api/storefront/v1/headless/search"
payload := strings.NewReader("{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-shop", "<x-shop>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://search.glood.ai/api/storefront/v1/headless/search")
.header("x-shop", "<x-shop>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://search.glood.ai/api/storefront/v1/headless/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-shop"] = '<x-shop>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"running shoe\",\n \"target_page\": \"search\",\n \"target_id\": 123,\n \"locale\": \"en-US\",\n \"market\": 123,\n \"facets\": [\n {\n \"key\": \"<string>\",\n \"value\": [\n \"<string>\"\n ],\n \"operator\": \"or\"\n }\n ],\n \"view\": \"product_details\",\n \"pagination\": {\n \"page\": 1,\n \"limit\": 20\n },\n \"options\": {\n \"include_aggregations\": false,\n \"include_highlights\": false,\n \"include_facets\": false,\n \"boost_in_stock\": true,\n \"personalize\": false\n },\n \"visitor_id\": \"<string>\",\n \"client_id\": \"<string>\",\n \"customer_id\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [
{
"product_id": 9311227838783,
"title": "Running Shoe",
"handle": "running-shoe",
"product_type": "Shoes",
"vendor": "Acme",
"tags": ["sale", "summer"],
"price": 49.99,
"compare_at_price": 69.99,
"display_price": 49.99,
"display_compare_at_price": 69.99,
"display_currency": "USD",
"original_currency": "USD",
"currency_rate": 1,
"available_for_sale": true,
"is_in_stock": true,
"total_inventory": 42,
"image": { "id": "gid://shopify/ProductImage/1", "url": "https://cdn.shopify.com/s/files/1/shoe.jpg", "width": 800, "height": 800, "alt_text": "Running Shoe" },
"featured_media": null,
"media": [],
"options": [ { "id": 111, "name": "Size", "position": 1, "values": ["S", "M", "L"] } ],
"variants": [
{
"variant_id": 456,
"title": "M",
"display_name": "Running Shoe - M",
"sku": "RS-M",
"position": 1,
"price": 49.99,
"compare_at_price": 69.99,
"available_for_sale": true,
"is_in_stock": true,
"sellable_online_quantity": 10,
"selected_options": [ { "name": "Size", "value": "M" } ],
"image": null
}
],
"metafields": [ { "namespace": "custom", "key": "material", "value": "leather" } ]
}
],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [
{
"key": "color",
"type": "terms",
"aggregation": { "buckets": [ { "value": "red", "count": 42, "selected": true } ] }
}
],
"metadata": { "query_time": 40, "total_time": 55, "region": "toronto", "search_type": "hybrid" }
}
{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [ { "product_id": 9311227838783 }, { "product_id": 9311227838784 } ],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [],
"metadata": { "query_time": 38, "total_time": 41, "region": "toronto", "search_type": "hybrid" }
}
{
"error": [
{
"message": "\"sort\" must be one of [relevance, title_asc, title_desc, price_asc, price_desc, best_selling, oldest, newest, collection_default]",
"path": ["sort"],
"type": "any.only",
"context": { "label": "sort", "key": "sort" }
}
],
"ok": false
}
{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [
{
"product_id": 9311227838783,
"title": "Running Shoe",
"handle": "running-shoe",
"product_type": "Shoes",
"vendor": "Acme",
"tags": ["sale", "summer"],
"price": 49.99,
"compare_at_price": 69.99,
"display_price": 49.99,
"display_compare_at_price": 69.99,
"display_currency": "USD",
"original_currency": "USD",
"currency_rate": 1,
"available_for_sale": true,
"is_in_stock": true,
"total_inventory": 42,
"image": { "id": "gid://shopify/ProductImage/1", "url": "https://cdn.shopify.com/s/files/1/shoe.jpg", "width": 800, "height": 800, "alt_text": "Running Shoe" },
"featured_media": null,
"media": [],
"options": [ { "id": 111, "name": "Size", "position": 1, "values": ["S", "M", "L"] } ],
"variants": [
{
"variant_id": 456,
"title": "M",
"display_name": "Running Shoe - M",
"sku": "RS-M",
"position": 1,
"price": 49.99,
"compare_at_price": 69.99,
"available_for_sale": true,
"is_in_stock": true,
"sellable_online_quantity": 10,
"selected_options": [ { "name": "Size", "value": "M" } ],
"image": null
}
],
"metafields": [ { "namespace": "custom", "key": "material", "value": "leather" } ]
}
],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [
{
"key": "color",
"type": "terms",
"aggregation": { "buckets": [ { "value": "red", "count": 42, "selected": true } ] }
}
],
"metadata": { "query_time": 40, "total_time": 55, "region": "toronto", "search_type": "hybrid" }
}
{
"status": "ok",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"products": [ { "product_id": 9311227838783 }, { "product_id": 9311227838784 } ],
"pagination": {
"total": 240, "page": 1, "limit": 20, "total_pages": 12,
"has_next_page": true, "has_prev_page": false, "next_page": 2, "prev_page": null
},
"facets": [],
"metadata": { "query_time": 38, "total_time": 41, "region": "toronto", "search_type": "hybrid" }
}
{
"error": [
{
"message": "\"sort\" must be one of [relevance, title_asc, title_desc, price_asc, price_desc, best_selling, oldest, newest, collection_default]",
"path": ["sort"],
"type": "any.only",
"context": { "label": "sort", "key": "sort" }
}
],
"ok": false
}
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
Shopify store URL. Resolves the shop for the request (headless does not accept ?shop=).
"shop.myshopify.com"
Body
Request body
Full product search (hybrid text + vector) with applied facets, sort, pagination and optional inline aggregations.
Search term. May be empty.
"running shoe"
Search context.
search, collection Target object id (e.g. collection id) when scoping to a collection.
BCP-47 locale.
Shopify market id.
Applied filters.
Show child attributes
Show child attributes
Result ordering. Omit it to use the resolved default: the sort configured for this collection if the merchant set one, otherwise the shop-wide default, otherwise relevance — the same value the config endpoint returns as default_ordering. collection_default reproduces the product order the merchant arranged on the collection in Shopify; it needs target_page: "collection" with a target_id and falls back to relevance without one. Collection positions are refreshed by the collection sync, so a product added or reordered since the last sync is ordered last until the next one.
relevance, title_asc, title_desc, price_asc, price_desc, best_selling, oldest, newest, collection_default, name_asc, name_desc, bestselling, created_asc, created_desc Product render mode.
product_details, product_ids Page-based pagination.
Show child attributes
Show child attributes
Search options.
Show child attributes
Show child attributes
Anonymous device/browser UID (the gl_s_uid cookie). The primary personalization key; stays stable when a customer logs in.
Per-session / per-device identifier.
Shopify customer id. Present only when the shopper is logged in; enables customer-level personalization.
Response
Search results
Product search results with pagination, optional inline facets and metadata.
Response status.
"ok"
Unique id for this search serve (use for attribution events).
Product objects (or { product_id } when view=product_ids).
Show child attributes
Show child attributes
Page-based pagination.
Show child attributes
Show child attributes
Inline facet aggregations. Present only when include_aggregations/include_facets was requested.
Show child attributes
Show child attributes
Response metadata.
Show child attributes
Show child attributes
Was this page helpful?