Migrating to Salsify PIM often raises a question: how to transfer a catalog of 50,000 SKU with 200 attributes to 1C-Bitrix without data loss? Manual import via Excel takes up to 3 months and doesn't guarantee integrity. Different attribute structures, nested values, and media files demand a well-thought-out integration. We offer a ready-made solution based on the Salsify REST API, cutting this process down to 1–2 weeks. The integration is designed for bidirectional data flow. Get a free consultation and complexity assessment for your project.
How We Implement Salsify-Bitrix Integration
Salsify is a cloud PIM platform for large-scale e-commerce. Its REST API allows exporting products, attributes, and media files. We connect via token authentication and build asynchronous sync through Bitrix system agents. The agent processes up to 10,000 elements per run, 3 times faster than manual import.
Key Salsify API Endpoints
-
GET products— list of products with attributes, filterable by update date. -
GET products/{product_id}— specific product. -
GET property_groups— attribute groups. -
GET assets?filter[product_id]={id}— product media files. -
GET digital_assets/{id}/download— download file.
Basic authentication:
GET https://app.salsify.com/api/v1/orgs/{org_id}/products
Authorization: Bearer {api_key}
PHP Client for API Interaction
We've prepared a SalsifyClient class that encapsulates HTTP requests, pagination, and error handling. It uses \Bitrix\Main\Web\HttpClient:
class SalsifyClient
{
private string $baseUrl;
private string $apiKey;
public function __construct(string $orgId, string $apiKey)
{
$this->baseUrl = "https://app.salsify.com/api/v1/orgs/{$orgId}/";
$this->apiKey = $apiKey;
}
private function request(string $endpoint, array $params = []): array
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
$http->setHeader('Content-Type', 'application/json');
$url = $this->baseUrl . $endpoint;
if ($params) {
$url .= '?' . http_build_query($params);
}
$response = $http->get($url);
return json_decode($response, true) ?? [];
}
public function getProducts(int $page = 1, int $perPage = 100, ?string $updatedAfter = null): array
{
$params = ['page' => $page, 'per_page' => $perPage];
if ($updatedAfter) {
$params['filter[updated_at][gte]'] = $updatedAfter;
}
return $this->request('products', $params);
}
public function getProperties(): array
{
return $this->request('properties');
}
public function downloadAsset(string $assetId): string
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
return $http->get($this->baseUrl . 'digital_assets/' . $assetId . '/download');
}
}
Attribute Mapping
Salsify stores attributes in a flat structure: each attribute is a "name": "value" pair. We configure mapping in a configuration file, mapping attribute ID or name to infoblock fields and properties:
// /local/config/salsify-mapping.php
return [
'Product Name' => ['target' => 'NAME', 'type' => 'field'],
'Long Description' => ['target' => 'DETAIL_TEXT', 'type' => 'field'],
'Short Description' => ['target' => 'PREVIEW_TEXT', 'type' => 'field'],
'Brand' => ['target' => 'BRAND', 'type' => 'prop'],
'Net Weight (kg)' => ['target' => 'WEIGHT', 'type' => 'prop'],
'Color' => ['target' => 'COLOR', 'type' => 'prop'],
'Country of Origin' => ['target' => 'COUNTRY_ORIGIN','type' => 'prop'],
'GTIN' => ['target' => 'CML2_BAR_CODE', 'type' => 'prop'],
'Manufacturer SKU' => ['target' => 'CML2_ARTICLE', 'type' => 'prop'],
];
Sync Agent
The main agent runs on schedule, fetches products changed since last update, and creates or updates infoblock elements. Asynchronous sync via agents is 3 times faster than manual import through the admin interface.
function syncSalsifyAgent(): string
{
$lastSync = \Bitrix\Main\Config\Option::get('salsify_sync', 'last_run', '');
$client = new SalsifyClient(SALSIFY_ORG_ID, SALSIFY_API_KEY);
$mapping = include '/local/config/salsify-mapping.php';
$page = 1;
$newSync = date('c');
do {
$response = $client->getProducts($page, 100, $lastSync ?: null);
$products = $response['products'] ?? [];
foreach ($products as $product) {
importSalsifyProduct($product, $mapping, $client);
}
$page++;
$meta = $response['meta'] ?? [];
} while (($meta['current_page'] ?? 1) < ($meta['total_pages'] ?? 1));
\Bitrix\Main\Config\Option::set('salsify_sync', 'last_run', $newSync);
return __FUNCTION__ . '();';
}
function importSalsifyProduct(array $product, array $mapping, SalsifyClient $client): void
{
$attributes = $product['attributes'] ?? [];
$sku = $product['salsify:id'];
$getValue = static function (array $attrs, string $key): mixed {
return $attrs[$key] ?? null;
};
$fields = ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'];
$props = [];
foreach ($mapping as $salsifyKey => $config) {
$value = $getValue($attributes, $salsifyKey);
if ($value === null) continue;
if (is_array($value)) {
$value = implode(', ', $value);
}
if ($config['type'] === 'field') {
$fields[$config['target']] = $value;
} else {
$props[$config['target']] = $value;
}
}
$existing = CIBlockElement::GetList(
[], ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
)->Fetch();
$el = new CIBlockElement();
if ($existing) {
$productId = $existing['ID'];
$el->Update($productId, $fields);
} else {
$productId = $el->Add($fields);
}
if ($productId) {
CIBlockElement::SetPropertyValuesEx($productId, CATALOG_IBLOCK_ID, $props);
$primaryImage = $product['salsify:primary_image'] ?? null;
if ($primaryImage) {
importSalsifyAsset($productId, $primaryImage, $client);
}
}
}
Data Transferred from Salsify to Bitrix
Sync covers three data groups:
- Core attributes — name, description, characteristics, GTIN, weight, color, country of origin.
- Media files — images, documents, videos. Two modes supported: full server upload or CDN links.
- Product relationships — hierarchies transferred to infoblock section structure and links.
Prices are usually not synced — they come from 1C. Mapping is customized per catalog.
How to Choose Media Storage Method?
Two approaches: upload files to server or store external CDN links. Comparison:
| Criterion | Server Upload | External CDN Links |
|---|---|---|
| File control | Full | Depends on provider |
| Disk space | Required | Not required |
| Page load speed | Server-dependent | Faster (CDN) |
| Broken link risk | Minimal | Possible if URL changes |
| Server load | High (downloading) | Low |
For catalogs up to 10,000 SKU, uploading is often chosen; for large projects, CDN.
How to Configure Attribute Mapping?
Mapping is key. For each Salsify attribute, we specify which infoblock field or property it should go to. Multiple values, directories, and highload-block bindings are supported. We provide a configuration file easily editable without code changes. If a Salsify attribute has a complex type (e.g., enum with nested fields), it's preprocessed using custom converter functions.
Example of complex attribute mapping
Suppose Salsify has a "Specifications" attribute with nested fields: "Weight", "Dimensions", "Material". Mapping converts it into multiple infoblock properties: WEIGHT, DIMENSIONS, MATERIAL. A custom converter parses the JSON structure and writes values to corresponding fields.
Process Overview
- Data structure analysis — examine mapping, identify complex attributes.
- Develop Salsify client — handle pagination, error handling, API limits.
- Configure mapping — for infoblock fields and properties, including multiple and directory types.
- Implement sync agent — support delta and webhooks (optional).
- Transfer media — choose strategy (download or external links).
- Test and debug — on test catalog, then production.
- Documentation and handover — architecture description, operation manual.
Work Deliverables
- Architecture documentation and integration diagram.
- Source code with comments in repository.
- Agent monitoring and alert setup.
- Team training (1–2 sessions).
- 1-month warranty support after launch.
Implementation Timelines
| Scope | Components | Timeline |
|---|---|---|
| Up to 5,000 SKU, basic content | Client + mapping + agent | 1–2 weeks |
| 10,000–100,000 SKU + media + webhooks | + complex attribute handling + optimization | 3–4 weeks |
| Multi-region catalog (different prices, regional content) | + Salsify channel logic + Bitrix multisite | 5–7 weeks |
Common Integration Mistakes
- Ignoring complex attributes (enum with nested structure) — leads to data loss.
- No duplicate SKU handling — creates duplicate items.
- Syncing prices from Salsify without coordination — Salsify is not designed for prices; better to source from 1C.
- Full media download — overloads server. We recommend external URLs.
We are certified Bitrix specialists with over 7 years of experience. During this time, we have completed 50+ integration projects with external systems, including PIM, ERP, and marketplaces. Our solutions run stably with catalogs up to 100,000 SKU. Contact us for a preliminary complexity and timeline assessment. Order a free consultation — we will detail the integration process and answer your questions.
Learn more about Salsify API capabilities in the official documentation: Salsify API Guide.







