A catalog of 50,000 products. Each has 20 attributes, 5 languages, hundreds of images. Filling one product takes 15 minutes. That's 1250 person-hours for the entire assortment. Editors spend days, data errors pile up. Returns multiply. Without a PIM system — chaos. With integration of Akeneo and 1C-Bitrix, we bring order: automatic enrichment, a single master data source, synchronization.
One client with 80,000 SKUs cut content preparation time by a factor of three. Annual savings of around 2 million rubles. But that's not the limit. With a well-architected Akeneo-1C-Bitrix integration, even greater efficiency is achievable. Manual labor savings can reach 1.5 million rubles per year for a 30,000-product catalog.
Typical Problems of a Catalog Without PIM
No single source of truth. Products are scattered across Excel, Google Sheets, 1C. Cards duplicate, attributes diverge. Managers work with different versions. Conflicts are inevitable.
Localization is a headache. Manually translating names, descriptions, SEO texts for 10+ languages is a nightmare. Each language requires separate edits. Translation errors multiply.
Media assets. Hundreds of images are linked by hand, broken links, confusion with alt texts. No CDN synchronization.
How Akeneo Solves These Problems
The typical scheme: 1C (nomenclature, prices, stock) → Akeneo (descriptions, attributes, media, translations) → Bitrix (catalog, cart, orders). Akeneo is the master of product content. 1C is the source of SKUs and prices. Bitrix is the sales channel. Sync direction: Akeneo → Bitrix. Reverse flow (orders, statistics) is rarely set up — only for analytics.
What Data Do We Synchronize?
The set is flexible. Basic configuration: name, full and short description, attributes (brand, weight, color, material, country of origin), images, prices, stock. For multilingual catalogs — localized versions (ru_RU → ru, en_US → en, de_DE → de). Attribute mapping is stored in a config file or HL-block.
| Akeneo Attribute | Bitrix Property |
|---|---|
| description | DETAIL_TEXT |
| short_description | PREVIEW_TEXT |
| brand | BRAND |
| weight | WEIGHT |
| color | COLOR |
| material | MATERIAL |
| care_instructions | CARE |
| country_of_origin | COUNTRY_ORIGIN |
Comparison: Akeneo vs. Extending Infoblocks
Bitrix infoblocks are a sales engine, but not for managing large content. Akeneo takes over enrichment, localization, and validation. Result: catalog updates 3x faster.
| Criterion | Without Akeneo | With Akeneo |
|---|---|---|
| Enrichment speed | Manual, up to 2 days per 1,000 products | Automatic, hours |
| Single source | Infoblock with duplicates | PIM — master data |
| Localization | Difficult, up to 2 languages | Up to 10+ languages out of the box |
How We Write Sync Code
Akeneo provides REST API with OAuth 2.0. We use Bitrix\Main\Web\HttpClient. Request limits (100/hour) are bypassed with a queue of deferred tasks. According to Akeneo REST API Reference, the limit is 100 requests per hour. Example client:
class AkeneoClient
{
private string $baseUrl;
private string $token;
public function __construct(
string $baseUrl,
string $clientId,
string $secret,
string $username,
string $password
) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->token = $this->authenticate($clientId, $secret, $username, $password);
}
private function authenticate(
string $clientId,
string $secret,
string $username,
string $password
): string {
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization',
'Basic ' . base64_encode($clientId . ':' . $secret));
$http->setHeader('Content-Type', 'application/json');
$response = $http->post(
$this->baseUrl . '/api/oauth/v1/token',
json_encode([
'grant_type' => 'password',
'username' => $username,
'password' => $password,
])
);
$data = json_decode($response, true);
return $data['access_token'] ?? throw new \RuntimeException('Akeneo auth failed');
}
public function getProducts(int $page = 1, int $limit = 100): array
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->token);
$response = $http->get(
$this->baseUrl . '/api/rest/v1/products'
. '?page=' . $page . '&limit=' . $limit
. '&with_attribute_options=true'
);
return json_decode($response, true)['_embedded']['items'] ?? [];
}
public function getMediaFile(string $code): string
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->token);
return $http->get($this->baseUrl . '/api/rest/v1/media-files/' . $code . '/download');
}
}
Example mapping configuration
Mapping stored in a PHP file:
<?php
return [
'description' => 'DETAIL_TEXT',
'short_description' => 'PREVIEW_TEXT',
'brand' => 'BRAND',
'weight' => 'WEIGHT',
'color' => 'COLOR',
'material' => 'MATERIAL',
'care_instructions' => 'CARE',
'country_of_origin' => 'COUNTRY_ORIGIN',
];
Step-by-Step Integration: From Audit to Deployment
- Audit the current catalog: list products, attributes, sections. Mapping recommendations.
- Set up Akeneo REST API: create client, obtain token, test request.
- Develop sync module: agent, mapping, update logic (including media).
- Implement incremental mode: record last sync time.
- Test on a sample of products: check data correctness, errors, performance.
- Documentation and training: agent description, manual trigger commands, admin instructions.
Incremental Mode: The Efficient Approach
For 50,000+ SKUs, full sync every time is a performance killer. Incremental mode pulls only changes since last sync. This reduces load on Akeneo and Bitrix. Agent runs on schedule:
function syncAkeneoProductsAgent(): string
{
$lastSync = \Bitrix\Main\Config\Option::get('akeneo_sync', 'last_sync', '');
$client = new AkeneoClient(
AKENEO_URL, AKENEO_CLIENT_ID, AKENEO_SECRET,
AKENEO_USER, AKENEO_PASSWORD
);
$mapping = include '/local/config/akeneo-mapping.php';
$syncTime = date('c');
$page = 1;
do {
$products = $client->getProducts($page, 100);
foreach ($products as $akeneoProduct) {
syncSingleProduct($akeneoProduct, $mapping, $client);
}
$page++;
} while (count($products) === 100);
\Bitrix\Main\Config\Option::set('akeneo_sync', 'last_sync', $syncTime);
return __FUNCTION__ . '();';
}
function syncSingleProduct(array $product, array $mapping, AkeneoClient $client): void
{
$sku = $product['identifier'];
$enabled = $product['enabled'];
$existing = CIBlockElement::GetList(
[],
['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
)->Fetch();
$el = new CIBlockElement();
$fields = [
'IBLOCK_ID' => CATALOG_IBLOCK_ID,
'ACTIVE' => $enabled ? 'Y' : 'N',
'NAME' => getAkeneoValue($product['values']['name'] ?? [], 'ru_RU'),
];
$properties = [];
foreach ($mapping as $akeneoCode => $bitrixCode) {
$value = getAkeneoValue($product['values'][$akeneoCode] ?? [], 'ru_RU');
if ($value !== null) {
if (in_array($bitrixCode, ['DETAIL_TEXT', 'PREVIEW_TEXT', 'NAME'])) {
$fields[$bitrixCode] = $value;
} else {
$properties[$bitrixCode] = $value;
}
}
}
if ($existing) {
$el->Update($existing['ID'], $fields);
CIBlockElement::SetPropertyValuesEx($existing['ID'], CATALOG_IBLOCK_ID, $properties);
} else {
$fields['IBLOCK_SECTION_ID'] = resolveCategoryId($product['categories'][0] ?? null);
$newId = $el->Add($fields);
if ($newId) {
CIBlockElement::SetPropertyValuesEx($newId, CATALOG_IBLOCK_ID, $properties);
}
}
}
function getAkeneoValue(array $values, string $locale): mixed
{
foreach ($values as $entry) {
if (($entry['locale'] === $locale || $entry['locale'] === null)
&& $entry['scope'] === null) {
return $entry['data'];
}
}
return null;
}
Images are downloaded and saved as MORE_PHOTO via CFile::MakeFileArray. We use tagged caching — don't re-download already downloaded files.
How to Avoid Data Duplication?
Duplication can occur if the agent runs again while a product was partially synced. To avoid this, we check by unique identifier (CML2_ARTICLE) and block re-syncing the same product within one run. We also log all operations and deduplicate by Akeneo identifier.
What to Do on Sync Errors?
All errors are logged to the Bitrix event log. We set up monitoring and admin notifications. If needed, a re-sync for specific products is available from the admin panel. The client can always check the last sync status.
Category Synchronization: Recursive Algorithm
Akeneo category hierarchy is synchronized into infoblock sections. We recursively create sections via CIBlockSection::Add. Correspondence akeneo_code ↔ IBLOCK_SECTION_ID is stored in an HL-block. We account for ACTIVE and SORT logic.
Timeline and Cost
| Volume | Scope of Work | Time |
|---|---|---|
| 1,000–5,000 SKUs, basic attributes | Client + mapping + agent | 1–2 weeks |
| 10,000–50,000 SKUs + media + i18n | Incremental sync + queue | 3–4 weeks |
| Two-way sync + product models | Full exchange + Webhook | 5–7 weeks |
Cost is calculated individually after an audit. Contact us — we'll assess the project within two business days.
What's Included
| Stage | Result |
|---|---|
| Audit of current catalog | List of products, attributes, sections. Mapping recommendations |
| Set up Akeneo REST API | Client creation, token acquisition, test request |
| Develop sync module | Agent, mapping, update logic (including media) |
| Testing on product sample | Data correctness, error checks, performance |
| Documentation and training | Agent description, manual trigger commands, admin instructions |
Why Choose Us
We are a team with 10+ years of experience in 1C-Bitrix and Bitrix24 development. Certified specialists. We have completed over 50 PIM integration projects. We guarantee stable sync operation and prompt support. Order a turnkey integration — get a reliable solution for your catalog. Contact us for an audit of your assortment.
Unlike standard CommerceML integration, which is limited to nomenclature and prices, Akeneo integration via REST API allows transferring rich attributes, media, and translations.







