Get product by ID or variant ID
Retrieve complete details for a single product using either its product ID or variant ID.
Response Structure:
- Simple products (no variants): Flat structure with all data at root level
- Multi-variant products: Hierarchical structure with:
- Product-level data at root (name, description, brand, media, etc.)
- Aggregate data (total_stock, price_range using selling_price)
- Clean variants array with only variant-specific fields (no redundancy)
Field Filtering: Supports both root-level and nested variant filtering:
- Root filtering: ~ 38% bandwidth reduction
- Nested filtering: ~ 52% bandwidth reduction
- Example:
fields=name,price_range,variants.sku,variants.selling_price,variants.stock
Currency: Prices returned in store’s default currency. For multi-currency support with
geographic detection, use the /v1/prices/products/{id} endpoint instead.
curl --request GET \
--url https://api.tybritelabs.com/v1/products/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.tybritelabs.com/v1/products/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.tybritelabs.com/v1/products/{id}', 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.tybritelabs.com/v1/products/{id}",
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.tybritelabs.com/v1/products/{id}"
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.tybritelabs.com/v1/products/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/products/{id}")
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{
"product_id": "715b8c10-eb11-4289-8c0e-2966e78cf9a5",
"name": "Apple MacBook Pro",
"category_id": "49f084e5-9ded-429c-aeb7-5918a2234953",
"category_name": "Laptops",
"brand": "Apple",
"thumbnail_url": "https://images.unsplash.com/photo-1517336714731-489689fd1ca8",
"media": [],
"variant_id": "15ca5a86-4b1b-47c3-8762-a1aa43941565",
"sku": "APP-MBP-13",
"price": 1999,
"selling_price": 1999,
"stock": 42,
"has_variants": false,
"display_currency": "USD",
"currency_symbol": "$",
"is_active": true,
"created_at": "2026-06-20T10:12:03.673701+00:00",
"updated_at": "2026-06-20T10:12:03.673701+00:00"
}Authorizations
API Key Authentication
Use your API key in the Authorization header:
Authorization: Bearer tybrite_sk_live_YOUR_KEY
Key Types:
Secret Keys (Server-Side Only):
- Format:
tybrite_sk_live_*(production) ortybrite_sk_test_*(sandbox) - Full read/write access to all endpoints
- ⚠️ NEVER expose in client-side code or public repositories
- Required for: every write a shopper does not make for themselves, authentication, payment verification, AI recommendations
Publishable Keys (Client-Safe):
- Format:
tybrite_pk_live_*(production) ortybrite_pk_test_*(sandbox) - Catalogue reads, plus the actions a shopper takes for themselves
- ✅ Safe for client-side JavaScript, mobile apps, and public code
- Allowed for: browsing products, search, CMS content, pricing queries, cart and wishlist, reviews, storefront events, and the Agent API's quotes, cart drafts and checkout intents
Endpoint-Specific Requirements:
- Authentication endpoints (
/v1/auth/*): Secret key required - Payment verification (
POST /v1/payments/verify): Secret key required - AI Recommendations (
POST /v1/recommendations): Secret key required - Semantic Search (
POST /v1/search): Both key types allowed (read-only operation) - Shopper actions (cart, wishlist, reviews, storefront events, Agent API quotes, cart drafts and checkout intents): Both key types allowed
- All other write operations: Secret key required
- All read operations: Both key types allowed
Using a publishable key for restricted operations returns 403 Forbidden.
Path Parameters
Product ID or Variant ID. This endpoint accepts both types of IDs, providing flexible product retrieval.
Accepted ID Types:
- Product ID: The main product identifier (e.g.,
715b8c10-eb11-4289-8c0e-2966e78cf9a5) - Variant ID: A specific variant identifier (e.g.,
15ca5a86-4b1b-47c3-8762-a1aa43941565)
Why This Matters:
- Simplifies API usage - no need to know if you have a product or variant ID
- Useful for barcode scanning where you might have variant IDs
- Consistent behavior regardless of ID type
Validation:
- Must be a valid UUID format
- Must exist in your store's product catalog
- Returns 404 if product/variant not found
SDK Usage:
Query Parameters
Comma-separated list of fields to include in the response. Supports both root-level and nested variant filtering for maximum bandwidth optimization.
Root-Level Fields:
- Core:
product_id,variant_id,name,sku,description - Pricing:
price,selling_price,sale_price,price_range,display_currency,currency_symbol - Inventory:
stock,total_stock,last_restocked(last_restockedis not in the default response — request it explicitly) - Media:
media,thumbnail_url - Metadata:
brand,category_name,subcategory_name,attributes.* - Flags:
has_variants,variant_count,is_default - Variants:
variants(includes all variant fields)
Nested Variant Filtering (52% bandwidth reduction): Use dot notation to filter specific fields within the variants array:
variants.variant_id- Variant identifiervariants.sku- Variant SKUvariants.selling_price- Customer-facing pricevariants.stock- Stock quantityvariants.variant_attributes- Variant attributes (color, size, etc.). Where a colour is set,coloris its name andcolor_hexthe matching#RRGGBBvalue, so a storefront can render a swatch rather than infer one from the name. Either may be absent.variants.variant_name- Variant display namevariants.is_default- Default variant flagvariants.*- All variant fields (same as justvariants)
Bandwidth Savings:
- Full product: ~1720 bytes (baseline)
- Root filtering:
1060 bytes (38% reduction) - Nested filtering:
830 bytes (52% reduction)
Examples:
Root-level filtering (~ 38% reduction):
fields=product_id,name,brand,total_stock,price_range,variants
Nested variant filtering (~ 52% reduction):
fields=product_id,name,brand,total_stock,price_range,
variants.variant_id,variants.sku,variants.selling_price,
variants.stock,variants.variant_attributes
Minimal cart validation:
fields=product_id,total_stock,variants.variant_id,variants.stock
SDK Usage:
Performance Impact:
- Nested filtering excludes non-default fields like last_restocked
- Perfect for mobile apps and bandwidth-constrained environments
- Recommended for product cards and listing pages
Response
Successfully retrieved product with complete details.
Response Structure:
For Simple Products (has_variants=false): Returns flat structure with all data at root level:
- All product fields (name, description, brand, media, etc.)
- All variant fields (variant_id, sku, price, stock, etc.)
- No variants array
For Multi-Variant Products (has_variants=true): Returns hierarchical structure:
- Product-level fields at root (name, description, brand, media, etc.)
- Aggregate data: total_stock, price_range (using selling_price)
- Flags: has_variants=true, variant_count
- Clean variants array with only variant-specific fields:
- variant_id, sku, price, sale_price, selling_price
- stock (last_restocked available via fields=variants.last_restocked)
- variant_attributes (color + color_hex, size, etc.)
- variant_name, is_default
Field Filtering:
- Root-level: Exclude unnecessary fields (~ 38% reduction)
- Nested: Filter variant fields using dot notation (~ 52% reduction)
- Example:
fields=name,price_range,variants.sku,variants.selling_price,variants.stock
SDK Usage:
Represents a product in the catalog. Response structure varies based on endpoint and variant configuration:
List Endpoint (GET /v1/products):
- Returns flat structure with default variant data only
- No variants array (keeps payload small for browsing)
- Includes has_variants flag to indicate if detail fetch needed
Detail Endpoints (GET /v1/products/:id, GET /v1/products/by-slug/:slug):
- Multi-variant products: Hierarchical structure with product-level data at root + variants array
- Simple products: Flat structure with all data at root (no variants array)
Field Filtering:
- Root-level filtering: Reduce top-level fields
- Nested filtering: Filter specific variant fields using dot notation
- Example: fields=name,price_range,variants.sku,variants.selling_price,variants.stock
Channel and logistics fields:
condition, gtin, mpn, weight_grams and dimensions_cm describe an
individual item for sales channels and shipping, so they belong to the
variant: two sizes of one shirt carry different codes and weights.
google_product_category, meta_product_category and tags classify the
listing as a whole and belong to the product. All of them are returned on
the list endpoint and on detail, and any of them can be named in fields=.
Product identifier. Identical to product_id.
"d8cea277-9bb6-4942-b9e9-2f2ac351509f"
Product identifier. Identical to id.
"d8cea277-9bb6-4942-b9e9-2f2ac351509f"
Product name
"Sony WH-1000XM4"
Mean rating of the product's approved reviews, from 1 to 5, to one decimal place. null when the product has no approved reviews. Present on product listings so a grid can show a rating under each card without a review request per product.
4.6
Number of approved reviews for the product. 0 when there are none.
121
Product description
"Wireless noise-cancelling headphones"
Category UUID
Category display name
"Electronics"
Subcategory UUID
Subcategory display name
"Headphones"
Product brand
"Sony"
Primary image URL for list views
"https://cdn.tybritelabs.com/stores/.../primary-123.jpg"
Array of product media objects including images and videos
Show child attributes
Show child attributes
SEO-friendly URL slug
"sony-wh-1000xm4-abc123"
SEO title for the product detail page
"Sony WH-1000XM4 Wireless Noise-Cancelling Headphones"
SEO meta description for the product detail page
"Industry-leading noise cancellation with 30-hour battery life. Shop the Sony WH-1000XM4 today."
SEO keywords for the product
[
"wireless headphones",
"noise cancelling",
"sony"
]
Whether product is featured
Display order for featured products
Product tags
The product's category in the Google Shopping taxonomy, as the merchant assigned it. Sales channels use this to file the listing; it is independent of category_name, which is the store's own taxonomy.
"Apparel & Accessories > Clothing > Outerwear"
The product's category in the Meta commerce taxonomy. The Meta counterpart of google_product_category, set separately because the two taxonomies do not share category names.
"apparel & accessories > clothing"
Product-level attributes (not variant-specific)
Shipping dimensions and weight
Show child attributes
Show child attributes
Product creation timestamp
Last update timestamp
Whether product is active
Variant identifier (only present for simple products or list endpoint)
"cc35d16b-fca5-4faa-8699-d3d5d3521bca"
Stock Keeping Unit (only present for simple products or list endpoint)
"SNY-WH1000"
Base price in major currency units (369.99 = $369.99), not minor units. Only present for simple products or the list endpoint.
369.99
Sale price in major currency units (369.99 = $369.99) if on sale
Actual customer-facing price (considers sale_price)
349.99
Available stock quantity (only present for simple products or list endpoint)
40
Last restock date. Not included in the default list response — request it explicitly with fields=last_restocked.
Variant-specific attributes (e.g., color, size)
{ "color": "Black" }
Variant display name
Whether this is the default variant
The condition the item is sold in. Sales channels require this on a listing and reject a value outside the three. Defaults to new.
new, refurbished, used "new"
The manufacturer's Global Trade Item Number (UPC, EAN, or ISBN). Sales channels match a listing against this to identify the product. It is not the same as the barcode the merchant scans in store, which may be an internal label and is never returned by this API.
"0885909950805"
Manufacturer part number. Identifies the item for products that carry no gtin, which is common for made-to-order and own-brand goods.
"WH1000XM4/B"
Shipped weight of the item in grams.
254
Packed dimensions in centimetres as free text, in the merchant's own format. Parse defensively rather than assuming a fixed shape.
"20x15x5"
Sum of stock across all variants (only present for multi-variant products)
85
Price range across variants using selling_price (only present for multi-variant products)
Show child attributes
Show child attributes
Whether product has multiple variants
true
Number of variants (only present for multi-variant products)
3
Store's default currency code
"EUR"
Currency symbol
"€"
Array of product variants (only present for multi-variant products in detail endpoints)
Show child attributes
Show child attributes
curl --request GET \
--url https://api.tybritelabs.com/v1/products/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.tybritelabs.com/v1/products/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.tybritelabs.com/v1/products/{id}', 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.tybritelabs.com/v1/products/{id}",
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.tybritelabs.com/v1/products/{id}"
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.tybritelabs.com/v1/products/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tybritelabs.com/v1/products/{id}")
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{
"product_id": "715b8c10-eb11-4289-8c0e-2966e78cf9a5",
"name": "Apple MacBook Pro",
"category_id": "49f084e5-9ded-429c-aeb7-5918a2234953",
"category_name": "Laptops",
"brand": "Apple",
"thumbnail_url": "https://images.unsplash.com/photo-1517336714731-489689fd1ca8",
"media": [],
"variant_id": "15ca5a86-4b1b-47c3-8762-a1aa43941565",
"sku": "APP-MBP-13",
"price": 1999,
"selling_price": 1999,
"stock": 42,
"has_variants": false,
"display_currency": "USD",
"currency_symbol": "$",
"is_active": true,
"created_at": "2026-06-20T10:12:03.673701+00:00",
"updated_at": "2026-06-20T10:12:03.673701+00:00"
}
