Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
finsilabs avatar

Product Information Management

  • 62 installs
  • 41 repo stars
  • Updated March 13, 2026
  • finsilabs/awesome-ecommerce-skills

Centralize product data in a PIM like Akeneo or Salsify and syndicate enriched content to all sales channels automatically.

About

Centralizes product data in a PIM system (Akeneo, Salsify) and syndicates enriched content across all sales channels. A developer uses it to keep product information consistent across many channels.

  • Centralized product data in Akeneo or Salsify
  • Automatic content syndication to all channels

Product Information Management by the numbers

  • 62 all-time installs (skills.sh)
  • Ranked #3,145 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill product-information-management

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs62
repo stars41
Last updatedMarch 13, 2026
Repositoryfinsilabs/awesome-ecommerce-skills

What it does

Centralize product data in a PIM like Akeneo or Salsify and syndicate enriched content to all sales channels automatically.

Files

SKILL.mdMarkdownGitHub ↗

Product Information Management

Overview

A Product Information Management (PIM) system is the single source of truth for product data — names, descriptions, images, attributes, and digital assets — across all channels (website, marketplaces, print catalogs). Akeneo and Salsify are the dominant PIM platforms. This skill covers connecting a PIM as the authoritative source for product enrichment, implementing sync between the PIM and your commerce platform, and building a pipeline that transforms PIM data into channel-specific formats.

When to Use This Skill

  • When product data is inconsistent across your website, marketplace listings, and internal systems
  • When the merchandising team manages product content in a PIM and the commerce platform needs to reflect it
  • When building a new headless storefront that needs a source of enriched product data
  • When adding a new sales channel (marketplace, B2B portal) that needs channel-specific product data
  • When auditing product data quality and identifying missing attributes across the catalog

Core Instructions

Step 1: Determine your platform and PIM integration approach

PlatformPIM Integration OptionWhat It Syncs
ShopifyAkeneo's official Shopify connector (free, Akeneo Marketplace) or Salsify Syndication for ShopifyProduct names, descriptions, images, and attributes → Shopify product metafields; Shopify variants mapped to Akeneo product models
WooCommerceAkeneo WooCommerce Connector (open-source, GitHub) or custom REST API syncProduct data pushed to WooCommerce via the Products REST API; images uploaded to WordPress media library
BigCommerceAkeneo BigCommerce Connector (Akeneo Marketplace) or Salsify for BigCommerceProduct attributes pushed to BigCommerce custom fields and variants; image syndication to BigCommerce CDN
Custom / HeadlessDirect REST API integration with Akeneo or SalsifyFull control over data mapping; incremental sync via updated filter; image upload to your CDN during sync

Step 2: Platform-specific PIM integration

---

Shopify

Connect Akeneo to Shopify using the official connector:

1. In your Akeneo instance, go to Connect → Marketplace and install the Shopify Connector (free, by Akeneo) 2. Configure a Connection in Akeneo (under Connect → Connections) with read permissions for Products, Media files, and Attribute options 3. In the connector settings, map your Akeneo channels/locales to your Shopify markets (e.g., Akeneo en_US scope → Shopify default language) 4. Map Akeneo attribute codes to Shopify fields:

  • name → Shopify product title
  • description → Shopify body HTML
  • price → Shopify variant price
  • Custom attributes → Shopify product metafields (configure under Settings → Custom data in Shopify admin)

5. Run an initial full sync and then schedule incremental syncs — the connector pulls only products with updated > last_sync_at

Verify the sync:

  • Go to a product in your Shopify admin and check that the title, description, and images match what's in Akeneo
  • Check metafields in the Shopify product page under Metafields section

---

WooCommerce

Sync Akeneo to WooCommerce using the REST API:

The open-source Akeneo WooCommerce Connector (available at github.com/akeneo/woocommerce-connector) provides a starting point, but many merchants build a custom sync script:

1. Set up a cron job (daily or hourly) that:

  • Fetches products from Akeneo updated since the last sync using GET /api/rest/v1/products?search={"updated":[{"operator":">","value":"..."}]}
  • Checks if the product exists in WooCommerce using GET /wp-json/wc/v3/products?sku={sku}
  • Creates or updates the WooCommerce product using POST or PUT /wp-json/wc/v3/products/{id}

2. Map Akeneo fields to WooCommerce fields:

  • Akeneo name (en_US) → WooCommerce name
  • Akeneo description → WooCommerce description
  • Akeneo images → Download and upload to WordPress media library, then set as WooCommerce product images
  • Akeneo custom attributes → WooCommerce product attributes or ACF custom fields

3. After each sync, clear WooCommerce's transient cache: wp transient delete-all via WP-CLI to ensure updated products appear immediately

---

BigCommerce

Connect Akeneo using the BigCommerce connector:

1. In Akeneo Marketplace, install the BigCommerce Connector and configure your BigCommerce API credentials (Client ID, Client Secret, Access Token from Advanced Settings → API Accounts) 2. Map Akeneo families to BigCommerce product types in the connector configuration 3. Configure attribute mappings:

  • Akeneo attributes → BigCommerce custom fields or variant option sets
  • Akeneo categories → BigCommerce category tree

4. Schedule regular incremental syncs from the connector settings

---

Custom / Headless

Connect to the Akeneo REST API:

// lib/akeneo/client.ts
export class AkeneoClient {
  private accessToken: string | null = null;
  private tokenExpiry: number = 0;

  constructor(private config: {
    baseUrl: string; clientId: string; clientSecret: string;
    username: string; password: string;
  }) {}

  async getToken(): Promise<string> {
    if (this.accessToken && this.tokenExpiry > Date.now() + 60000) return this.accessToken;

    const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
    const res = await fetch(`${this.config.baseUrl}/api/oauth/v1/token`, {
      method: 'POST',
      headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/json' },
      body: JSON.stringify({ grant_type: 'password', username: this.config.username, password: this.config.password }),
    });

    const data = await res.json();
    this.accessToken = data.access_token;
    this.tokenExpiry = Date.now() + data.expires_in * 1000;
    return this.accessToken!;
  }

  async getAll(path: string): Promise<any[]> {
    const items: any[] = [];
    let nextUrl: string | null = path;

    while (nextUrl) {
      const token = await this.getToken();
      const res = await fetch(`${this.config.baseUrl}${nextUrl}`, {
        headers: { 'Authorization': `Bearer ${token}` },
      });
      const page = await res.json();
      items.push(...(page._embedded?.items ?? []));
      nextUrl = page._links?.next?.href?.replace(this.config.baseUrl, '') ?? null;
    }

    return items;
  }
}

export const akeneo = new AkeneoClient({
  baseUrl: process.env.AKENEO_BASE_URL!,
  clientId: process.env.AKENEO_CLIENT_ID!,
  clientSecret: process.env.AKENEO_CLIENT_SECRET!,
  username: process.env.AKENEO_USERNAME!,
  password: process.env.AKENEO_PASSWORD!,
});

Transform Akeneo's locale/scope-scoped attribute format into a flat storefront product:

// lib/akeneo/product-transformer.ts
export function transformAkeneoProduct(akeneoProduct: any, locale = 'en_US', scope = 'ecommerce') {
  const getValue = (attrCode: string, defaultValue: any = null) => {
    const values = akeneoProduct.values[attrCode] ?? [];
    const match = values.find(v => v.locale === locale && v.scope === scope)
      ?? values.find(v => v.locale === locale && v.scope === null)
      ?? values.find(v => v.locale === null && v.scope === scope)
      ?? values.find(v => v.locale === null && v.scope === null);
    return match?.data ?? defaultValue;
  };

  return {
    sku: akeneoProduct.identifier,
    name: getValue('name', '') as string,
    description: getValue('description', '') as string,
    brand: getValue('brand', '') as string,
    categories: akeneoProduct.categories,
    attributes: {
      color: getValue('color'),
      size: getValue('size'),
      material: getValue('material'),
    },
    enabled: akeneoProduct.enabled,
  };
}

Incremental sync job (fetch only updated products):

// jobs/akeneo-sync.ts
export async function syncAkeneoProducts() {
  const lastSyncAt = await db.syncState.getLastSync('akeneo_products');
  const syncStartTime = new Date();
  const updatedAt = lastSyncAt?.toISOString() ?? '2020-01-01T00:00:00+00:00';

  const products = await akeneo.getAll(
    `/api/rest/v1/products?search={"updated":[{"operator":">","value":"${updatedAt}"}]}&limit=100&with_attribute_options=true`
  );

  let synced = 0, errors = 0;

  for (const akeneoProduct of products) {
    try {
      const storefrontProduct = transformAkeneoProduct(akeneoProduct);
      // Validate required fields before upserting
      if (!storefrontProduct.name) {
        console.warn(`Skipping ${akeneoProduct.identifier}: missing name`);
        continue;
      }
      await db.products.upsert(storefrontProduct.sku, {
        ...storefrontProduct,
        akeneoUpdatedAt: new Date(akeneoProduct.updated),
      });
      synced++;
    } catch (err: any) {
      errors++;
      await db.syncErrors.insert({ productId: akeneoProduct.identifier, error: err.message });
    }
  }

  await db.syncState.updateLastSync('akeneo_products', syncStartTime);
  console.log(`Akeneo sync complete: ${synced} synced, ${errors} errors`);
}

Webhook-triggered sync when Akeneo publishes a product (Akeneo's Event API):

// Register the webhook endpoint in Akeneo under: Connect → Webhooks
// POST /api/webhooks/akeneo
export async function POST(req: NextRequest) {
  const event = await req.json();
  if (event.event_type === 'product.updated' || event.event_type === 'product.created') {
    const sku = event.data.resource.identifier;
    const akeneoProduct = await akeneo.getAll(`/api/rest/v1/products/${sku}?with_attribute_options=true`);
    const storefrontProduct = transformAkeneoProduct(akeneoProduct);
    await db.products.upsert(sku, storefrontProduct);
    // Purge CDN cache for this product's page
    await revalidateProductPage(storefrontProduct.slug);
  }
  return NextResponse.json({ received: true });
}

Best Practices

  • Treat the PIM as the source of truth — never write product content back from commerce to PIM — data flows from PIM to commerce; only push back to PIM for data the PIM explicitly manages (e.g., SEO metadata your platform generates)
  • Use incremental sync, not full sync — fetching all products every 15 minutes is expensive; use Akeneo's updated filter to fetch only changed products
  • Upload images to your own CDN during sync — Akeneo media file URLs are internal API URLs requiring authentication; never serve them directly to customers; upload to S3/R2/Cloudinary during sync
  • Cache attribute options locally — color, size, and material option label lookups change rarely; cache them in Redis and refresh hourly to avoid per-product API calls
  • Validate required attributes before syncing to the storefront — a product without a name or primary image should not be published; add validation before upsert

Common Pitfalls

ProblemSolution
Sync fails on products with missing required attributesWrap each product sync in try/catch; log the product identifier with the error; skip and continue rather than aborting the entire sync
Images not available after syncAkeneo media URLs require authentication; download and re-upload to your CDN during sync — never serve akeneo-base-url/api/rest/v1/media-files/... directly
Akeneo API rate limitsUse batched requests and run syncs off-peak; cache attribute options locally to reduce API calls per product
Category mapping out of sync after PIM reorganizationBuild a category sync job that runs before the product sync; alert when an Akeneo category code has no mapping in your commerce platform
Shopify connector not syncing metafieldsEnsure the metafield namespace and key in the connector configuration match what's configured in Shopify admin → Settings → Custom data → Products

Related Skills

  • @marketplace-connectors
  • @webhook-architecture
  • @erp-integration
  • @analytics-integration

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.