OpenAkeneo.RestApiClient 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package OpenAkeneo.RestApiClient --version 0.1.0
                    
NuGet\Install-Package OpenAkeneo.RestApiClient -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenAkeneo.RestApiClient" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenAkeneo.RestApiClient" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="OpenAkeneo.RestApiClient" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpenAkeneo.RestApiClient --version 0.1.0
                    
#r "nuget: OpenAkeneo.RestApiClient, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpenAkeneo.RestApiClient@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpenAkeneo.RestApiClient&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=OpenAkeneo.RestApiClient&version=0.1.0
                    
Install as a Cake Tool

OpenAkeneo.RestApiClient

Unofficial .NET client library for the Akeneo PIM REST API — typed models, automatic OAuth token management, streaming pagination, and Polly-based retry handling.

  • Note: This project is currently in development, APIs may change between minor versions until 1.0. Use at your own risk!
  • Note: Akeneo is a registered trademark of Akeneo SA. This project is not affiliated with Akeneo SA.
  • Note: This project was developed with assistance from AI tools. Please consider this fact against your AI governance policy before using this library.

Why this exists

Akeneo does not publish an official .NET SDK. The options available are either outdated, incomplete, or tightly coupled to specific project structures. This library was built to fill that gap — a clean, modern .NET client that handles the OAuth lifecycle, retries, and HAL pagination so you can focus on working with your PIM data rather than the HTTP layer.


Features

  • Two-layer architecture: low-level HTTP service + high-level typed context
  • Automatic OAuth token acquisition, in-memory caching, and proactive refresh at 75% of token lifetime
  • Transparent 401 retry — fetches a new token and retries once without any extra code
  • IAsyncEnumerable<T> streaming for all list resources — handles pagination automatically
  • Polly resilience pipeline: 5 retries with exponential back-off, jitter, and Retry-After support
  • CancellationToken support on every method
  • Optional disk-based token cache for persistence across process restarts

Installation

NuGet package coming soon.

For now, clone the repository and add a project reference.


Quickstart

// 1. Load settings
var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: false)
    .Build();

var settings = configuration.GetSection("AkeneoSettings").Get<AkeneoRestApiSettings>();

// 2. Create the client
var context = new AkeneoContext(settings);

// 3. Use it
await foreach (var product in context.StreamProductUuidsAsync())
{
    Console.WriteLine($"{product.Uuid} — enabled: {product.Enabled}");
}

Configuration

Copy appsettings.example.json to appsettings.json and fill in your credentials:

{
  "AkeneoSettings": {
    "Id": "MyConnection",
    "Name": "My Akeneo Connection",
    "ClientId": "your_client_id",
    "ClientSecret": "your_client_secret",
    "Username": "your_api_username",
    "Password": "your_api_password",
    "RestApiUrl": "https://your-instance.cloud.akeneo.com",
    "TokenFilePath": ""
  }
}

TokenFilePath is optional. When set (e.g. "akeneo_token.{0}.json"), the OAuth token is cached to disk and reused across restarts. Leave empty to cache in memory only.


Architecture

Layer Class Responsibility
Low-level AkeneoRestApiService OAuth, HTTP, retries, token cache
High-level AkeneoContext Typed methods for all Akeneo resources

For most use cases, only AkeneoContext is needed.


Usage

Token Management

Tokens are acquired automatically on the first call. Explicit control is also available:

var token = await apiService.GetTokenAsync();
var freshToken = await apiService.GetTokenAsync(forceRefresh: true);

Tokens are refreshed automatically at 75% of their lifetime. On a 401 Unauthorized response the service transparently fetches a new token and retries once.


Error Handling

All API errors throw AkeneoApiException:

try
{
    var product = await context.GetProductUuidAsync("non-existent-uuid");
}
catch (AkeneoApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
    Console.WriteLine($"Product not found: {ex.ApiMessage}");
}
catch (AkeneoApiException ex)
{
    Console.WriteLine($"API error {ex.StatusCode} at {ex.RequestUrl}: {ex.ApiMessage}");
}

Pagination Patterns

Every list resource exposes three access patterns:

Method Description
StreamXxxAsync() IAsyncEnumerable<T> — yields items one-by-one, fetches pages on demand. Best for large catalogs.
GetXxxListAsync(page, limit) Returns a single page with HAL navigation links.
GetXxxListFullAsync() Buffers all pages into a List<T>. Convenient but may use significant memory on large catalogs.

System Information

var info = await context.GetSystemInformationAsync();
Console.WriteLine($"Akeneo version: {info.Version}");

Products (UUID-based)

The preferred modern API uses product UUIDs.

// Stream all products
await foreach (var product in context.StreamProductUuidsAsync())
    Console.WriteLine($"{product.Uuid} — enabled: {product.Enabled}");

// Stream with a search filter (JSON-encoded Akeneo search syntax)
var search = """{"enabled":[{"operator":"=","value":true}]}""";
await foreach (var product in context.StreamProductUuidsAsync(search: search))
    Console.WriteLine(product.Uuid);

// Single product by UUID
var product = await context.GetProductUuidAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

// Create or update
await context.CreateOrUpdateProductUuidAsync(new ProductUuid
{
    Uuid = "a4f47e32-b29c-4f3d-a0b2-123456789abc",
    Enabled = true,
    Family = "clothing"
});

// Draft (requires Workflow feature)
var draft = await context.GetProductUuidDraftAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

Products (Identifier-based)

The legacy identifier (SKU) API is also fully supported.

await foreach (var product in context.StreamProductIdentifiersAsync())
    Console.WriteLine(product.Identifier);

var product = await context.GetProductIdentifierAsync("my-sku-001");

await context.CreateOrUpdateProductIdentifierAsync(new ProductIdentifier
{
    Identifier = "my-sku-001",
    Family = "clothing",
    Enabled = true
});

Product Models

await foreach (var model in context.StreamProductModelsAsync())
    Console.WriteLine($"{model.Code} — family: {model.Family}");

var model = await context.GetProductModelAsync("summer_collection_2024");

await context.CreateOrUpdateProductModelAsync(new ProductModel
{
    Code = "summer_collection_2024",
    Family = "clothing",
    FamilyVariant = "clothing_color_size"
});

Product Media Files

await foreach (var file in context.StreamProductMediaFilesAsync())
    Console.WriteLine($"{file.Code} — {file.MimeType}");

byte[] bytes = await context.DownloadProductMediaFileAsync("f/f/c/f/ffcf299bae0e4aeb0b85ea232722cf2a5efea125_image.jpg");
File.WriteAllBytes("output.jpg", bytes);

Families & Variants

await foreach (var family in context.StreamFamiliesAsync())
    Console.WriteLine(family.Code);

var family = await context.GetFamilyAsync("digital_cameras");

await context.CreateOrUpdateFamilyAsync(new Family
{
    Code = "my_family",
    AttributeAsLabel = "name",
    Labels = new() { ["en_US"] = "My Family" }
});

await foreach (var variant in context.StreamFamilyVariantsAsync("clothing"))
    Console.WriteLine(variant.Code);

var variant = await context.GetFamilyVariantAsync("clothing", "clothing_color_size");

Attributes

await foreach (var attr in context.StreamAttributesAsync())
    Console.WriteLine($"{attr.Code} — {attr.Type}");

// With a type filter
var search = """{"type":[{"operator":"IN","value":["pim_catalog_simpleselect"]}]}""";
var selectAttrs = await context.GetAttributeListFullAsync(search: search);

var attr = await context.GetAttributeAsync("accessories_care_instructions");

Attribute Options & Groups

var options = await context.GetAttributeOptionListFullAsync("color");
var option  = await context.GetAttributeOptionAsync("color", "red");

await foreach (var group in context.StreamAttributeGroupsAsync())
    Console.WriteLine(group.Code);

Categories

await foreach (var cat in context.StreamCategoriesAsync())
    Console.WriteLine($"{cat.Code} — parent: {cat.Parent}");

var cat = await context.GetCategoryAsync("master");

byte[] bytes = await context.DownloadCategoryMediaFileAsync("c/7/3/c/c73cc4c27c46f22447bcda64db3345269e29ecf4_banner.png");

Channels, Locales, Currencies, Measurement Families

var channels            = await context.GetChannelListFullAsync();
var locales             = await context.GetLocaleListFullAsync();
var currencies          = await context.GetCurrencyListFullAsync();
var measurementFamilies = await context.GetMeasurementFamilyListAsync();

Association Types

var all   = await context.GetAssociationTypeListFullAsync();
var assoc = await context.GetAssociationTypeAsync("x_sell");

Reference Entities

var entities   = await context.GetReferenceEntityListAsync();
var entity     = await context.GetReferenceEntityAsync("brand");
var attributes = await context.GetReferenceEntityAttributeListAsync("brand");
var records    = await context.GetReferenceEntityRecordListAsync("brand");
var record     = await context.GetReferenceEntityRecordAsync("brand", "nike");

byte[] bytes = await context.DownloadReferenceEntityMediaFileAsync("f/f/c/f/ffcf299bae0e4aeb0b85ea232722cf2a5efea125_logo.png");

Assets

var assetFamilies = await context.GetAssetFamilyListAsync();
var assetFamily   = await context.GetAssetFamilyAsync("packshots");
var attrs         = await context.GetAssetAttributeListAsync("packshots");

byte[] bytes = await context.DownloadAssetMediaFileAsync("path/to/asset_file.jpg");

Jobs

var result    = await context.LaunchExportJobAsync("csv_product_export");
var execution = await context.GetJobExecutionAsync(result.JobExecutionId);
Console.WriteLine($"Status: {execution.Status}");

var importResult = await context.LaunchImportJobAsync("csv_product_import");

Catalogs

var catalogs    = await context.GetCatalogListAsync();
var catalog     = await context.GetCatalogAsync("my-catalog-id");
var productPage = await context.GetCatalogProductUuidListAsync("my-catalog-id", page: 1, limit: 100);

Cancellation Token Support

Every method accepts an optional CancellationToken:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

await foreach (var product in context.StreamProductUuidsAsync(ct: cts.Token))
    Console.WriteLine(product.Uuid);

Resilience and Retries

AkeneoRestApiService uses a Polly resilience pipeline aligned with Akeneo's recommended retry strategy:

  • 5 retries with exponential back-off starting at 500 ms, capped at 30 s, with jitter
  • Handles 429 Too Many Requests, 408 Request Timeout, transient 5xx errors, and network faults
  • Respects the Retry-After response header automatically
  • Logs retries at Warning level when a logger is provided

No additional configuration is required.


API Reference

Full Akeneo REST API documentation: api.akeneo.com


License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0 96 6/10/2026
0.7.0 96 6/5/2026
0.6.0 97 6/4/2026
0.5.0 106 5/21/2026
0.4.0 101 5/17/2026
0.3.1 96 5/16/2026
0.3.0 95 5/15/2026
0.2.0 100 5/15/2026
0.1.0 109 5/8/2026