Create Product
curl --request POST \
--url https://api.example.com/api/products/create \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <x-api-key>' \
--data '
{
"name": "<string>",
"price": 123,
"description": "<string>",
"stock": 123,
"current_price": 123,
"short_description": "<string>",
"shipping_cost": 123,
"primary_image": "<string>",
"images": [
{}
],
"variations": [
{}
],
"size": [
{}
],
"colors": [
{}
],
"categories": [
{}
],
"is_sale": true,
"is_active": true,
"custom_questions": [
{}
],
"internal_note": "<string>",
"metadata": {}
}
'import requests
url = "https://api.example.com/api/products/create"
payload = {
"name": "<string>",
"price": 123,
"description": "<string>",
"stock": 123,
"current_price": 123,
"short_description": "<string>",
"shipping_cost": 123,
"primary_image": "<string>",
"images": [{}],
"variations": [{}],
"size": [{}],
"colors": [{}],
"categories": [{}],
"is_sale": True,
"is_active": True,
"custom_questions": [{}],
"internal_note": "<string>",
"metadata": {}
}
headers = {
"X-API-Key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
price: 123,
description: '<string>',
stock: 123,
current_price: 123,
short_description: '<string>',
shipping_cost: 123,
primary_image: '<string>',
images: [{}],
variations: [{}],
size: [{}],
colors: [{}],
categories: [{}],
is_sale: true,
is_active: true,
custom_questions: [{}],
internal_note: '<string>',
metadata: {}
})
};
fetch('https://api.example.com/api/products/create', 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.example.com/api/products/create",
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([
'name' => '<string>',
'price' => 123,
'description' => '<string>',
'stock' => 123,
'current_price' => 123,
'short_description' => '<string>',
'shipping_cost' => 123,
'primary_image' => '<string>',
'images' => [
[
]
],
'variations' => [
[
]
],
'size' => [
[
]
],
'colors' => [
[
]
],
'categories' => [
[
]
],
'is_sale' => true,
'is_active' => true,
'custom_questions' => [
[
]
],
'internal_note' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <x-api-key>"
],
]);
$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://api.example.com/api/products/create"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<x-api-key>")
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://api.example.com/api/products/create")
.header("X-API-Key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/products/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"product_id": "<string>"
}Product APIs
Create Product
Create a new product in your store
POST
/
api
/
products
/
create
Create Product
curl --request POST \
--url https://api.example.com/api/products/create \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <x-api-key>' \
--data '
{
"name": "<string>",
"price": 123,
"description": "<string>",
"stock": 123,
"current_price": 123,
"short_description": "<string>",
"shipping_cost": 123,
"primary_image": "<string>",
"images": [
{}
],
"variations": [
{}
],
"size": [
{}
],
"colors": [
{}
],
"categories": [
{}
],
"is_sale": true,
"is_active": true,
"custom_questions": [
{}
],
"internal_note": "<string>",
"metadata": {}
}
'import requests
url = "https://api.example.com/api/products/create"
payload = {
"name": "<string>",
"price": 123,
"description": "<string>",
"stock": 123,
"current_price": 123,
"short_description": "<string>",
"shipping_cost": 123,
"primary_image": "<string>",
"images": [{}],
"variations": [{}],
"size": [{}],
"colors": [{}],
"categories": [{}],
"is_sale": True,
"is_active": True,
"custom_questions": [{}],
"internal_note": "<string>",
"metadata": {}
}
headers = {
"X-API-Key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
price: 123,
description: '<string>',
stock: 123,
current_price: 123,
short_description: '<string>',
shipping_cost: 123,
primary_image: '<string>',
images: [{}],
variations: [{}],
size: [{}],
colors: [{}],
categories: [{}],
is_sale: true,
is_active: true,
custom_questions: [{}],
internal_note: '<string>',
metadata: {}
})
};
fetch('https://api.example.com/api/products/create', 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.example.com/api/products/create",
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([
'name' => '<string>',
'price' => 123,
'description' => '<string>',
'stock' => 123,
'current_price' => 123,
'short_description' => '<string>',
'shipping_cost' => 123,
'primary_image' => '<string>',
'images' => [
[
]
],
'variations' => [
[
]
],
'size' => [
[
]
],
'colors' => [
[
]
],
'categories' => [
[
]
],
'is_sale' => true,
'is_active' => true,
'custom_questions' => [
[
]
],
'internal_note' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <x-api-key>"
],
]);
$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://api.example.com/api/products/create"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<x-api-key>")
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://api.example.com/api/products/create")
.header("X-API-Key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/products/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"price\": 123,\n \"description\": \"<string>\",\n \"stock\": 123,\n \"current_price\": 123,\n \"short_description\": \"<string>\",\n \"shipping_cost\": 123,\n \"primary_image\": \"<string>\",\n \"images\": [\n {}\n ],\n \"variations\": [\n {}\n ],\n \"size\": [\n {}\n ],\n \"colors\": [\n {}\n ],\n \"categories\": [\n {}\n ],\n \"is_sale\": true,\n \"is_active\": true,\n \"custom_questions\": [\n {}\n ],\n \"internal_note\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"product_id": "<string>"
}Overview
Create a new product in your store using API key authentication. Perfect for bulk imports or automated product creation from your inventory system.Rate Limit: 20 requests per minute
Endpoint
POST https://api.tribemade.in/api/products/create
Authentication
string
required
Your TribeMade API key (format:
tb-xxxx-xxx-xxxx)Request Body
Required Fields
string
required
Product name (3-30 characters)
number
required
Original/MRP price in INR (must be > 0)
string
required
Detailed product description (0-500 characters)
integer
required
Available quantity (must be >= 0)
Optional Fields
number
default:"price"
Sale price in INR (must be >= 0). Defaults to the original price if not provided.
string
default:"First 100 chars of description"
Short description for listings (0-50 characters)
number
default:0
Shipping cost in INR (must be >= 0)
string
Primary product image URL or base64 encoded image (max 5MB)
array
Additional product images (max 10 images, each max 5MB)
array
Product variations (max 20 items). Example: [“Regular Fit”, “Slim Fit”]
array
Available sizes (max 20 items). Example: [“S”, “M”, “L”, “XL”]
array
Available colors (max 20 items). Example: [“White”, “Black”, “Navy Blue”]
array
Product categories (max 20, must exist in your store). Create categories in your dashboard first.
boolean
default:false
Mark product as on sale
boolean
default:true
Product visibility to customers
array
Custom questions for customers (max 5 questions). Each question must have:
question: Question text (1-200 characters)type: Either “text” or “image”
string
default:""
Private seller-only notes (0-500 characters). Not visible to customers.
object
Custom key-value pairs for additional product information
Response
string
UUID of the newly created product
Examples
Minimal Example
Create a basic product with only required fields:curl -X POST https://api.tribemade.in/api/products/create \
-H "X-API-Key: tb-a1b2-c3d-e4f5" \
-H "Content-Type: application/json" \
-d '{
"name": "Simple T-Shirt",
"price": 499,
"description": "Basic cotton t-shirt",
"stock": 50
}'
import requests
url = "https://api.tribemade.in/api/products/create"
headers = {
"X-API-Key": "tb-a1b2-c3d-e4f5",
"Content-Type": "application/json"
}
data = {
"name": "Simple T-Shirt",
"price": 499,
"description": "Basic cotton t-shirt",
"stock": 50
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
# Output: {"product_id": "660e8400-e29b-41d4-a716-446655440123"}
const fetch = require('node-fetch');
const url = 'https://api.tribemade.in/api/products/create';
const headers = {
'X-API-Key': 'tb-a1b2-c3d-e4f5',
'Content-Type': 'application/json'
};
const data = {
name: 'Simple T-Shirt',
price: 499,
description: 'Basic cotton t-shirt',
stock: 50
};
fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
// Output: {"product_id": "660e8400-e29b-41d4-a716-446655440123"}
<?php
$url = 'https://api.tribemade.in/api/products/create';
$headers = array(
'X-API-Key: tb-a1b2-c3d-e4f5',
'Content-Type: application/json'
);
$data = array(
'name' => 'Simple T-Shirt',
'price' => 499,
'description' => 'Basic cotton t-shirt',
'stock' => 50
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
// Output: {"product_id": "660e8400-e29b-41d4-a716-446655440123"}
?>
Success Response
{
"product_id": "660e8400-e29b-41d4-a716-446655440123"
}
Full Example with All Options
Create a product with all available fields:{
"name": "Premium Cotton T-Shirt",
"price": 1299,
"current_price": 999,
"description": "High-quality 100% cotton t-shirt with premium finish and comfortable fit",
"short_description": "Premium cotton tee",
"stock": 100,
"shipping_cost": 50,
"primary_image": "https://cdn.example.com/tshirt.jpg",
"images": [
"https://cdn.example.com/tshirt-2.jpg",
"https://cdn.example.com/tshirt-3.jpg"
],
"variations": ["Regular Fit", "Slim Fit"],
"size": ["S", "M", "L", "XL", "XXL"],
"colors": ["White", "Black", "Navy Blue"],
"categories": ["Fashion", "Men's Wear"],
"is_sale": true,
"is_active": true,
"custom_questions": [
{
"question": "Do you want your name printed?",
"type": "text"
},
{
"question": "Upload your design",
"type": "image"
}
],
"internal_note": "Supplier: ABC Corp, Cost: ₹500, MOQ: 50, Lead time: 3 days",
"metadata": {
"material": "100% Cotton",
"care": "Machine wash cold",
"origin": "India"
}
}
import requests
url = "https://api.tribemade.in/api/products/create"
headers = {
"X-API-Key": "tb-a1b2-c3d-e4f5",
"Content-Type": "application/json"
}
data = {
"name": "Premium Cotton T-Shirt",
"price": 1299,
"current_price": 999,
"description": "High-quality 100% cotton t-shirt with premium finish",
"short_description": "Premium cotton tee",
"stock": 100,
"shipping_cost": 50,
"primary_image": "https://cdn.example.com/tshirt.jpg",
"images": ["https://cdn.example.com/tshirt-2.jpg"],
"variations": ["Regular Fit", "Slim Fit"],
"size": ["S", "M", "L", "XL", "XXL"],
"colors": ["White", "Black", "Navy Blue"],
"categories": ["Fashion", "Men's Wear"],
"is_sale": True,
"is_active": True,
"custom_questions": [
{
"question": "Do you want your name printed?",
"type": "text"
}
],
"internal_note": "Supplier: ABC Corp, Cost: ₹500",
"metadata": {
"material": "100% Cotton",
"care": "Machine wash cold"
}
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
Error Responses
400 Bad Request - Missing Required Fields
{
"error": "name, price, description, and stock are required"
}
400 Bad Request - Invalid Name Length
{
"error": "Name must be between 3 and 30 characters"
}
400 Bad Request - Invalid Price
{
"error": "price must be > 0"
}
400 Bad Request - Invalid Description
{
"error": "Description must be between 0 and 500 characters"
}
400 Bad Request - Invalid Stock
{
"error": "stock must be >= 0"
}
400 Bad Request - Invalid Short Description
{
"error": "short_description must be between 0 and 50 characters"
}
400 Bad Request - Invalid Categories
{
"error": "All categories must be in store categories",
"invalid": ["InvalidCategory"]
}
Categories must be created in your store first via the Dashboard. Use exact category names (case-sensitive).
400 Bad Request - Too Many Images
{
"error": "Maximum 10 images allowed"
}
400 Bad Request - Image Too Large
{
"error": "Primary image base64 size must be <= 5MB"
}
400 Bad Request - Too Many Array Items
{
"error": "variations cannot have more than 20 items"
}
This error also applies to:
size, colors, categories arrays (max 20 items each)400 Bad Request - Too Many Custom Questions
{
"error": "Maximum 5 custom questions allowed"
}
400 Bad Request - Invalid Custom Question Type
{
"error": "custom_questions[0].type must be 'text' or 'image'"
}
401 Unauthorized
{
"error": "Missing API key"
}
{
"error": "Invalid API key"
}
429 Too Many Requests
{
"error": "Rate limit exceeded",
"retry_after": 60
}
Custom Questions
Custom questions allow you to collect additional information from customers during checkout.Question Format
{
"question": "Your question text here (1-200 characters)",
"type": "text" // or "image"
}
Question Types
| Type | Description | Customer Input |
|---|---|---|
text | Text question | Customer provides text answer |
image | Image upload | Customer uploads an image |
Examples
{
"custom_questions": [
{
"question": "Do you want your name printed?",
"type": "text"
},
{
"question": "Upload your design for customization",
"type": "image"
},
{
"question": "Any special instructions?",
"type": "text"
}
]
}
Internal Notes
Useinternal_note to store private information visible only to you:
{
"internal_note": "Supplier: ABC Corp, Cost: ₹500, MOQ: 50, Lead time: 3 days, Contact: supplier@example.com"
}
- Supplier information
- Cost price and margins
- Minimum order quantities
- Lead times
- Internal SKU codes
- Warehouse locations
Internal notes are never shown to customers - they’re only visible in your dashboard and API responses.
Bulk Import Example
Import multiple products efficiently:import requests
import time
url = "https://api.tribemade.in/api/products/create"
headers = {
"X-API-Key": "tb-a1b2-c3d-e4f5",
"Content-Type": "application/json"
}
products = [
{
"name": "Product 1",
"price": 499,
"description": "Description 1",
"stock": 50
},
{
"name": "Product 2",
"price": 699,
"description": "Description 2",
"stock": 30
},
# ... more products
]
# Import in batches to respect rate limits (20 req/min)
batch_size = 15
for i in range(0, len(products), batch_size):
batch = products[i:i + batch_size]
for product in batch:
try:
response = requests.post(url, headers=headers, json=product)
if response.status_code == 201:
result = response.json()
print(f"Created: {product['name']} -> {result['product_id']}")
else:
print(f"Failed: {product['name']} -> {response.json()['error']}")
except Exception as e:
print(f"Error: {product['name']} -> {str(e)}")
# Wait before next batch to avoid rate limit
if i + batch_size < len(products):
print(f"Batch complete. Waiting 60s...")
time.sleep(60)
print("Import complete!")
Best Practices
Validate data before sending
Validate data before sending
Validate all fields client-side before making API requests to reduce errors:
- Check name length (3-30 characters)
- Ensure price > 0
- Verify categories exist in your store
- Confirm image sizes ≤ 5MB
Use image URLs when possible
Use image URLs when possible
Instead of base64-encoding images, host them on a CDN and provide URLs:✅ Recommended:❌ Less efficient:
{
"primary_image": "https://cdn.example.com/product.jpg"
}
{
"primary_image": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
}
Keep arrays within limits
Keep arrays within limits
Maximum limits for arrays:
- Images: 10
- Variations, sizes, colors, categories: 20 each
- Custom questions: 5
Respect rate limits
Respect rate limits
- Maximum 20 requests per minute
- For bulk imports, batch requests with 60-second pauses
- See Rate Limits for details
Use internal notes effectively
Use internal notes effectively
Store useful internal information:
- Supplier details and contact info
- Cost price and profit margins
- SKU codes and barcodes
- Warehouse locations
- Reorder information
Next Steps
Edit Product
Update existing product details
Delete Product
Remove products from your store
Order APIs
Manage orders programmatically
Rate Limits
Understand API rate limits

