Search the storefront catalogue
Returns ranked hits across the company’s public products, enrollment packs, pages, media, playlists and posts: only what the public storefront lists. Facets count the matching results of each requested type. Available to every membership, limited to 30 searches per minute per credential. Responses are never stored.
curl --request GET \
--url https://api.fluid.app/api/member/v2026-10/search \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fluid.app/api/member/v2026-10/search"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fluid.app/api/member/v2026-10/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://api.fluid.app/api/member/v2026-10/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://api.fluid.app/api/member/v2026-10/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.fluid.app/api/member/v2026-10/search")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/member/v2026-10/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": 200,
"data": [
{
"kind": "products",
"id": "0199a3f2-5c1e-7d4b-9a10-3b2c1d4e5f61",
"title": "Primex Wellness Kit",
"image_url": "https://ik.imagekit.io/fluid/primex/wellness-kit.jpg",
"url": "https://primex.fluid.app/home/products/primex-wellness-kit",
"share_url": "https://primex.fluid.app/riley-kim/products/primex-wellness-kit",
"snippet": "Everything you need for a 30-day wellness reset.",
"score": 12.4
}
],
"meta": {
"request_uuid": "0199a3f2-7a10-7c3e-8f21-4d5e6f708192",
"timestamp": "2026-09-30T18:24:05Z",
"facets": {
"products": 1,
"media": 0
},
"pagination": {
"next_cursor": null
}
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": "Rate limit exceeded",
"message": "Too many requests. Please wait before trying again.",
"retry_after": 42
}{
"status": 503,
"error": {
"message": "Search is unavailable; retry shortly",
"details": {
"error": "unavailable"
}
},
"meta": {
"request_uuid": "0199a3f2-7a10-7c3e-8f21-4d5e6f708193",
"timestamp": "2026-09-30T18:24:06Z"
}
}Authorizations
A member credential in the Authorization: Bearer header: a member
session JWT, a portal JWT whose login is linked to a membership in
that company, or an opaque member token. The credential alone
identifies the member and company.
Query Parameters
Search text, 2 to 200 characters.
2 - 200Comma-separated types to search: products, enrollment-packs, pages, media, playlists or posts. All six by default.
200"products,media"
Opaque cursor from meta.pagination.next_cursor. Expires after one hour and only continues the same query and types. A catalogue reindex between pages invalidates it with a 400; repeat the search without a cursor.
Maximum number of hits returned.
1 <= x <= 50curl --request GET \
--url https://api.fluid.app/api/member/v2026-10/search \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.fluid.app/api/member/v2026-10/search"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.fluid.app/api/member/v2026-10/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://api.fluid.app/api/member/v2026-10/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://api.fluid.app/api/member/v2026-10/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.fluid.app/api/member/v2026-10/search")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.fluid.app/api/member/v2026-10/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": 200,
"data": [
{
"kind": "products",
"id": "0199a3f2-5c1e-7d4b-9a10-3b2c1d4e5f61",
"title": "Primex Wellness Kit",
"image_url": "https://ik.imagekit.io/fluid/primex/wellness-kit.jpg",
"url": "https://primex.fluid.app/home/products/primex-wellness-kit",
"share_url": "https://primex.fluid.app/riley-kim/products/primex-wellness-kit",
"snippet": "Everything you need for a 30-day wellness reset.",
"score": 12.4
}
],
"meta": {
"request_uuid": "0199a3f2-7a10-7c3e-8f21-4d5e6f708192",
"timestamp": "2026-09-30T18:24:05Z",
"facets": {
"products": 1,
"media": 0
},
"pagination": {
"next_cursor": null
}
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"status": 123,
"error": {
"message": "<string>",
"details": {}
},
"meta": {
"request_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"error": "Rate limit exceeded",
"message": "Too many requests. Please wait before trying again.",
"retry_after": 42
}{
"status": 503,
"error": {
"message": "Search is unavailable; retry shortly",
"details": {
"error": "unavailable"
}
},
"meta": {
"request_uuid": "0199a3f2-7a10-7c3e-8f21-4d5e6f708193",
"timestamp": "2026-09-30T18:24:06Z"
}
}