Skip to content

Product Catalog Sync

Synchronize your product catalog with Wildberries.

Description

This use case demonstrates how to manage your product catalog on Wildberries, including listing products, updating information, and syncing with external systems.

📖 For detailed guide on fetching product cards, see Working with Product Cards Guide

Get All Products

typescript
import { WildberriesSDK } from 'daytona-wildberries-typescript-sdk';

const sdk = new WildberriesSDK({
  apiKey: process.env.WB_API_KEY!
});

// Get all products with pagination
async function getAllProducts() {
  const allCards = [];
  let cursor: any = { limit: 100 };

  while (true) {
    const response = await sdk.products.createCardsList({
      settings: {
        filter: { withPhoto: -1 },  // All cards
        cursor
      }
    }, { locale: 'ru' });

    if (response.cards) {
      allCards.push(...response.cards);
    }

    if ((response.cards?.length ?? 0) < 100 || !response.cursor?.updatedAt) {
      break;
    }

    cursor = {
      limit: 100,
      updatedAt: response.cursor.updatedAt,
      nmID: response.cursor.nmID
    };

    await new Promise(resolve => setTimeout(resolve, 650));
  }

  console.log(`Total products: ${allCards.length}`);
  return allCards;
}

const products = await getAllProducts();

Get Products with Filtering

typescript
// Get products with specific filters
async function getProductsFiltered() {
  const response = await sdk.products.createCardsList({
    settings: {
      filter: {
        withPhoto: 1,  // Only with photos
        brands: ['MyBrand']
      },
      cursor: { limit: 100 }
    }
  }, { locale: 'ru' });

  return response.cards ?? [];
}

Get Categories Structure

typescript
interface CategoryTree {
  id: number;
  name: string;
  children: CategoryTree[];
}

async function getCategoryTree(): Promise<CategoryTree[]> {
  // Get parent categories
  const parents = await sdk.products.getParentAll();

  const tree: CategoryTree[] = [];

  for (const parent of parents.data ?? []) {
    // Get child categories for each parent
    const children = await sdk.products.getObjectAll({
      parentID: parent.id
    });

    tree.push({
      id: parent.id,
      name: parent.name,
      children: children.data?.map(child => ({
        id: child.objectID,
        name: child.objectName,
        children: []
      })) ?? []
    });
  }

  return tree;
}

Get Product Characteristics

typescript
// Get required characteristics for a category
async function getCategoryCharacteristics(categoryId: number) {
  const characteristics = await sdk.products.getObjectCharc(categoryId);

  const required = characteristics.data?.filter(c => c.required);
  const optional = characteristics.data?.filter(c => !c.required);

  console.log(`Category ${categoryId}:`);
  console.log(`  Required: ${required?.length ?? 0} characteristics`);
  console.log(`  Optional: ${optional?.length ?? 0} characteristics`);

  return { required, optional };
}

Sync Products from External System

typescript
interface ExternalProduct {
  sku: string;
  name: string;
  description: string;
  price: number;
  stock: number;
  barcode: string;
}

async function syncFromExternalSystem(externalProducts: ExternalProduct[]) {
  // 1. Get existing WB products (using getAllProducts helper from above)
  const wbProducts = await getAllProducts();

  // 2. Create lookup map
  const wbProductMap = new Map(
    wbProducts.map(p => [p.vendorCode, p])
  );

  // 3. Identify products to update
  const updates: { nmId: number; price: number }[] = [];
  const stockUpdates: { sku: string; amount: number }[] = [];

  for (const ext of externalProducts) {
    const wbProduct = wbProductMap.get(ext.sku);

    if (wbProduct) {
      // Check if price needs update
      if (wbProduct.sizes?.[0]?.price !== ext.price) {
        updates.push({
          nmId: wbProduct.nmID,
          price: ext.price
        });
      }

      // Always sync stock
      stockUpdates.push({
        sku: ext.barcode,
        amount: ext.stock
      });
    }
  }

  // 4. Apply updates
  if (updates.length > 0) {
    await sdk.products.updatePrices(updates);
    console.log(`Updated prices for ${updates.length} products`);
  }

  console.log(`Sync complete: ${updates.length} price updates`);
}

Export Product Catalog

typescript
interface CatalogExport {
  nmId: number;
  vendorCode: string;
  title: string;
  brand: string;
  category: string;
  price: number;
  discountPrice?: number;
  photos: string[];
  barcode: string;
  stock: number;
}

async function exportCatalog(): Promise<CatalogExport[]> {
  // Get all products (using getAllProducts helper from above)
  const products = await getAllProducts();

  // Get stock levels
  const stocks = await sdk.reports.getStocks(
    new Date().toISOString().split('T')[0]
  );

  const stockMap = new Map(
    stocks.map(s => [s.nmId, s.quantity])
  );

  // Build export
  return products.map(p => ({
    nmId: p.nmID,
    vendorCode: p.vendorCode ?? '',
    title: p.title ?? '',
    brand: p.brand ?? '',
    category: p.subjectName ?? '',
    price: p.sizes?.[0]?.price ?? 0,
    discountPrice: p.sizes?.[0]?.discountedPrice,
    photos: p.photos?.map(ph => ph.big ?? '') ?? [],
    barcode: p.sizes?.[0]?.skus?.[0] ?? '',
    stock: stockMap.get(p.nmID) ?? 0
  }));
}

// Usage
const catalog = await exportCatalog();
console.log(`Exported ${catalog.length} products`);

// Save to JSON
import { writeFileSync } from 'fs';
writeFileSync('catalog.json', JSON.stringify(catalog, null, 2));

Monitor Product Status

typescript
async function checkProductHealth() {
  // Get all products (using getAllProducts helper from above)
  const products = await getAllProducts();
  const stocks = await sdk.reports.getStocks(
    new Date().toISOString().split('T')[0]
  );

  const stockMap = new Map(stocks.map(s => [s.nmId, s.quantity]));

  const issues = {
    outOfStock: [] as number[],
    lowStock: [] as number[],
    noPhotos: [] as number[],
    noPrice: [] as number[]
  };

  for (const product of products) {
    const stock = stockMap.get(product.nmID) ?? 0;

    if (stock === 0) {
      issues.outOfStock.push(product.nmID);
    } else if (stock < 5) {
      issues.lowStock.push(product.nmID);
    }

    if (!product.photos?.length) {
      issues.noPhotos.push(product.nmID);
    }

    if (!product.sizes?.[0]?.price) {
      issues.noPrice.push(product.nmID);
    }
  }

  console.log('Product Health Report:');
  console.log(`  Out of stock: ${issues.outOfStock.length}`);
  console.log(`  Low stock (<5): ${issues.lowStock.length}`);
  console.log(`  No photos: ${issues.noPhotos.length}`);
  console.log(`  No price: ${issues.noPrice.length}`);

  return issues;
}

Back to Examples | Next: Pricing Updates

Made with ❤️ for the Wildberries developer community