DeveloperFebruary 2025 · 8 min read

Akeneo API Authentication: OAuth2 Setup Guide for Developers

Before you can export a single product from Akeneo, you need a working OAuth2 connection. This guide covers everything: creating API credentials, making the token request, handling expiry, and avoiding common authentication mistakes.

How Akeneo API authentication works

Akeneo uses OAuth2 with the Resource Owner Password Credentials grant (also called "password flow"). Unlike typical OAuth2 web flows with redirect URLs, this flow is designed for server-to-server integrations:

1
You send: Client ID + Client Secret + Akeneo username + Akeneo password → /api/oauth/v1/token
2
Akeneo returns: access_token (valid 1 hour) + refresh capability
3
You use: Authorization: Bearer {token} in all subsequent API calls
4
Token expires: Request a new token using the same credentials — there is no refresh_token in password flow
Important: Akeneo's OAuth2 is not the standard OAuth2 authorization code flow with redirect URIs. There is no "login with Akeneo" popup — this is pure API-to-API authentication. The "username" and "password" are your Akeneo PIM user credentials.

Step 1 — Create an API connection in Akeneo

Log into your Akeneo PIM and navigate to System → API Connections. If you don't see this menu, you need the ROLE_ADMINISTRATOR role.

  1. 1.Click Create
  2. 2.Enter a label: SyncPIM Export (or any name)
  3. 3.Akeneo generates a Client ID and Client Secret
  4. 4.Copy the Client Secret immediately — it's shown only once. If you miss it, regenerate it.

Client ID: 2_abc123def456ghi789

Client Secret: 7j8k9l0m1n2o3p4q5r6s7t8u9v0w1x2y3z4

Step 2 — Request an access token

Make a POST request to /api/oauth/v1/token. The credentials are sent as Basic Auth (base64-encoded client_id:client_secret) and the body is JSON:

# Using curl:
curl -X POST https://your-akeneo.com/api/oauth/v1/token \
  -H "Content-Type: application/json" \
  -u "2_abc123def456ghi789:7j8k9l0m1n2o3p4q5r6s7t8u9v0w1x2y3z4" \
  -d '{
    "username": "your_akeneo_username",
    "password": "your_akeneo_password",
    "grant_type": "password"
  }'

# Response:
{
  "access_token": "NzY4MDM4OGE4MjZjYTRiOGQ0YTJlYmI3...",
  "expires_in": 3600,
  "token_type": "bearer",
  "scope": null
}

The same request in Python:

import requests
from base64 import b64encode

def get_akeneo_token(base_url, client_id, client_secret, username, password):
    credentials = b64encode(f"{client_id}:{client_secret}".encode()).decode()

    response = requests.post(
        f"{base_url}/api/oauth/v1/token",
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Basic {credentials}"
        },
        json={
            "username": username,
            "password": password,
            "grant_type": "password"
        }
    )
    response.raise_for_status()
    return response.json()["access_token"]

token = get_akeneo_token(
    "https://your-akeneo.com",
    "2_abc123def456ghi789",
    "7j8k9l0m...",
    "admin",
    "your_password"
)

Step 3 — Use the token in API requests

Include the token in the Authorization header as a Bearer token:

import requests

def get_products(base_url, token, page=1, limit=100):
    response = requests.get(
        f"{base_url}/api/rest/v1/products",
        headers={"Authorization": f"Bearer {token}"},
        params={"page": page, "limit": limit, "with_count": "true"}
    )
    response.raise_for_status()
    return response.json()

# First page
data = get_products("https://your-akeneo.com", token)
print(f"Total products: {data['items_count']}")
# → Total products: 4283

Handling token expiry (1-hour TTL)

Akeneo access tokens expire after 3600 seconds (1 hour). For long-running exports, you need to detect expiry and re-authenticate. The API returns HTTP 401 when the token is expired:

import time

class AkeneoClient:
    def __init__(self, base_url, client_id, client_secret, username, password):
        self.base_url = base_url
        self.creds = (client_id, client_secret, username, password)
        self.token = None
        self.token_expires_at = 0

    def get_token(self):
        # Refresh 5 minutes before expiry
        if time.time() < self.token_expires_at - 300:
            return self.token

        self.token = get_akeneo_token(self.base_url, *self.creds)
        self.token_expires_at = time.time() + 3600
        return self.token

    def request(self, path, params=None):
        response = requests.get(
            f"{self.base_url}{path}",
            headers={"Authorization": f"Bearer {self.get_token()}"},
            params=params
        )
        # If 401, force token refresh and retry once
        if response.status_code == 401:
            self.token_expires_at = 0
            response = requests.get(
                f"{self.base_url}{path}",
                headers={"Authorization": f"Bearer {self.get_token()}"},
                params=params
            )
        response.raise_for_status()
        return response.json()

Akeneo API rate limits

Akeneo does not publish official rate limits, but in practice:

Akeneo Serenity (Cloud)

~30 requests/second sustained, higher bursts tolerated

Rate limits vary by plan — contact Akeneo support for specifics

Akeneo Enterprise Edition (self-hosted)

Determined by your server capacity — no API-level throttle by default

Server performance (CPU/RAM) is the limiting factor

Akeneo Growth Edition

~10–20 requests/second in our testing

Use limit=100 per page and avoid parallel requests

# Safe pagination pattern: use limit=100, add small delay on 429
import time

def paginate_products(client, delay=0.1):
    page = 1
    while True:
        data = client.request("/api/rest/v1/products",
                               params={"page": page, "limit": 100})
        items = data.get("_embedded", {}).get("items", [])
        if not items:
            break
        yield from items

        # Check if there's a next page
        next_link = data.get("_links", {}).get("next")
        if not next_link:
            break

        page += 1
        time.sleep(delay)  # polite delay

Common authentication errors and fixes

HTTP 401 — invalid_client

Cause: Wrong Client ID or Client Secret. Double-check for trailing spaces when copy-pasting.

Fix: Regenerate the client secret in Akeneo admin and copy it again.

HTTP 400 — invalid_grant

Cause: Wrong username or password. The Akeneo user account may be locked or have wrong permissions.

Fix: Try logging into the Akeneo UI with the same credentials. If locked, unlock from System → Users.

HTTP 401 — expired token

Cause: Token TTL (3600s) exceeded. Common in long-running exports.

Fix: Implement token refresh logic (re-request token before expiry as shown above).

HTTP 403 — access denied

Cause: The API user doesn't have permission to access the requested resource (e.g., products in a channel they can't see).

Fix: Check the user's role and category/channel permissions in Akeneo admin.

Connection refused / SSL error

Cause: Wrong base URL format. Akeneo URL should not have a trailing slash and must use HTTPS.

Fix: Use https://your-akeneo.com (no trailing slash, no /api path in the base URL).

Skip OAuth2 complexity with SyncPIM

SyncPIM handles OAuth2 authentication for you. You paste your Akeneo URL, Client ID, Client Secret, username, and password once in the connection wizard — SyncPIM manages token refresh, rate limiting, and pagination automatically.

You never write authentication code. SyncPIM connects to your Akeneo instance using the same credentials, then exports products to your database on your schedule.

Connect Akeneo in 2 minutes

Paste your credentials once. SyncPIM handles OAuth2, token refresh, and pagination for every export.

Related