Products API
The Products API enables semantic search and retrieval of products from the catalog.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /acp/products | Search products with filters |
GET | /acp/products/search | Search products (alias) |
GET | /acp/products/{product_id} | Get single product |
Search Products
Search for products using natural language queries and filters.
GET /acp/products
GET /acp/products/searchParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | No | Natural language search query (uses semantic search) |
category | string | No | Filter by category (case-insensitive partial match) |
tag | string | No | Filter by tag |
min_price | number | No | Minimum price (inclusive) |
max_price | number | No | Maximum price (inclusive) |
page | integer | No | Page number (default: 1) |
page_size | integer | No | Results per page (default: 10, max: 100) |
Response
{
"items": [
{
"id": "string",
"title": "string",
"description": "string",
"vendor": "string",
"category": "string",
"price": 0.00,
"currency": "USD",
"availability": {
"status": "in_stock",
"quantity": 0
},
"tags": ["string"],
"attributes": {
"Material": ["Cotton", "Polyester"]
},
"images": [
{
"url": "string",
"alt_text": "string",
"position": 0
}
],
"variants": [
{
"sku": "string",
"title": "string",
"price": 0.00,
"currency": "USD",
"attributes": {
"Size": "M",
"Color": "Black"
},
"inventory": {
"status": "in_stock",
"quantity": 0
}
}
],
"similarity_score": 0.00
}
],
"page": 1,
"page_size": 10,
"total": 0,
"version": "string",
"generated_at": "2024-01-15T10:30:00Z"
}Examples
cURL
# Basic semantic search
curl "https://{{YOUR_STORE_URL}}/acp/products?q=comfortable+running+shoes" \
-H "Accept: application/json" \
-H "X-ACP-API-Key: {{YOUR_API_KEY}}"
# With price filter
curl "https://{{YOUR_STORE_URL}}/acp/products?q=wireless+headphones&min_price=50&max_price=200" \
-H "Accept: application/json" \
-H "X-ACP-API-Key: {{YOUR_API_KEY}}"
# Category filter
curl "https://{{YOUR_STORE_URL}}/acp/products?category=Electronics&page_size=20" \
-H "Accept: application/json" \
-H "X-ACP-API-Key: {{YOUR_API_KEY}}"Python
import requests
BASE_URL = "https://{{YOUR_STORE_URL}}"
HEADERS = {
"Accept": "application/json",
"X-ACP-API-Key": "{{YOUR_API_KEY}}"
}
# Semantic search
response = requests.get(
f"{BASE_URL}/acp/products",
params={
"q": "comfortable running shoes",
"max_price": 150
},
headers=HEADERS
)
products = response.json()
for product in products["items"]:
print(f"{product['title']} - ${product['price']}")
print(f" Relevance: {product.get('similarity_score', 'N/A')}")TypeScript
const BASE_URL = "https://{{YOUR_STORE_URL}}";
const API_KEY = "{{YOUR_API_KEY}}";
interface Product {
id: string;
title: string;
price: number;
similarity_score?: number;
}
interface SearchResponse {
items: Product[];
page: number;
page_size: number;
total: number;
}
async function searchProducts(query: string, maxPrice?: number): Promise<SearchResponse> {
const params = new URLSearchParams({ q: query });
if (maxPrice) params.append("max_price", maxPrice.toString());
const response = await fetch(`${BASE_URL}/acp/products?${params}`, {
headers: {
"Accept": "application/json",
"X-ACP-API-Key": API_KEY
}
});
return response.json();
}
// Usage
const results = await searchProducts("wireless headphones", 200);
console.log(`Found ${results.total} products`);Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class LornClient {
private static final String BASE_URL = "https://{{YOUR_STORE_URL}}";
private static final String API_KEY = "{{YOUR_API_KEY}}";
private final HttpClient client = HttpClient.newHttpClient();
public String searchProducts(String query, Double maxPrice) throws Exception {
StringBuilder url = new StringBuilder(BASE_URL + "/acp/products?");
url.append("q=").append(URLEncoder.encode(query, StandardCharsets.UTF_8));
if (maxPrice != null) {
url.append("&max_price=").append(maxPrice);
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url.toString()))
.header("Accept", "application/json")
.header("X-ACP-API-Key", API_KEY)
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return response.body();
}
}
// Usage
LornClient client = new LornClient();
String results = client.searchProducts("running shoes", 150.0);
System.out.println(results);Go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
const (
baseURL = "https://{{YOUR_STORE_URL}}"
apiKey = "{{YOUR_API_KEY}}"
)
type Product struct {
ID string `json:"id"`
Title string `json:"title"`
Price float64 `json:"price"`
SimilarityScore *float64 `json:"similarity_score,omitempty"`
}
type SearchResponse struct {
Items []Product `json:"items"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int `json:"total"`
}
func searchProducts(query string, maxPrice *float64) (*SearchResponse, error) {
params := url.Values{}
params.Add("q", query)
if maxPrice != nil {
params.Add("max_price", fmt.Sprintf("%.2f", *maxPrice))
}
req, err := http.NewRequest("GET", baseURL+"/acp/products?"+params.Encode(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("X-ACP-API-Key", apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result SearchResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
maxPrice := 150.0
results, err := searchProducts("comfortable running shoes", &maxPrice)
if err != nil {
panic(err)
}
fmt.Printf("Found %d products\n", results.Total)
for _, p := range results.Items {
fmt.Printf("- %s: $%.2f\n", p.Title, p.Price)
}
}Get Product
Retrieve a single product by ID.
GET /acp/products/{product_id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | string | Yes | The product ID (lorn_id) |
Response
Returns a single Product object with all details.
{
"id": "prod_abc123",
"title": "Nike Air Zoom Pegasus 40",
"description": "Responsive cushioning meets a breathable upper...",
"vendor": "Nike",
"category": "Footwear > Running",
"price": 129.99,
"currency": "USD",
"availability": {
"status": "in_stock",
"quantity": 42
},
"tags": ["running", "athletic", "cushioned"],
"attributes": {
"Upper Material": ["Mesh"],
"Cushioning": ["Zoom Air"]
},
"images": [
{
"url": "https://cdn.example.com/nike-pegasus-40-main.jpg",
"alt_text": "Nike Air Zoom Pegasus 40 - Side View",
"position": 1
}
],
"variants": [
{
"sku": "nike-pegasus-40-black-10",
"title": "Black / Size 10",
"price": 129.99,
"currency": "USD",
"requires_shipping": true,
"taxable": true,
"weight_grams": 280,
"weight_unit": "g",
"attributes": {
"Color": "Black",
"Size": "10"
},
"inventory": {
"status": "in_stock",
"quantity": 8
}
},
{
"sku": "nike-pegasus-40-white-10",
"title": "White / Size 10",
"price": 129.99,
"currency": "USD",
"attributes": {
"Color": "White",
"Size": "10"
},
"inventory": {
"status": "in_stock",
"quantity": 5
}
}
]
}Examples
cURL
curl "https://{{YOUR_STORE_URL}}/acp/products/{{PRODUCT_ID}}" \
-H "Accept: application/json" \
-H "X-ACP-API-Key: {{YOUR_API_KEY}}"Python
import requests
def get_product(product_id: str) -> dict:
response = requests.get(
f"{BASE_URL}/acp/products/{product_id}",
headers=HEADERS
)
response.raise_for_status()
return response.json()
# Usage
product = get_product("prod_abc123")
print(f"Product: {product['title']}")
print(f"Price: ${product['price']}")
print(f"Variants: {len(product['variants'])}")TypeScript
async function getProduct(productId: string): Promise<Product> {
const response = await fetch(`${BASE_URL}/acp/products/${productId}`, {
headers: {
"Accept": "application/json",
"X-ACP-API-Key": API_KEY
}
});
if (!response.ok) {
throw new Error(`Product not found: ${productId}`);
}
return response.json();
}
// Usage
const product = await getProduct("prod_abc123");
console.log(`${product.title} - $${product.price}`);Java
public String getProduct(String productId) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/acp/products/" + productId))
.header("Accept", "application/json")
.header("X-ACP-API-Key", API_KEY)
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 404) {
throw new RuntimeException("Product not found: " + productId);
}
return response.body();
}Go
func getProduct(productID string) (*Product, error) {
req, err := http.NewRequest("GET", baseURL+"/acp/products/"+productID, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("X-ACP-API-Key", apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == 404 {
return nil, fmt.Errorf("product not found: %s", productID)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var product Product
if err := json.Unmarshal(body, &product); err != nil {
return nil, err
}
return &product, nil
}Object Reference
Product
| Field | Type | Description |
|---|---|---|
id | string | Unique product identifier |
title | string | Product name |
description | string | Full product description |
vendor | string | Brand/manufacturer |
category | string | Product category |
price | number | Base price |
currency | string | Currency code (ISO 4217) |
availability | Inventory | Stock status |
tags | string[] | Product tags |
attributes | object | Product attributes (key: values[]) |
images | Image[] | Product images |
variants | Variant[] | Product variants |
similarity_score | number | Semantic search relevance (0-1) |
Variant
| Field | Type | Description |
|---|---|---|
sku | string | Unique variant SKU |
title | string | Variant title |
price | number | Variant price |
currency | string | Currency code |
requires_shipping | boolean | Needs shipping |
taxable | boolean | Is taxable |
weight_grams | number | Weight in grams |
weight_unit | string | Weight unit |
attributes | object | Variant attributes (key: value) |
inventory | Inventory | Variant stock |
Inventory
| Field | Type | Description |
|---|---|---|
status | string | in_stock, limited, out_of_stock |
quantity | integer | Stock count |
policy | string | Inventory policy |
Image
| Field | Type | Description |
|---|---|---|
url | string | Image URL |
alt_text | string | Alt text |
position | integer | Display order |
Errors
| Status | Error | Description |
|---|---|---|
| 400 | invalid_request | Invalid query parameters |
| 404 | not_found | Product not found |
| 503 | service_unavailable | Supabase not configured |
Example Error
{
"detail": "Product not found"
}