Webhooks: List Product by ID
Overview
The List Product by ID webhook provides an optimized two-stage approach for retrieving products. This is useful for performance optimization when you have a large product catalog.
Instead of returning full product details with every request, you can return a lightweight list of product IDs and their last_updated timestamps. Talk2sync uses this information to determine which products have changed and only requests full details for those that need updating.
Two-Stage Retrieval Process
Stage 1: List Product IDs (Lightweight Index)
Request:
Talk2sync first requests a lightweight index of all your products:
Numeric Offset Pagination
GET https://www.example.com/your/endpoint/url?offset=0&sortorder=desc&jobid=123
T2SKey: {{API_KEY}}
Cursor-Based Pagination
GET https://www.example.com/your/endpoint/url?next=YXNkaWhhc3BvZGhpYXM&sortorder=desc&jobid=123
T2SKey: {{API_KEY}}
Response:
Return a lightweight list with only essential information:
{
"paging": {
"pageSize": 20,
"itemsTotal": 2,
"offset": 0
},
"products": [
{
"_id": "100004777",
"sku": "100004777",
"last_updated": 1517360038797
},
{
"_id": "100004778",
"sku": "100004778",
"last_updated": 1517360038800
}
]
}
Response Fields:
| Field | Type | Description |
|---|---|---|
_id | string | Unique product identifier (required) |
sku | string | Product SKU/code |
last_updated | integer | Unix timestamp (milliseconds) of last modification |
Stage 2: Retrieve Full Product Details (On Demand)
Request:
When Talk2sync determines a product needs updating, it requests the full product details using the product ID:
GET https://www.example.com/your/endpoint/url?id=100004777&jobid=123
T2SKey: {{API_KEY}}
Request Parameters:
| Parameter | Type | Description | Required |
|---|---|---|---|
id | string | Product ID to retrieve full details for | ✓ Yes |
jobid | integer | Unique identifier for this sync job | ✓ Yes |
T2SKey | header | Your security key | ✓ Yes |
Response:
Return the complete product details:
{
"_id": "100004777",
"sku": "100004777",
"last_updated": 1517360038797,
"title": "EON618S JBL SUBWOOFER 18\" AMPLIFICADO",
"url": "",
"brand": "",
"mpn": "",
"model": "",
"description": "JBL Premium Transducers",
"variations": [
{
"availabilities": [
{
"tag": "default",
"quantity": 50
}
],
"prices": [
{
"tag": "default",
"currency": "USD",
"number": 1060.51
}
],
"images": [
{
"url": "https://www.example.com/13080-thickbox_default/eon618s-jbl-subwoofer-18-amplificado-.jpg"
}
],
"videos": [
{
"url": ""
}
],
"barcode": "",
"size": "",
"color": "",
"variationid": ""
}
],
"properties": [
{
"extraattributes": {
"promociones": "",
"status": "",
"condicion_venta": "",
"peso": "",
"relacionados": "",
"custom_01": "",
"custom_02": "1",
"custom_03": ""
}
}
]
}
Complete Field Reference
Lightweight Product Object (Stage 1)
| Field | Type | Description |
|---|---|---|
_id | string | Unique product identifier (required) |
sku | string | Product SKU/code |
last_updated | integer | Unix timestamp (milliseconds) of last modification |
Full Product Object (Stage 2)
| Field | Type | Description |
|---|---|---|
_id | string | Unique product identifier (required) |
sku | string | Product SKU/code |
last_updated | integer | Unix timestamp (milliseconds) of last modification |
title | string | Product name/title |
url | string | Product URL in your store |
brand | string | Product brand |
mpn | string | Manufacturer Part Number |
model | string | Product model |
description | string | Detailed product description |
variations | array | Array of product variations |
properties | array | Array of custom properties/attributes |
Variation Object (Stage 2 Only)
| Field | Type | Description |
|---|---|---|
variationid | string | Unique variation identifier |
size | string | Size attribute |
color | string | Color attribute |
barcode | string | Barcode/EAN code |
availabilities | array | Stock information by location |
prices | array | Pricing information |
images | array | Product images |
videos | array | Product videos |
Availability Object
| Field | Type | Description |
|---|---|---|
tag | string | Location identifier (e.g., "default", "warehouse_1") |
quantity | integer | Available quantity |
Price Object
| Field | Type | Description |
|---|---|---|
tag | string | Price tier identifier (e.g., "default", "wholesale") |
currency | string | ISO 4217 currency code (e.g., "USD", "MXN") |
number | number | Price value |
Implementation Example (Node.js/Express)
// Stage 1: List product IDs with last_updated timestamps
app.get('/api/products', (req, res) => {
const { offset = 0, next, sortorder = 'desc', jobid } = req.query;
const apiKey = req.headers['t2skey'];
// Validate API key
if (apiKey !== process.env.TALK2SYNC_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
// Fetch lightweight product data (IDs and timestamps only)
const allProducts = fetchProductsLightweight(sortorder);
const pageSize = 20;
const startIndex = parseInt(offset) || 0;
const paginatedProducts = allProducts.slice(startIndex, startIndex + pageSize);
const response = {
paging: {
pageSize: paginatedProducts.length,
itemsTotal: allProducts.length,
offset: startIndex
},
products: paginatedProducts.map(product => ({
_id: product.id,
sku: product.sku,
last_updated: new Date(product.updatedAt).getTime()
}))
};
res.json(response);
} catch (error) {
console.error('Error fetching products:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Stage 2: Retrieve full product details by ID
app.get('/api/products', (req, res) => {
const { id, jobid } = req.query;
const apiKey = req.headers['t2skey'];
// Validate API key
if (apiKey !== process.env.TALK2SYNC_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
// If ID parameter is present, return full product details
if (id) {
const product = fetchProductById(id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
const response = {
_id: product.id,
sku: product.sku,
last_updated: new Date(product.updatedAt).getTime(),
title: product.name,
url: product.url,
brand: product.brand,
mpn: product.mpn,
model: product.model,
description: product.description,
variations: product.variations.map(v => ({
variationid: v.id,
size: v.size || "",
color: v.color || "",
barcode: v.barcode || "",
availabilities: v.stocks.map(s => ({
tag: s.location || "default",
quantity: s.quantity
})),
prices: v.prices.map(p => ({
tag: p.tier || "default",
currency: p.currency || "USD",
number: p.value
})),
images: v.images.map(img => ({ url: img })),
videos: v.videos.map(vid => ({ url: vid }))
})),
properties: [{
extraattributes: product.customFields || {}
}]
};
res.json(response);
}
} catch (error) {
console.error('Error fetching product:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
How Talk2sync Uses This Approach
- First Request: Talk2sync calls your endpoint without an
idparameter to get the lightweight index - Comparison: Talk2sync compares the
last_updatedtimestamp with what it has stored - Selective Requests: For products with newer
last_updatedvalues, Talk2sync makes individual requests with theidparameter - Bandwidth Optimization: Only products that have actually changed trigger full data requests
Performance Benefits
| Aspect | Benefit |
|---|---|
| Bandwidth | Reduced by only fetching full details for changed products |
| Sync Speed | Much faster initial index comparison |
| Database Load | Lower load from fewer full product fetches |
| Scalability | Handles large catalogs efficiently |
When to Use This Approach
✓ Use when:
- Your catalog has 10,000+ products
- Products are updated infrequently
- You want to minimize bandwidth usage
- Response time is critical
✗ Avoid when:
- Your catalog is small (< 1,000 products)
- Most products change frequently
- Bandwidth is not a concern
- Simplicity is more important than optimization
Response Status Codes
| Status | Meaning | Action |
|---|---|---|
200 OK | Data retrieved successfully | Talk2sync processes the response |
400 Bad Request | Invalid parameters | Check your request parameters |
401 Unauthorized | Invalid or missing API key | Verify your security key |
404 Not Found | Product ID does not exist | Check the product ID |
500 Internal Server Error | Server error | Check your server logs |
Pagination Guidelines
Stage 1: List Endpoints
Use the same pagination strategies as List Products:
- Numeric Offset: For small to medium datasets
- Cursor-Based: For large or frequently changing datasets
Stage 2: Single Product Request
No pagination needed—this endpoint always returns a single complete product.
Best Practices
✓ Do:
- Return lightweight data in Stage 1 (IDs and timestamps only)
- Keep
last_updatedtimestamps accurate and up-to-date - Validate the API key on every request
- Return 404 if a requested product ID doesn't exist
- Cache full product details when possible
✗ Don't:
- Return full product details in Stage 1 (defeats the purpose)
- Return incomplete data in Stage 2
- Include sensitive information in responses
- Ignore the sorting parameter in Stage 1
Comparison: List Products vs List Product by ID
| Feature | List Products | List Product by ID |
|---|---|---|
| Use Case | Small catalogs, simple integrations | Large catalogs, performance optimization |
| Stage 1 Response | Full product details | Lightweight (ID + timestamp) |
| Stage 2 Response | N/A | Full product details (on demand) |
| Bandwidth Usage | High (all details always sent) | Low (only changed products fetched) |
| Complexity | Simple | More complex (two endpoints) |
| Best For | < 1,000 products | 10,000+ products |