Product API Guide
Product API Usage Guide
Overview
The Product API v1 provides a comprehensive RESTful interface for accessing product information. All endpoints are public and do not require authentication for viewing products.
Base URL: http://localhost:3000/api/v1 (development)
API Version: v1
Content Type: application/json
Table of Contents
- Getting Started
- Endpoints
- Filtering & Sorting
- Pagination
- Code Examples
- Error Handling
- Rate Limiting
- Best Practices
Getting Started
Base URL
Development: http://localhost:3000/api/v1
Production: https://www.example.com/api/v1
Response Format
All responses follow a consistent format:
Success Response:
{
"status": "success",
"data": {
// Response data here
}
}
Error Response:
{
"status": "error",
"message": "Error description",
"error_code": "ERROR_CODE",
"errors": ["Detailed error 1", "Detailed error 2"] // Optional, for validation errors
}
Endpoints
1. List Products
GET /api/v1/products
Returns a paginated list of published products with filtering, sorting, and pagination support.
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number (minimum: 1) |
per_page |
integer | No | 20 | Items per page (1-100, max: 100) |
search |
string | No | - | Search query (searches in name, description, SKU) |
category_id |
integer | No | - | Filter by category ID |
min_price |
float | No | - | Minimum price filter (inclusive) |
max_price |
float | No | - | Maximum price filter (inclusive) |
inventory_status |
string | No | - | Filter by inventory: in_stock, low_stock, out_of_stock |
featured |
boolean | No | - | Filter featured products (true/false) |
min_rating |
float | No | - | Minimum rating filter (0.0 to 5.0) |
order_by |
string | No | updated_at |
Sort field: price, name, created_at, rating, sales, updated_at |
direction |
string | No | desc |
Sort direction: asc or desc |
Example Request:
GET /api/v1/products?page=1&per_page=20&min_price=100&max_price=500&featured=true&order_by=price&direction=asc
Example Response:
{
"status": "success",
"data": {
"products": [
{
"id": 123,
"name": "Wireless Bluetooth Headphones",
"slug": "wireless-bluetooth-headphones",
"price": 199.99,
"compare_at_price": 249.99,
"on_sale": true,
"discount_percentage": 20.0,
"in_stock": true,
"featured": false,
"category": {
"id": 5,
"name": "Electronics",
"slug": "electronics"
},
"tags": [
{
"id": 1,
"name": "wireless",
"slug": "wireless"
}
],
"primary_image_url": "https://example.com/images/product-123.jpg",
"wishlist_count": 25,
"views_count": 150,
"sales_count": 75,
"rating_average": 4.5,
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}
],
"pagination": {
"current_page": 1,
"per_page": 20,
"total_pages": 5,
"total_count": 100,
"has_next_page": true,
"has_prev_page": false
}
}
}
2. Get Product Details
GET /api/v1/products/{id}
Returns detailed information about a specific product by ID.
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Product ID |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
include_related |
boolean | No | false |
Include related products (up to 4) |
include_reviews |
boolean | No | false |
Include product reviews (up to 10 most recent) |
include_variants |
boolean | No | false |
Include product variants |
Example Request:
GET /api/v1/products/123?include_related=true&include_reviews=true&include_variants=true
Example Response:
{
"status": "success",
"data": {
"product": {
"id": 123,
"name": "Wireless Bluetooth Headphones",
"slug": "wireless-bluetooth-headphones",
"description": "High-quality wireless headphones with noise cancellation...",
"price": 199.99,
"on_sale": true,
"in_stock": true,
"category": {
"id": 5,
"name": "Electronics"
},
"related_products": [
{
"id": 124,
"name": "Wired Headphones",
"price": 99.99
}
],
"reviews": [
{
"id": 1,
"rating": 5,
"title": "Great product!",
"content": "Really happy with this purchase.",
"author_name": "John Doe",
"created_at": "2025-01-10T12:00:00Z"
}
],
"reviews_count": 15,
"variants": [
{
"id": 1,
"name": "Large",
"price": 219.99,
"in_stock": true
}
],
"has_variants": true
}
}
}
3. Search Products
GET /api/v1/products/search
Search products by query string. Searches in product name, description, and SKU.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | Search query (max 255 characters) |
page |
integer | No | Page number |
per_page |
integer | No | Items per page (1-100) |
| All other filters from index endpoint | - | No | Same as index endpoint |
Example Request:
GET /api/v1/products/search?q=wireless&min_price=100&max_price=500&per_page=10
Example Response:
{
"status": "success",
"data": {
"products": [...],
"pagination": {...},
"query": "wireless",
"total_results": 15
}
}
4. List Categories
GET /api/v1/products/categories
Returns a list of all active product categories.
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
include_product_count |
boolean | No | false |
Include product count per category |
only_with_products |
boolean | No | false |
Only return categories that have products |
Example Request:
GET /api/v1/products/categories?include_product_count=true&only_with_products=true
Example Response:
{
"status": "success",
"data": {
"categories": [
{
"id": 5,
"name": "Electronics",
"slug": "electronics",
"description": "Electronic devices and accessories",
"product_count": 25
}
],
"total": 10
}
}
Filtering & Sorting
Filtering Options
By Price Range:
GET /api/v1/products?min_price=100&max_price=500
By Category:
GET /api/v1/products?category_id=5
By Inventory Status:
GET /api/v1/products?inventory_status=in_stock
By Featured Status:
GET /api/v1/products?featured=true
By Rating:
GET /api/v1/products?min_rating=4.0
Combined Filters:
GET /api/v1/products?category_id=5&min_price=100&max_price=500&featured=true&inventory_status=in_stock&min_rating=4.0
Sorting Options
Sort by Price (Ascending):
GET /api/v1/products?order_by=price&direction=asc
Sort by Name (Descending):
GET /api/v1/products?order_by=name&direction=desc
Sort by Rating:
GET /api/v1/products?order_by=rating&direction=desc
Sort by Sales:
GET /api/v1/products?order_by=sales&direction=desc
Available Sort Fields:
price- Product pricename- Product namecreated_at- Creation daterating- Average ratingsales- Sales countupdated_at- Last update (default)
Pagination
All list endpoints support pagination:
Basic Pagination:
GET /api/v1/products?page=1&per_page=20
Navigate Pages:
GET /api/v1/products?page=2&per_page=20
Pagination Metadata:
{
"pagination": {
"current_page": 1,
"per_page": 20,
"total_pages": 5,
"total_count": 100,
"has_next_page": true,
"has_prev_page": false
}
}
Best Practices:
- Use
has_next_pageandhas_prev_pageto determine navigation - Maximum
per_pageis 100 - Default
per_pageis 20
Code Examples
JavaScript (Fetch API)
// List products with filters
async function getProducts(filters = {}) {
const params = new URLSearchParams({
page: filters.page || 1,
per_page: filters.perPage || 20,
...filters
});
const response = await fetch(`/api/v1/products?${params}`);
const data = await response.json();
if (data.status === 'success') {
return data.data;
} else {
throw new Error(data.message);
}
}
// Get product details
async function getProduct(id, options = {}) {
const params = new URLSearchParams();
if (options.includeRelated) params.append('include_related', 'true');
if (options.includeReviews) params.append('include_reviews', 'true');
if (options.includeVariants) params.append('include_variants', 'true');
const response = await fetch(`/api/v1/products/${id}?${params}`);
const data = await response.json();
return data.data.product;
}
// Search products
async function searchProducts(query, filters = {}) {
const params = new URLSearchParams({
q: query,
...filters
});
const response = await fetch(`/api/v1/products/search?${params}`);
const data = await response.json();
return data.data;
}
// Usage examples
const products = await getProducts({
category_id: 5,
min_price: 100,
max_price: 500,
featured: true,
order_by: 'price',
direction: 'asc'
});
const product = await getProduct(123, {
includeRelated: true,
includeReviews: true,
includeVariants: true
});
const searchResults = await searchProducts('wireless', {
min_price: 100,
per_page: 10
});
Python (Requests)
import requests
BASE_URL = "http://localhost:3000/api/v1"
def get_products(filters=None):
"""List products with optional filters"""
params = {
'page': 1,
'per_page': 20
}
if filters:
params.update(filters)
response = requests.get(f"{BASE_URL}/products", params=params)
response.raise_for_status()
data = response.json()
if data['status'] == 'success':
return data['data']
else:
raise Exception(data['message'])
def get_product(product_id, include_related=False, include_reviews=False, include_variants=False):
"""Get product details by ID"""
params = {}
if include_related:
params['include_related'] = 'true'
if include_reviews:
params['include_reviews'] = 'true'
if include_variants:
params['include_variants'] = 'true'
response = requests.get(f"{BASE_URL}/products/{product_id}", params=params)
response.raise_for_status()
data = response.json()
return data['data']['product']
def search_products(query, filters=None):
"""Search products by query string"""
params = {'q': query}
if filters:
params.update(filters)
response = requests.get(f"{BASE_URL}/products/search", params=params)
response.raise_for_status()
data = response.json()
return data['data']
def get_categories(include_product_count=False, only_with_products=False):
"""List product categories"""
params = {}
if include_product_count:
params['include_product_count'] = 'true'
if only_with_products:
params['only_with_products'] = 'true'
response = requests.get(f"{BASE_URL}/products/categories", params=params)
response.raise_for_status()
data = response.json()
return data['data']['categories']
# Usage examples
products = get_products({
'category_id': 5,
'min_price': 100,
'max_price': 500,
'featured': 'true',
'order_by': 'price',
'direction': 'asc'
})
product = get_product(123, include_related=True, include_reviews=True)
search_results = search_products('wireless', {'min_price': 100})
categories = get_categories(include_product_count=True, only_with_products=True)
cURL
# List products
curl "http://localhost:3000/api/v1/products?page=1&per_page=20&min_price=100&max_price=500"
# Get product details
curl "http://localhost:3000/api/v1/products/123?include_related=true&include_reviews=true"
# Search products
curl "http://localhost:3000/api/v1/products/search?q=wireless&min_price=100"
# List categories
curl "http://localhost:3000/api/v1/products/categories?include_product_count=true"
Ruby (Net::HTTP)
require 'net/http'
require 'json'
require 'uri'
BASE_URL = 'http://localhost:3000/api/v1'
def get_products(filters = {})
params = { page: 1, per_page: 20 }.merge(filters)
uri = URI("#{BASE_URL}/products")
uri.query = URI.encode_www_form(params)
response = Net::HTTP.get_response(uri)
data = JSON.parse(response.body)
raise data['message'] unless data['status'] == 'success'
data['data']
end
def get_product(id, options = {})
params = {}
params['include_related'] = 'true' if options[:include_related]
params['include_reviews'] = 'true' if options[:include_reviews]
params['include_variants'] = 'true' if options[:include_variants]
uri = URI("#{BASE_URL}/products/#{id}")
uri.query = URI.encode_www_form(params) unless params.empty?
response = Net::HTTP.get_response(uri)
data = JSON.parse(response.body)
data['data']['product']
end
# Usage
products = get_products(
category_id: 5,
min_price: 100,
max_price: 500,
featured: true,
order_by: 'price',
direction: 'asc'
)
product = get_product(123, include_related: true, include_reviews: true)
Error Handling
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request (invalid parameters) |
| 404 | Not Found (product not found) |
| 429 | Too Many Requests (rate limit exceeded) |
| 500 | Internal Server Error |
Error Response Format
{
"status": "error",
"message": "Error description",
"error_code": "ERROR_CODE",
"errors": ["Detailed error 1", "Detailed error 2"] // Optional
}
Common Error Codes
NOT_FOUND- Resource not foundBAD_REQUEST- Invalid request parametersVALIDATION_ERROR- Validation failed (includeserrorsarray)MISSING_PARAMETER- Required parameter missingDATABASE_ERROR- Database error occurredINTERNAL_ERROR- Internal server error
Error Handling Example
async function getProductSafely(id) {
try {
const response = await fetch(`/api/v1/products/${id}`);
const data = await response.json();
if (data.status === 'success') {
return data.data.product;
} else {
// Handle specific error codes
switch (data.error_code) {
case 'NOT_FOUND':
console.error('Product not found');
break;
case 'BAD_REQUEST':
console.error('Invalid request:', data.message);
break;
default:
console.error('Error:', data.message);
}
throw new Error(data.message);
}
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
Rate Limiting
The API implements rate limiting to prevent abuse:
| Endpoint | Limit | Period |
|---|---|---|
| Index | 100 requests | 1 minute |
| Show | 200 requests | 1 minute |
| Search | 30 requests | 1 minute |
| Categories | 60 requests | 1 minute |
Rate Limit Response:
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please try again later."
}
HTTP Headers:
Retry-After: 60- Seconds until retry is allowed
Best Practices
1. Use Pagination
Always use pagination for list endpoints to avoid loading too much data:
// Good: Paginated request
const products = await getProducts({ page: 1, per_page: 20 });
// Bad: Requesting all products at once
const products = await getProducts({ per_page: 1000 });
2. Cache Responses
The API responses are cached. Use appropriate cache headers:
// Check cache headers
const response = await fetch('/api/v1/products');
const cacheControl = response.headers.get('Cache-Control');
3. Handle Errors Gracefully
Always check response status and handle errors:
const response = await fetch('/api/v1/products');
if (!response.ok) {
const error = await response.json();
// Handle error appropriately
}
4. Use Appropriate Filters
Use filters to reduce response size and improve performance:
// Good: Filtered request
const products = await getProducts({
category_id: 5,
min_price: 100,
max_price: 500
});
// Bad: Fetch all then filter client-side
const allProducts = await getProducts();
const filtered = allProducts.filter(p => p.category.id === 5);
5. Request Only Needed Data
Use include parameters only when needed:
// Good: Request only what you need
const product = await getProduct(123, { includeReviews: true });
// Bad: Request everything
const product = await getProduct(123, {
includeRelated: true,
includeReviews: true,
includeVariants: true
});
6. Respect Rate Limits
Implement exponential backoff for rate limit errors:
async function fetchWithRetry(url, options = {}, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}