Akeneo Magento and Shopify connectors are the most common Akeneo integration requests we see—and the easiest place for silent catalog drift. This guide covers the patterns Spygar uses so storefronts stay aligned with PIM truth, including the real REST calls from the official Akeneo API reference index.
A connector is a product contract—if ownership is fuzzy, every sync becomes a conflict.
Ownership first
Akeneo should own marketing content, structured attributes, categories (as modeled), associations, and assets. Magento/Shopify typically own price, inventory, and orderable status. Write the RACI before mapping fields. Ambiguity here is the root of most “sync bugs.”
Authenticate the connector
Every Magento/Shopify sync worker needs a short-lived Bearer token from POST /api/oauth/v1/token. Store client id/secret and connection credentials in a secrets manager—never in the storefront.
/api/oauth/v1/token
Official docs
Get an authentication token
Exchange connection credentials for a Bearer access token. No prior Bearer auth is required for this call.
Headers
Content-Type: application/json (or application/x-www-form-urlencoded)Authorization: Basic {base64(client_id:client_secret)}
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
username |
body | string | Yes | PIM connection username |
password |
body | string | Yes | PIM connection password |
grant_type |
body | string | Yes | Must be password |
Example request
curl -X POST "https://YOUR_PIM_HOST/api/oauth/v1/token" \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'CLIENT_ID:CLIENT_SECRET' | base64)" \
-d '{
"username": "your_connection_username",
"password": "your_connection_password",
"grant_type": "password"
}'
Response — 200 OK
Returns an authentication token used as Authorization: Bearer on later calls.
{
"access_token": "ZTZmYjU4ZmQxZWNmMzk1M2NlYzA5NmFhNmIzVjExMzE4NmJmODBkZGIyYTliYmQyNjk2ZDQwZThmNjdiZDQzOQ",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token": "M3FlODI0OTE3ODMyNjViMzRiOWE5ODMyNWViMThkNDU5YzJjNjFiZjNkZWFjMzIyYjc4YTgzZWY1MjE5ZTY5Mw"
}
Response — 400 Bad Request
Malformed JSON or invalid request framing.
{
"code": 400,
"message": "Invalid JSON message received"
}
Response — 422 Unprocessable Entity
Validation failed (wrong grant_type, bad credentials shape, etc.).
{
"code": 422,
"message": "Property \"grant_type\" expects a valid grant type. Check the expected format on the API documentation."
}
Pull changed products from Akeneo
Use GET /api/rest/v1/products-uuid with pagination_type=search_after, channel scope, target locales, and with_completenesses=true. Filter on updated for incremental sync. Full parameter and response shapes are documented by Akeneo here.
/api/rest/v1/products-uuid
Official docs
Get list of products (UUID)
Preferred product list endpoint. Paginated, filterable, and permission-aware. Prefer pagination_type=search_after for large catalogs.
Headers
Authorization: Bearer {access_token}Accept: application/json
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
search |
query | string (JSON) | No | Filter products (see Filters docs) |
scope |
query | string | No | Return scopable values for this channel (+ non-scopable) |
locales |
query | string | No | Comma-separated locales for localizable values |
attributes |
query | string | No | Comma-separated attribute codes to return |
pagination_type |
query | string | No | page (default) or search_after |
page |
query | integer | No | Page number when pagination_type=page (default 1) |
search_after |
query | string | No | Cursor for search_after pagination (do not invent manually) |
limit |
query | integer | No | Page size (default 10) |
search_scope |
query | string | No | Default scope for multi-attribute filters |
search_locale |
query | string | No | Default locale for multi-attribute filters |
with_count |
query | boolean | No | Include items_count (can be expensive) |
with_attribute_options |
query | boolean | No | Include option labels (linked_data) |
with_quality_scores |
query | boolean | No | Include quality scores |
with_completenesses |
query | boolean | No | Include completenesses |
with_root_parent |
query | boolean | No | Include root parent model code for variants |
Example request
curl -G "https://YOUR_PIM_HOST/api/rest/v1/products-uuid" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Accept: application/json" \
--data-urlencode 'pagination_type=search_after' \
--data-urlencode 'limit=20' \
--data-urlencode 'scope=ecommerce' \
--data-urlencode 'locales=en_US,fr_FR' \
--data-urlencode 'attributes=sku,name,description' \
--data-urlencode 'with_completenesses=true' \
--data-urlencode 'search={"updated":[{"operator":">","value":"2026-01-01 00:00:00"}]}'
Response — 200 OK
Paginated product collection with navigation links.
{
"_links": {
"self": {
"href": "https://YOUR_PIM_HOST/api/rest/v1/products-uuid?pagination_type=search_after&limit=20"
},
"first": {
"href": "https://YOUR_PIM_HOST/api/rest/v1/products-uuid?pagination_type=search_after&limit=20"
},
"next": {
"href": "https://YOUR_PIM_HOST/api/rest/v1/products-uuid?pagination_type=search_after&search_after=CURSOR&limit=20"
}
},
"_embedded": {
"items": [
{
"_links": {
"self": {
"href": "https://YOUR_PIM_HOST/api/rest/v1/products-uuid/25566245-55c3-42ce-86d9-8610ac459fa8"
}
},
"uuid": "25566245-55c3-42ce-86d9-8610ac459fa8",
"enabled": true,
"family": "tshirt",
"categories": ["summer_collection"],
"groups": [],
"parent": null,
"values": {
"sku": [{"data": "top", "locale": null, "scope": null}],
"name": [
{"data": "Top", "locale": "en_US", "scope": null},
{"data": "Débardeur", "locale": "fr_FR", "scope": null}
]
},
"created": "2026-01-05T10:00:00+00:00",
"updated": "2026-03-01T12:30:00+00:00",
"completenesses": [
{"scope": "ecommerce", "locale": "en_US", "data": 100}
]
}
]
}
}
Response — 401 Unauthorized
Missing/expired token or invalid credentials.
{
"code": 401,
"message": "Authentication is required"
}
Response — 422 Unprocessable Entity
Invalid search JSON or unsupported query combination.
{
"code": 422,
"message": "Property \"search\" expects a valid search. Check the expected format on the API documentation."
}
Variant mapping is the hard part
Akeneo product models must translate cleanly into Magento configurables or Shopify variants. Wrong axes create duplicate PDPs or missing options. Prototype with your hardest family and verify storefront UX before scaling the connector.
- Stable external IDs: prefer Akeneo UUID as the immutable key
- Incremental upserts with idempotent writers into Magento/Shopify
- Separate structure sync from content/media sync when helpful
- Canary SKUs that detect silent freezes
Media and completeness gates
Do not publish products missing required hero/gallery roles. Upload or relink media via POST /api/rest/v1/media-files when Akeneo is the asset source, and only push storefronts when completeness thresholds pass.
/api/rest/v1/media-files
Official docs
Create a new product media file
Upload a binary and associate it to a product (or product model) attribute value via multipart form-data.
Headers
Authorization: Bearer {access_token}Content-Type: multipart/form-data (with boundary)
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
product |
form | string (JSON) | No | {"identifier":"...","attribute":"...","scope":"...","locale":"..."} — use product OR product_model, not both |
product_model |
form | string (JSON) | No | Same shape targeting a product model |
file |
form | binary | Yes | Media binary |
Example request
curl -X POST "https://YOUR_PIM_HOST/api/rest/v1/media-files" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-F 'product={"identifier":"top","attribute":"image","scope":null,"locale":null}' \
-F "file=@./hero-top.jpg;type=image/jpeg"
Response — 201 Created
Media created and linked. Location header contains the media resource URI.
Headers:
Location: /api/rest/v1/media-files/f/i/l/e/hash_hero-top.jpg
Body: (empty)
Response — 422 Unprocessable Entity
Invalid product JSON, unknown attribute, or file validation failure.
{
"code": 422,
"message": "Validation failed.",
"errors": [
{
"property": "product",
"message": "The product does not exist."
}
]
}
Push content updates back? Prefer PATCH upserts
When ERP or enrichment tools write into Akeneo (not the storefront), use PATCH /api/rest/v1/products-uuid/{uuid} for idempotent upserts—including add_categories / remove_categories when you must not clobber taxonomy.
/api/rest/v1/products-uuid/{uuid}
Official docs
Update/create a product (UUID)
Upsert by UUID: updates if present, creates if missing. Supports add_categories / remove_categories. EE may create a draft when rights are limited.
Headers
Authorization: Bearer {access_token}Content-Type: application/json
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
uuid |
path | string | Yes | Product UUID |
create_missing_options |
query | string | No | Auto-create missing select options when allowed |
update_parent_values |
query | boolean | No | When true, allow updating parent model values while patching a variant |
enabled |
body | boolean | No | Enable/disable product |
family |
body | string|null | No | Family code |
categories |
body | array[string] | No | Replace category set |
add_categories |
body | array[string] | No | Add categories (preserve existing) |
remove_categories |
body | array[string] | No | Remove categories (preserve others) |
parent |
body | string|null | No | Parent product model |
values |
body | object | No | Partial attribute values to merge |
Example request
curl -X PATCH "https://YOUR_PIM_HOST/api/rest/v1/products-uuid/25566245-55c3-42ce-86d9-8610ac459fa8" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"add_categories": ["winter_collection"],
"values": {
"name": [
{"data": "Top — refreshed", "locale": "en_US", "scope": null}
],
"description": [
{"data": "Updated summer top copy", "locale": "en_US", "scope": "ecommerce"}
]
}
}'
Response — 204 No Content
Product updated successfully (empty body).
Body: (empty)
Response — 201 Created
No product existed for UUID; product was created. Location header returned.
Headers:
Location: /api/rest/v1/products-uuid/25566245-55c3-42ce-86d9-8610ac459fa8
Body: (empty)
Response — 422 Unprocessable Entity
Validation failed on patched payload.
{
"code": 422,
"message": "Validation failed.",
"errors": [
{
"property": "values",
"message": "The name attribute requires a value in the locale en_US."
}
]
}
Failure handling humans can use
Retries, dead-letter queues, and UUID-level messages beat “check the logs.” Add a reconciliation job that re-lists via products-uuid periodically. For a full integration engagement—including ERP and marketplaces—see our Akeneo integration services and the deeper Akeneo API integration guide.
Build vs buy
Evaluate existing connectors when your model is standard. Build or extend when transforms, volume, or variant complexity exceed what packages handle cleanly—using Akeneo’s job/step pattern documented in How to create a custom Akeneo connector (official guide: create-connector.html). Either way, treat the connector as a supported product with monitoring and a runbook—not a weekend script.