Fetching Products
This guide provides comprehensive instructions for retrieving product data using the Nue Lifecycle Management API. Learn how to fetch all products or retrieve individual products by ID.
Prerequisites
Before you begin, ensure you have:
- A valid Nue API key
- Basic understanding of REST APIs and JSON
- Familiarity with product data structures
Authentication
All product operations require authentication using your Nue API key in the nue-api-key header:
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");Product Retrieval Methods
Fetch All Products
Try it now: Fetch Products →
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
// Fetch all published products
fetch('https://api.nue.io/catalog/products', {
method: 'GET',
headers: myHeaders
})
.then(response => response.json())
.then(products => {
console.log(`Retrieved ${products.length} products`);
products.forEach(product => {
console.log(`\n--- ${product.name} ---`);
console.log(`ID: ${product.id}`);
console.log(`SKU: ${product.sku}`);
console.log(`Status: ${product.status}`);
console.log(`Publish Status: ${product.publishStatus}`);
console.log(`Description: ${product.description || 'No description'}`);
// Display pricing information
if (product.priceBookEntries && product.priceBookEntries.length > 0) {
console.log('Available Pricing:');
product.priceBookEntries.forEach(entry => {
console.log(` - ${entry.currencyIsoCode}: ${entry.listPrice}/${entry.uom.name}`);
});
}
// Display bundle information if applicable
if (product.configurable && product.productOptions) {
console.log(`Bundle Components: ${product.productOptions.length} options`);
}
});
})
.catch(error => console.log('Error:', error));Fetch Single Product by ID
Retrieve a specific product with detailed information:
// Use a real Salesforce Product ID
const productId = "01tEa00000E8IpUIAV";
fetch(`https://api.nue.io/catalog/products/${productId}`, {
method: 'GET',
headers: myHeaders
})
.then(response => {
if (response.status === 404) {
console.log('❌ Product not found');
return null;
}
return response.json();
})
.then(product => {
if (!product) return;
console.log('✅ Product found:');
console.log(`Name: ${product.name}`);
console.log(`SKU: ${product.sku}`);
console.log(`ID: ${product.id}`);
console.log(`Status: ${product.status}`);
console.log(`Publish Status: ${product.publishStatus}`);
// Display detailed product information
console.log(`\nProduct Details:`);
console.log(` Description: ${product.description || 'Not provided'}`);
console.log(` Record Type: ${product.recordType}`);
console.log(` Price Model: ${product.priceModel}`);
console.log(` Configurable: ${product.configurable ? 'Yes' : 'No'}`);
// Display pricing options
if (product.priceBookEntries && product.priceBookEntries.length > 0) {
console.log(`\nPricing Options:`);
product.priceBookEntries.forEach(entry => {
const recommended = entry.recommended ? ' ⭐' : '';
console.log(` ${entry.currencyIsoCode} ${entry.listPrice}/${entry.uom.name}${recommended}`);
});
}
// Display availability
console.log(`\nAvailability:`);
console.log(` Start Date: ${product.startDate || 'Not set'}`);
console.log(` End Date: ${product.endDate || 'No expiration'}`);
// Display bundle information
if (product.configurable && product.productOptions) {
console.log(`\nBundle Information:`);
console.log(` Components: ${product.productOptions.length} options`);
product.productOptions.forEach((option, index) => {
console.log(` ${index + 1}. ${option.optionName} (Required: ${option.required})`);
});
}
})
.catch(error => console.log('Error:', error));Advanced Product Management
Product Catalog Manager
Build a comprehensive product catalog with advanced search and categorization:
class ProductCatalogManager {
constructor(apiKey) {
this.apiKey = apiKey;
this.headers = new Headers();
this.headers.append("nue-api-key", apiKey);
this.headers.append("Content-Type", "application/json");
this.productCache = new Map();
}
async buildProductCatalog() {
try {
console.log('📦 Building product catalog...');
// Fetch all published products
const response = await fetch('https://api.nue.io/catalog/products', {
method: 'GET',
headers: this.headers
});
if (!response.ok) {
throw new Error(`Failed to fetch products: ${response.status}`);
}
const products = await response.json();
// Build comprehensive catalog
const catalog = {
products: products,
byCategory: {},
byRecordType: {},
byCurrency: {},
byStatus: { active: [], inactive: [] },
bundles: [],
recurringProducts: [],
statistics: {
totalProducts: products.length,
activeProducts: 0,
inactiveProducts: 0,
bundleProducts: 0,
recurringProducts: 0,
categories: new Set(),
currencies: new Set(),
averagePrice: 0,
priceRange: { min: null, max: null }
}
};
let totalPrice = 0;
let priceCount = 0;
products.forEach(product => {
// Categorize by category
if (product.productCategory) {
if (!catalog.byCategory[product.productCategory]) {
catalog.byCategory[product.productCategory] = [];
}
catalog.byCategory[product.productCategory].push(product);
catalog.statistics.categories.add(product.productCategory);
}
// Categorize by record type
if (product.recordType) {
if (!catalog.byRecordType[product.recordType]) {
catalog.byRecordType[product.recordType] = [];
}
catalog.byRecordType[product.recordType].push(product);
}
// Categorize by currency from price book entries
if (product.priceBookEntries && product.priceBookEntries.length > 0) {
product.priceBookEntries.forEach(entry => {
if (entry.currencyIsoCode) {
if (!catalog.byCurrency[entry.currencyIsoCode]) {
catalog.byCurrency[entry.currencyIsoCode] = [];
}
if (!catalog.byCurrency[entry.currencyIsoCode].includes(product)) {
catalog.byCurrency[entry.currencyIsoCode].push(product);
}
catalog.statistics.currencies.add(entry.currencyIsoCode);
}
});
}
// Categorize by status
if (product.status === 'Active') {
catalog.byStatus.active.push(product);
catalog.statistics.activeProducts++;
} else {
catalog.byStatus.inactive.push(product);
catalog.statistics.inactiveProducts++;
}
// Track bundles
if (product.configurable) {
catalog.bundles.push(product);
catalog.statistics.bundleProducts++;
}
// Track recurring products
if (product.priceModel === 'Recurring') {
catalog.recurringProducts.push(product);
catalog.statistics.recurringProducts++;
}
// Price analysis from price book entries
if (product.priceBookEntries && product.priceBookEntries.length > 0) {
product.priceBookEntries.forEach(entry => {
if (entry.listPrice) {
const price = parseFloat(entry.listPrice);
totalPrice += price;
priceCount++;
if (catalog.statistics.priceRange.min === null || price < catalog.statistics.priceRange.min) {
catalog.statistics.priceRange.min = price;
}
if (catalog.statistics.priceRange.max === null || price > catalog.statistics.priceRange.max) {
catalog.statistics.priceRange.max = price;
}
}
});
}
// Cache product for quick lookup
this.productCache.set(product.id, product);
});
// Calculate average price
if (priceCount > 0) {
catalog.statistics.averagePrice = totalPrice / priceCount;
}
// Display catalog summary
console.log('\n📊 Product Catalog Summary:');
console.log(` Total Products: ${catalog.statistics.totalProducts}`);
console.log(` Active: ${catalog.statistics.activeProducts}`);
console.log(` Inactive: ${catalog.statistics.inactiveProducts}`);
console.log(` Bundles: ${catalog.statistics.bundleProducts}`);
console.log(` Recurring: ${catalog.statistics.recurringProducts}`);
console.log(` Categories: ${catalog.statistics.categories.size}`);
console.log(` Currencies: ${Array.from(catalog.statistics.currencies).join(', ')}`);
if (catalog.statistics.averagePrice > 0) {
console.log(` Average Price: $${catalog.statistics.averagePrice.toFixed(2)}`);
console.log(` Price Range: $${catalog.statistics.priceRange.min} - $${catalog.statistics.priceRange.max}`);
}
console.log(`\n📦 Products by Record Type:`);
Object.entries(catalog.byRecordType)
.sort(([,a], [,b]) => b.length - a.length)
.forEach(([recordType, products]) => {
console.log(` ${recordType}: ${products.length} products`);
});
console.log(`\n🏷️ Products by Category:`);
Object.entries(catalog.byCategory)
.sort(([,a], [,b]) => b.length - a.length)
.forEach(([category, products]) => {
console.log(` ${category}: ${products.length} products`);
});
return catalog;
} catch (error) {
console.error('Failed to build product catalog:', error);
throw error;
}
}
async getProductById(productId) {
// Check cache first
if (this.productCache.has(productId)) {
return this.productCache.get(productId);
}
try {
const response = await fetch(`https://api.nue.io/catalog/products/${productId}`, {
method: 'GET',
headers: this.headers
});
if (response.status === 404) {
return null;
}
if (!response.ok) {
throw new Error(`Failed to fetch product: ${response.status}`);
}
const product = await response.json();
this.productCache.set(productId, product);
return product;
} catch (error) {
console.error(`Failed to fetch product ${productId}:`, error);
return null;
}
}
searchProducts(catalog, searchTerm) {
const term = searchTerm.toLowerCase();
return catalog.products.filter(product => {
return product.name?.toLowerCase().includes(term) ||
product.sku?.toLowerCase().includes(term) ||
product.description?.toLowerCase().includes(term) ||
product.productCategory?.toLowerCase().includes(term);
});
}
getProductsByCategory(catalog, category) {
return catalog.byCategory[category] || [];
}
getActiveProductsInPriceRange(catalog, minPrice, maxPrice, currency = null) {
return catalog.byStatus.active.filter(product => {
if (!product.priceBookEntries || product.priceBookEntries.length === 0) return false;
return product.priceBookEntries.some(entry => {
if (currency && entry.currencyIsoCode !== currency) return false;
if (!entry.listPrice) return false;
const price = parseFloat(entry.listPrice);
return price >= minPrice && price <= maxPrice;
});
});
}
getBundleProducts(catalog) {
return catalog.bundles;
}
getRecurringProducts(catalog) {
return catalog.recurringProducts;
}
}
// Usage
const catalogManager = new ProductCatalogManager("YOUR_API_KEY_HERE");
catalogManager.buildProductCatalog()
.then(catalog => {
console.log('\n📦 Product catalog built successfully');
// Search example
const searchResults = catalogManager.searchProducts(catalog, 'platform');
console.log(`\n🔍 Search for "platform": ${searchResults.length} results`);
// Get products by category
const firstCategory = Object.keys(catalog.byCategory)[0];
if (firstCategory) {
const categoryProducts = catalogManager.getProductsByCategory(catalog, firstCategory);
console.log(`\n🏷️ ${firstCategory} category: ${categoryProducts.length} products`);
}
// Get products in price range
const priceRangeProducts = catalogManager.getActiveProductsInPriceRange(catalog, 100, 1000, 'USD');
console.log(`\n💰 Products $100-$1000: ${priceRangeProducts.length} products`);
return catalog;
});Error Handling and Best Practices
Robust Product Fetching
async function safeProductFetch(productId = null) {
const myHeaders = new Headers();
myHeaders.append("nue-api-key", "YOUR_API_KEY_HERE");
myHeaders.append("Content-Type", "application/json");
try {
// Validate Salesforce ID format if productId provided
if (productId) {
const salesforceIdRegex = /^[a-zA-Z0-9]{15,18}$/;
if (!salesforceIdRegex.test(productId)) {
throw new Error(`Invalid Salesforce ID format: ${productId}`);
}
}
// GET /catalog/products returns an array of products.
// GET /catalog/products/{productId} returns a single product object.
const url = productId
? `https://api.nue.io/catalog/products/${productId}`
: 'https://api.nue.io/catalog/products';
const response = await fetch(url, {
method: 'GET',
headers: myHeaders
});
if (productId && response.status === 404) {
throw new Error(`Product ${productId} not found or not published`);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
const products = productId ? [data] : data;
return {
products,
found: products.length,
requested: productId ? 1 : 'all',
error: null
};
} catch (error) {
console.error('Product fetch error:', error);
return {
products: [],
found: 0,
requested: productId ? 1 : 'all',
error: error.message
};
}
}
// Usage with error handling
safeProductFetch("01tEa00000E8IpUIAV").then(result => {
if (result.error) {
console.error('Fetch failed:', result.error);
} else {
console.log(`Successfully fetched ${result.found} product(s)`);
result.products.forEach(product => {
console.log(`- ${product.name} (${product.id})`);
});
}
});Query Parameters Reference
Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
productId | String | No | Salesforce Product ID to retrieve specific product | "01tEa00000E8IpUIAV" |
Response Structure
Product Object Fields
Core Identity:
- id - Unique product identifier (Salesforce ID)
- name - Product name
- sku - SKU or product code
- description - Product description
- status - Product status (Active, Inactive, Draft)
- publishStatus - Publish status for self-service
Product Classification:
- recordType - Product record type (Product, Service, Bundle)
- productCategory - Product category classification
- configurable - Whether product has configuration options
Pricing Information:
- priceBookEntries - Array of pricing options
- listPrice - Price amount
- currencyIsoCode - Currency code
- uom - Unit of measure object
- active - Whether pricing is active
- recommended - Whether pricing is recommended
Bundle Information (if configurable):
- productOptions - Array of bundle components
- bundleTemplate - Bundle behavior model
Dynamic Product Options
When a bundle contains dynamic product options, the catalog expands these into concrete product options at publish time. Each dynamically generated option has a composite ID that combines the original dynamic option ID with the price book entry ID:
productOptionId = "{{dynamicOptionId}}_{{priceBookEntryId}}"Example Response with Dynamic Options:
{
"id": "01tQL00000LaZO9YAN",
"name": "Enterprise Platform Bundle",
"recordType": "Bundle",
"configurable": true,
"productOptions": [
{
"id": "a0jQL00000JP4IPYA1_01uQL00000CXUGXYA5",
"name": "Premium Integration",
"optionName": "Premium Integration",
"optionType": "LinkToBundleQuantity",
"defaultQuantity": 1.0,
"quantityEditable": false,
"required": false,
"bundled": false,
"priceBookEntry": {
"id": "01uQL00000CXUGXYA5",
"listPrice": 110,
"active": true,
"billingTiming": "In Advance",
"uom": {
"id": "a0yQL000006hrleYAA",
"name": "User/Year",
"quantityDimension": "User",
"termDimension": "Year"
},
"priceBookId": "01sQL00000IMyw2YAD",
"productId": "01tQL00000LMgR7YAL"
},
"optionProductFilter": "{\"filters\":[...],\"soql\":\"(Name like '%Integration%')\"}"
},
{
"id": "a0jQL00000JP4IPYA1_01uQL00000CXUGaYAP",
"name": "Premium Integration",
"optionName": "Premium Integration",
"optionType": "LinkToBundleQuantity",
"defaultQuantity": 1.0,
"quantityEditable": false,
"required": false,
"bundled": false,
"priceBookEntry": {
"id": "01uQL00000CXUGaYAP",
"listPrice": 55,
"active": true,
"billingTiming": "In Advance",
"uom": {
"id": "a0yQL000006hrlgYAA",
"name": "User/Semi-Annual",
"quantityDimension": "User",
"termDimension": "Semi-Annual"
},
"priceBookId": "01sQL00000IMyw2YAD",
"productId": "01tQL00000LMgR7YAL"
},
"optionProductFilter": "{\"filters\":[...],\"soql\":\"(Name like '%Integration%')\"}"
},
{
"id": "a0jQL00000JP4IPYA1_01uQL00000CXUGYYA5",
"name": "Premium Integration",
"optionName": "Premium Integration",
"optionType": "LinkToBundleQuantity",
"defaultQuantity": 1.0,
"quantityEditable": false,
"required": false,
"bundled": false,
"priceBookEntry": {
"id": "01uQL00000CXUGYYA5",
"listPrice": 13,
"active": true,
"billingTiming": "In Arrears",
"uom": {
"id": "a0yQL000006hrlbYAA",
"name": "User/Month",
"quantityDimension": "User",
"termDimension": "Month"
},
"priceBookId": "01sQL00000IMyw2YAD",
"productId": "01tQL00000LMgR7YAL"
},
"optionProductFilter": "{\"filters\":[...],\"soql\":\"(Name like '%Integration%')\"}"
}
]
}In this example, the "Premium Integration" product option was configured as a dynamic option with a product filter. When published, it expanded into multiple concrete options—one for each matching price book entry. All options share the same base dynamic option ID (a0jQL00000JP4IPYA1) but have different price book entry IDs appended, creating unique composite IDs for each pricing variation.
Identifying Dynamic Options:
Dynamic product options can be identified by:
- The presence of optionProductFilter field containing the filter criteria
- Composite IDs in the format {dynamicOptionId}_{priceBookEntryId}
- Multiple product options with the same base ID but different price book entries
Using Dynamic Option IDs
When creating orders with dynamic product options, pass the full composite ID (including the _ and price book entry ID) in the productOptionId field.
Common Use Cases
Product Catalog Display
// Build product catalog for display
const catalog = await catalogManager.buildProductCatalog();
// Enable search and filtering featuresSingle Product Details
// Get detailed product information
const product = await catalogManager.getProductById("01tEa00000E8IpUIAV");
// Display product details and pricingSearch and Filtering
// Search products by term
const results = catalogManager.searchProducts(catalog, "platform");
// Filter by category or price rangePerformance Optimization
Efficient Product Operations
- Cache products: Store frequently accessed product information
- Validate IDs: Client-side validation of Salesforce ID format
- Error recovery: Implement graceful handling of missing products
- Single API calls: Fetch all products once, then search/filter locally
Rate Limiting
- Respect limits: Rate limits apply; contact Nue support to confirm your limits or request an increase
- Implement backoff: Use exponential backoff for retry logic
- Monitor usage: Track API consumption patterns
This guide enables you to efficiently retrieve and manage product data using the Nue Lifecycle Management API, supporting both bulk catalog operations and individual product lookups with proper Salesforce ID handling.