Introduction
Commerce Max Retailer Budgets introduces a buying model in which campaign budgets are funded by the retailer rather than the advertiser.
This guide covers the API changes required for third-party buying platforms to create and manage Retailer Budgets Sponsored Products campaigns end-to-end.
Business Context
Prior to this release, the Retail Media API did not expose retailer scoping on balances, campaigns, or line items. Retailer-budgets buying requires platforms to associate balances, campaigns, and line items to the same retailer β the API now enforces these constraints explicitly.
Prerequisites
- API version
2026-01 or later is required to see retailer-billed balances. On prior versions, retailer-budgets balances are hidden by default to prevent integration surprises during rollout.
- Platforms must use the supply account ID when querying balances if they wish to discover retailer-budgets balances.
Key Concepts
- Retailer-budgets balance β a budget object funded by the retailer, scoped to a specific
RetailerId. Cannot be created via API; must be retrieved via GET /balances.
RetailerId β the identifier of the retailer that funds the balance and scopes the campaign. Must be consistent across balance β campaign β line item.
budgetModel β a new field on the retailer search response indicating which budget models (e.g., retailerBilled, capped, uncapped) are supported at a given retailer.
Backward compatibility for balance endpoints:
- The
poNumber field on balance responses is removed in 2026-01 and replaced by two separate fields: retailerPoNumber and criteoPoNumber. This is a breaking change for consumers of GET /balances on prior versions who rely on poNumber.
- All other new fields (
retailerId, privateMarketBillingType) are additive. Retailer-budgets balances are hidden by default on prior API versions.
Endpoints Overview
All endpoints changes related to Retailer Budgets are also documented in the following pages:
Verb | Endpoint | Description |
|---|
GET | /retail-media/balances/{balanceId}
| Get a single balance. Now includesretailerId,retailerPoNumber,criteoPoNumber. |
GET | /retail-media/accounts/{accountId}/balances
| List balances. Now includes retailer fields. Retailer-budgets balances hidden on prior versions. |
GET | /retail-media/balances/{balanceId}/history
| Get balance change history. Now includesretailerPoNumber,criteoPoNumber. |
POST | /retail-media/balances/{balanceId}/campaigns/append
| Add campaigns to a balance. Validates retailer consistency. |
POST | /retail-media/balances/{balanceId}/campaigns/delete
| Remove campaigns from a balance. Returns error for retailer-billed balances. |
POST | /retail-media/accounts/{accountId}/campaigns
| Create a campaign. Now accepts and validatesretailerId. |
GET | /retail-media/campaigns
| List campaigns. Now returnsretailerId; supports filtering by retailer. |
GET | /retail-media/campaigns/{campaignId}
| Get a campaign. Now returnsretailerId. |
POST | /retail-media/campaigns/{campaignId}/auction-line-items
| Create a line item. EnforcestargetRetailerIdmatches campaign retailer. |
POST | /retail-media/accounts/{accountId}/retailers/search
| Search retailers. Now returnsbudgetModelincampaignAvailabilities. |
Attributes
New and Changed Fields on Balances
Attribute | Data Type | Mutable | Description |
|---|
retailerId
| string?
| init | Retailer this balance is scoped to. Present only on retailer-budgets balances. Nullable? Y (null for non-retailer-billed) |
retailerPoNumber
| string?
| always | Retailer purchase order number.Replacesthe removedpoNumberfield. Nullable? Y |
criteoPoNumber
| string?
| always | Criteo purchase order number. Replacesthe removedpoNumberfield. Nullable? Y |
privateMarketBillingType
| enum
| init | Billing type for Private Market. Values:NotApplicable,BillByRetailer,BillByCriteo,Unknown |
~~poNumber~~ | string
| β | Removedin2026-01. Replaced byretailerPoNumberandcriteoPoNumber. |
New Field on Campaigns
A new field retailerId has been added to the following endpoints:
/accounts/{accountId}/campaigns
/campaigns/{campaignId}
Attribute | Data Type | Description |
|---|
retailerId
| string
| The retailer this campaign is associated with. Required when using a retailer-budgets balance. Writeable?Y (at create) Nullable?Y (for non-retailer-budgets campaigns) |
New Fields on Retailer Search
The new fields are added to the following endpoint:
/accounts/{accountId}/retailers/search
Attribute | Data Type | Description |
|---|
budgetModel
| string
| Budget model(s) supported for the givenbuyType/campaignTypecombination at this retailer. Values:capped,uncapped,retailerBudgets,criteoBudgets Writeable?N Nullable?N |
New Error Codes
Error code | Endpoint | Meaning |
|---|
RetailerBilledBalanceImmutable
| DELETE /balances/{balanceId}/campaigns
| Cannot remove a retailer-budgets balance from a campaign. |
RetailerMismatchWithBalance
| POST /accounts/{accountId}/campaigns
| CampaignRetailerIddoes not match the balanceβsRetailerId. |
RetailerMismatchWithCampaign
| POST /campaigns/{campaignId}/auction-line-items
| Line itemtargetRetailerIddoes not match the campaignβsRetailerId. |
Endpoint Changes
Get List of Balances for an Account
This endpoint returns a paginated list of balances for an account.
On API version 2026-01 and later, retailer-budgets balances are included and retailerId, retailerPoNumber, and criteoPoNumber are returned. The old poNumber field is removed.
Query parameters
offset (int, default 0),
limit (int, default 25, max 500),
limitToId (List<string>)
Sample Request
curl -L -X GET 'https://api.criteo.com/2026-01/retail-media/accounts/625702934721171442/balances' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>'
import http.client
conn = http.client.HTTPSConnection("api.criteo.com")
headers = {'Accept': 'application/json', 'Authorization': 'Bearer <TOKEN>'}
conn.request("GET", "/2026-01/retail-media/accounts/625702934721171442/balances", headers=headers)
res = conn.getresponse()
print(res.read().decode("utf-8"))
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url("https://api.criteo.com/2026-01/retail-media/accounts/625702934721171442/balances")
.method("GET", null)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <TOKEN>")
.build();
Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/2026-01/retail-media/accounts/625702934721171442/balances');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setHeader(array('Accept' => 'application/json', 'Authorization' => 'Bearer <TOKEN>'));
echo $request->send()->getBody();
Sample Response
{
"metadata": {
"totalItemsAcrossAllPages": 135,
"currentPageSize": 25,
"currentPageIndex": 0,
"totalPages": 6
},
"data": [
{
"id": "100000000000000001",
"type": "BalanceV1",
"attributes": {
"name": "Sample Name",
"retailerPoNumber": null,
"criteoPoNumber": "PO-CRITEO-123",
"retailerId": null,
"memo": "Sample memo",
"deposited": 10.0,
"spent": 10.0,
"remaining": 0.0,
"startDate": "2020-04-13",
"endDate": null,
"status": "ended",
"createdAt": "2020-04-13T15:39:48+00:00",
"updatedAt": "2023-06-13T13:35:53+00:00",
"balanceType": "capped",
"spendType": "onsite",
"privateMarketBillingType": "notApplicable"
}
},
{
"id": "100000000000000002",
"type": "BalanceV1",
"attributes": {
"name": "Sample Name",
"retailerPoNumber": "PO-RETAILER-123",
"criteoPoNumber": "PO-CRITEO-123",
"retailerId": 123,
"memo": "Sample memo",
"deposited": 100.0,
"spent": 0.16,
"remaining": 99.84,
"startDate": "2026-03-24",
"endDate": null,
"status": "active",
"createdAt": "2026-03-24T18:15:58+00:00",
"updatedAt": "2026-05-20T17:17:40+00:00",
"balanceType": "capped",
"spendType": "onsite",
"privateMarketBillingType": "notApplicable"
}
}
],
"warnings": [],
"errors": []
}
Add Campaigns to a Balance
This endpoint adds one or more campaigns to a balance. It validates that campaigns are retailer-native when the balance is retailer-budget.
Sample Request
curl -L -X POST 'https://api.criteo.com/2026-01/retail-media/balances/814886589256347648/campaigns/append' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
-d '{
"data": {
"attributes": {
"ids": ["718038552188952576", "234105251251242423"]
},
"type": "AppendCampaignsRequest"
}
}'
Sample Response
{
"data": {
"attributes": {
"ids": ["718038552188952576", "234105251251242423"]
},
"type": "BalanceCampaignsV1"
},
"warnings": [],
"errors": []
}
Sample Response β Error (non-retailer-native campaign)
{
"errors": [
{
"message": "Only retailer-sold campaigns are allowed to be mapped to a retailer-budget balance.",
"status": 400
}
]
}
Remove Campaign(s) from a Balance
This endpoint removes one or more campaigns from a balance.
Sample Request
curl -L -X POST 'https://api.criteo.com/2026-01/retail-media/balances/814886589256347648/campaigns/delete' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
-d '{
"data": {
"attributes": {
"ids": ["234105251251242423"]
},
"type": "DeleteCampaignsRequest"
}
}'
Sample Response
{
"data": {
"attributes": {
"ids": ["718038552188952576"]
},
"type": "BalanceCampaignsV1"
},
"warnings": [],
"errors": []
}
Create New Campaigns
This endpoint creates a new campaign.
For retailer-budgets campaigns, retailerId is required and must match the retailerId on the drawable balance.
Sample Request
curl -L -X POST 'https://api.criteo.com/2026-01/retail-media/accounts/625702934721171442/campaigns' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
-d '{
"data": {
"type": "Campaign",
"attributes": {
"name": "Retailer Budgets Campaign Q2",
"startDate": "2026-07-01T00:00:00+00:00",
"clickAttributionWindow": "30D",
"viewAttributionWindow": "None",
"retailerId": "1298",
"drawableBalanceIds": ["814886589256347648"]
}
}
}'
Sample Response
{
"data": {
"id": "718038552188952576",
"type": "Campaign",
"attributes": {
"name": "Retailer Budgets Campaign Q2",
"accountId": "625702934721171442",
"type": "auction",
"status": "inactive",
"retailerId": "1298",
"drawableBalanceIds": ["814886589256347648"],
"budget": null,
"budgetSpent": 0.0,
"budgetRemaining": null,
"startDate": "2026-07-01T00:00:00+00:00",
"endDate": null,
"clickAttributionWindow": "30D",
"viewAttributionWindow": "None",
"createdAt": "2026-05-08T00:00:00+00:00",
"updatedAt": "2026-05-08T00:00:00+00:00"
}
}
}
Get Campaigns by Account ID and Campaign ID
Both endpoints now return retailerId in the campaign attributes.
The list endpoint supports filtering by retailerId.
Sample Request β Get a Single Campaign
curl -L -X GET 'https://api.criteo.com/2026-01/retail-media/campaigns/718038552188952576' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>'
Sample Response
{
"data": {
"id": "718038552188952576",
"type": "Campaign",
"attributes": {
"name": "Retailer Budgets Campaign Q2",
"accountId": "625702934721171442",
"type": "auction",
"status": "active",
"retailerId": "1298",
"drawableBalanceIds": ["814886589256347648"],
"budget": null,
"budgetSpent": 1250.00,
"budgetRemaining": null,
"startDate": "2026-07-01T00:00:00+00:00",
"endDate": null,
"clickAttributionWindow": "30D",
"viewAttributionWindow": "None",
"createdAt": "2026-05-08T00:00:00+00:00",
"updatedAt": "2026-07-01T00:00:00+00:00"
}
}
}
Create an Auction Line Item
Creates an auction line item.
For retailer-budgets campaigns, targetRetailerId is required and must match the campaignβs retailerId. Only one retailer is allowed per retailer-billed campaign.
Sample Request
curl -L -X POST 'https://api.criteo.com/2026-01/retail-media/campaigns/718038552188952576/auction-line-items' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
-d '{
"data": {
"type": "SponsoredProductsLineItem",
"attributes": {
"name": "Retailer Budget Line Item",
"targetRetailerId": "1298",
"startDate": "2026-07-01",
"bidStrategy": "automated",
"optimizationStrategy": "conversion",
"keywordStrategy": "genericAndBranded"
}
}
}'
Sample Response
{
"data": {
"id": "234105251251242423",
"type": "SponsoredProductsLineItem",
"attributes": {
"name": "Retailer Budget Line Item",
"campaignId": "718038552188952576",
"targetRetailerId": "1298",
"startDate": "2026-07-01T00:00:00+00:00",
"endDate": null,
"status": "inactive",
"budget": null,
"budgetSpent": 0.0,
"budgetRemaining": null,
"targetBid": 0.3,
"maxBid": null,
"monthlyPacing": null,
"dailyPacing": null,
"isAutoDailyPacing": false,
"bidStrategy": "automated",
"optimizationStrategy": "conversion",
"keywordStrategy": "genericAndBranded",
"flightSchedule": null,
"createdAt": "2026-05-08T00:00:00+00:00",
"updatedAt": "2026-05-08T00:00:00+00:00"
}
},
"warnings": [],
"errors": []
}
Sample Response β Error (retailer mismatch)
{
"errors": [
{
"message": "The line item targetRetailerId must match the campaign retailerId.",
"status": 400
}
]
}
Search Retailer for an Account
This endpoint allows searching for available retailers for an account.
The campaignAvailabilities object now includes budgetModel, allowing platforms to determine which budget models are supported at a given retailer before creating a campaign.
Sample Request
curl -L -X POST 'https://api.criteo.com/2026-01/retail-media/accounts/625702934721171442/retailers/search' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
-d '{}'
Sample Response
{
"data": [
{
"id": "retailer-789",
"type": "Retailer",
"attributes": {
"name": "Example Retailer",
"campaignAvailabilities": [
{
"buyType": "auction",
"campaignType": "sponsoredProducts",
"isAvailable": true,
"budgetModel": "retailerBilled",
"validCombinations": [
{
"pageType": "search",
"pageEnvironmentType": "offsite"
}
]
}
]
}
}
]
}
Responses
Response | Title | Detail | Troubleshooting |
|---|
π’200 | Success | | Request executed successfully. |
π΄400 | Retailer-budget balance mapping | βOnly retailer-sold campaigns are allowed to be mapped to a retailer-billed balance.β | Only campaigns scoped to the same retailer as the balance can be appended via/campaigns/append. |
π΄400 | RetailerMismatchWithBalance
| CampaignretailerIddoes not match the balanceretailerId. | Ensure theretailerIdin campaign settings matches theretailerIdon the balance you are associating. |
π΄400 | RetailerMismatchWithCampaign
| Line itemtargetRetailerIddoes not match the campaignretailerId. | EnsuretargetRetailerIdon the line item matches theretailerIdset on the parent campaign. |
π΄400 | Error deserializing request
| A required field is missing or has an invalid value. | Review the request body against the attributes table above. |
π΄403 | Unauthorized
| | Verify your access token and that your account has access to the retailer in question. |