Balances Endpoints

View and manage all available balances across campaigns

📝

Getting Started

Learn more about campaign management with our API here

Endpoints

MethodEndpointDescription
GET/accounts/{accountId}/balancesRetrieve all balances associated with a specific account.
GET/accounts/{accountId}/balances/{balanceId}Retrieve a specific balance
POST/accounts/{accountId}/balancesCreate a new balance for a specified account.
PATCH/accounts/{accountId}/balances/{balanceId}Modify balance's metadata (name, start/end date - for deposited funds, see below)
POST/accounts/{accountId}/balances/{balanceId}/add-fundsAdd/remove funds deposited in a specific balance.
GET/balances/{balanceId}/campaignsRetrieve all campaigns linked to a specific balance.
POST/balances/{balanceId}/campaigns/appendAdd campaigns to a specific balance.
POST/balances/{balanceId}/campaigns/deleteRemove campaigns from a specific balance.
GET/balances/{balanceId}/historyRetrieve all changes made historically to a balance

Balance Parameters

AttributeData TypeDescription
idstringBalance ID

Accepted values: string of int64
Writeable? N / Nullable? N
name*stringBalance name

Accepted values: up to 255-char strings
Writeable? Y / Nullable? N
poNumberstringPurchase order number

Accepted values: up to 32-char strings
Writeable? Y / Nullable? Y
depositeddecimalAmount of funds deposited; uncapped if null

Accepted values: deposited ≥ 0.0
Writeable? Y / Nullable? Y
spentdecimalAmount of funds already spent

Accepted values: 0 ≤ spentdeposited
Writeable? N / Nullable? N
remainingdecimalAmount of funds already spent

Accepted values: 0 ≤ remainingdeposited (or null, if deposited not set)
Writeable? N / Nullable? Y
startDate*timestampBalance start date; if time zone is not set, will consider Account's time zone as default

Accepted values: yyyy-mm-ddThh:mm:ss±hh:mm(in ISO-8601 )
Writeable? Y / Nullable? N
endDatetimestampBalance end date; if time zone is not set, will consider Account's time zone as default

Accepted values: yyyy-mm-ddThh:mm:ss±hh:mm(in ISO-8601 )
Default: if null or absent, balance will be available indefinitely
Writeable? Y / Nullable? Y
statusenumBalance current status

Accepted values: active, scheduled, ended
Writeable? N / Nullable? N
createdAttimestampTimestamp of balance creation, in UTC

Accepted values: yyyy-mm-ddThh:mm:ss±hh:mm (in ISO-8601)
Writeable? N / Nullable? N
updatedAttimestampTimestamp of last balance update, in UTC

Accepted values: yyyy-mm-ddThh:mm:ss±hh:mm (in ISO-8601)
Writeable? N / Nullable? N
memostringAn optional memo note that can be set in the balance

Accepted values: up to 250-char strings
Writeable? Y / Nullable? Y
balanceTypeenumThe balance type is computed based on the deposited amount:

Accepted values: capped, uncapped
Writeable? N / Nullable? N

- capped: if the deposited amount is provided.
- uncapped: when there is no amount defined (set to null)
spendTypeenumThe type of balance that will be used based on the campaign type

Accepted values: Onsite, Offsite, OffsiteAwareness
Writeable? N / Nullable? N
privateMarketBillingTypeenumBilling type of the balance

Accepted values: notApplicable, billByRetailer, billByCriteo

⚠️ Note:

* balances created through the API will, automatically, be denoted asbillByRetailer
* only balances denoted as billByRetailer can be modified through the API

Writeable? N / Nullable? N

* Required for Balance creation


Balance History Parameters

AttributeData TypeDescription
dateOfModificationtimestampTimestamp of balance update

Accepted values: yyyy-mm-ddThh:mm:ss±hh:mm (in ISO-8601)
Writeable? N / Nullable? N
modifiedByUserstringUsername who modified the insertion order

Accepted values: strings in format "j.doe"
Writeable? N / Nullable? N
changeTypeenumDefinition of the type of change in a balance

Accepted values:

- BalanceCreated: new balance is created
- BalanceAdded: capped balance amount was increased by a certain amount
- BalanceRemoved: capped balance is decreased by a certain amount
- BalanceUncapped: capped balance is changed to uncapped
- BalanceCapped: uncapped balance is changed to capped
- StartDate: start date is modified
- EndDate: end date is modified
- BalanceName: balance name is modified
- PoNumber: PO Number is modified
- ValueAdd: new additional amount is added to the balance
- SalesforceId: SalesForceID (internal Criteo ID) is modified by Criteo (this would appear for Criteo billed balances)
changeDetailsobjectStructure with the change details (from Balance History endpoint)

Parameters:

- previousValue: previous value of a property of the balance
- currentValue: current value of a property of the balance
- changeValue: change value of a property of the balance

Get all Balances

This endpoint lists all balances in an account.

Results are paginated using pageIndex and pageSize query parameters; if omitted, defaults to 0 and 25, respective - see API Response

https://api.criteo.com/{version}/retail-media/accounts/{accountId}/balances?pageIndex={pageIndex}&pageSize={pageSize}

Sample Request

curl -X GET "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances?pageIndex=0&pageSize=25" \
    -H "Authorization: Bearer <MY_ACCESS_TOKEN>"
import requests

url = "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances?pageIndex=0&pageSize=25"

payload={}
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("text/plain");

RequestBody body = RequestBody.create(mediaType, "");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances?pageIndex=0&pageSize=25")
  .method("GET", body)
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances?pageIndex=0&pageSize=25');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'Accept' => 'application/json',
  'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));

try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}

catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "metadata": {
        "totalItemsAcrossAllPages": 94,
        "currentPageSize": 25,
        "currentPageIndex": 0,
        "totalPages": 4,
        "nextPage": "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances?pageIndex=1&pageSize=25"
    },
    "data": [
        {
            "id": "14094543095747588032",
            "type": "BalanceResponseV2",
            "attributes": {
                "name": "Balance 123",
                "poNumber": "13993827",
                "memo": "uncapped balance, free to spend!",
                "deposited": null,
                "spent": 42931.28,
                "remaining": null,
                "startDate": "2020-04-06",
                "endDate": null,
                "status": "active",
                "createdAt": "2020-04-06T00:02:41+00:00",
                "updatedAt": "2020-04-06T00:02:41+00:00",
                "balanceType": "uncapped",
                "spendType": "Onsite",
                "privateMarketBillingType": "notApplicable"
            }
        },
 
        // ...
 
        {
            "id": "4237496305219757554",
            "type": "BalanceResponseV2",
            "attributes": {
                "name": "Balance 789",
                "poNumber": "",
                "memo": "10k for the special 2s-day promotion",
                "deposited": 10000.00,
                "spent": 923.40,
                "remaining": 9076.60,
                "startDate": "2025-02-01",
                "endDate": null,
                "status": "scheduled",
                "createdAt": "2025-01-06T00:48:11+00:00",
                "updatedAt": "2025-01-07T22:19:57+00:00",
                "balanceType": "capped",
                "spendType": "Onsite",
                "privateMarketBillingType": "notApplicable"
            }
        }
    ]
}

Get specific balance

Retrieves the balance details of one specific balances belonging to an account

https://api.criteo.com/{version}/retail-media/accounts/{accountId}/balances/{balanceId}

Sample Request

curl -L 'https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/4237496305219757554' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <MY_ACCESS_TOKEN>'

Sample Response

{
    "data": {
        "id": "4237496305219757554",
        "type": "BalanceResponseV2",
        "attributes": {
            "name": "Balance 789",
            "poNumber": "",
            "memo": "10k for the special 2s-day promotion",
            "deposited": 10000.0,
            "spent": 923.4,
            "remaining": 9076.6,
            "startDate": "2025-02-01",
            "endDate": null,
            "status": "scheduled",
            "createdAt": "2025-01-06T00:48:11+00:00",
            "updatedAt": "2025-01-07T22:19:57+00:00",
            "balanceType": "capped",
            "spendType": "Onsite",
            "privateMarketBillingType": "notApplicable"
        }
    },
    "warnings": [],
    "errors": []
}

Create a new account Balance

This endpoint creates a new balance in the specified account.

https://api.criteo.com/{version}/retail-media/accounts/{accountId}/balances

Sample Request

curl -L -X POST 'https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
    -d '{
            "data": {
                "attributes": {
                    "name": "Balance 2025 Q1",
                    "startDate": "2025-01-01",
                    "spendType": "Onsite",
                    "poNumber": null,
                    "deposited": 12500.00,
                    "endDate": "",
                    "memo": "Balance for campaigns in 2025 Q1"
                }
            }
        }'
import requests
import json

url = "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances"

payload = json.dumps({
    "data": {
        "attributes": {
            "name": "Balance 2025 Q1",
            "startDate": "2025-01-01",
            "spendType": "Onsite",
            "poNumber": None,
            "deposited": 12500.00,
            "endDate": "",
            "memo": "Balance for campaigns in 2025 Q1"
        }
    }
})
headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("application/json");

RequestBody body = RequestBody.create(mediaType, "{\"data\":{\"attributes\":{\"name\":\"Balance 2025 Q1\",\"startDate\":\"2025-01-01\",\"spendType\":\"Onsite\",\"poNumber\": null,\"deposited\": 12500.00,\"endDate\":\"\",\"memo\":\"Balance for campaigns in 2025 Q1\"}}}");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
    'follow_redirects' => TRUE
));
$request->setHeader(array(
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
    'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));
$request->setBody('{"data":{"attributes":{"name":"Balance 2025 Q1","startDate":"2025-01-01","spendType":"Onsite","poNumber":null,"deposited":12500.00,"endDate":"","memo":"Balance for campaigns in 2025 Q1"}}');
try{
    $response = $request->send();
    if ($response->getStatus() == 200) {
        echo $response->getBody();
    }
    else {
        echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
        $response->getReasonPhrase();
    }
}
catch(HTTP_Request2_Exception $e) {
    echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "id": "697385288434028544",
    "type": "BalanceResponseV2",
    "data": {
        "attributes": {
            "name": "Balance 2025 Q1",
            "poNumber": null,
            "memo": "Balance for campaigns in 2025 Q1",
            "deposited": 12500.00,
            "spent": 0.00,
            "remaining": 12500.00,
            "startDate": "2025-01-01",
            "endDate": null,
            "status": "active",
            "createdAt": "2025-04-08T10:00:09+00:00",
            "updatedAt": "2025-04-08T10:00:09+00:00",
            "balanceType": "capped",
            "spendType": "Onsite",
            "privateMarketBillingType": "billByRetailer"
        }
    },
    "warnings": [],
    "errors": []
}

Modify Balance metadata

This endpoint modifies the metadata of a specified balance, like name, poNumber, startDate or endDate. To modify deposited funds, check the following endpoint.

ℹ️

Only Balances created through the API can be modified through this endpoint, ie, with billing type billByRetailer

https://api.criteo.com/{version}/retail-media/accounts/{accountId}/balances/{balanceId}

Sample Request

curl -L -X PATCH 'https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
    -d '{
            "data": {
                "attributes": {
                    "startDate": "2025-01-01",
                    "endDate": "2025-04-01",
                    "poNumber": "PO 12345",
                    "memo": "Balance for campaigns in 2025 Q1 (with start and end date)"
                }
            }
        }'
import requests
import json

url = "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544"

payload = json.dumps({
    "data": {
        "attributes": {
            "startDate": "2025-01-01",
            "endDate": "2025-04-01",
            "poNumber": "PO 12345",
            "memo": "Balance for campaigns in 2025 Q1 (with start and end date)"
        }
    }
})
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("PATCH", url, headers=headers, data=payload)

print(response.text)
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("application/json");

RequestBody body = RequestBody.create(mediaType, "{\"data\":{\"attributes\":{\"startDate\":\"2025-01-01\",\"endDate\":\"2025-04-01\",\"poNumber\":\"PO 12345\",\"memo\":\"Balance for campaigns in 2025 Q1 (with start and end date)\"}}}");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544")
  .method("PATCH", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
    'follow_redirects' => TRUE
));
$request->setHeader(array(
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
    'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));
$request->setBody('{"data":{"attributes":{"startDate":"2025-01-01","endDate":"2025-04-01","poNumber":"PO 12345","memo":"Balance for campaigns in 2025 Q1 (with start and end date)"}}}');
try{
    $response = $request->send();
    if ($response->getStatus() == 200) {
        echo $response->getBody();
    }
    else {
        echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
        $response->getReasonPhrase();
    }
}
catch(HTTP_Request2_Exception $e) {
    echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "id": "697385288434028544",
    "type": "BalanceResponseV2",
    "data": {
        "attributes": {
            "name": "Balance 2025 Q1",
            "poNumber": "PO 12345",
            "memo": "Balance for campaigns in 2025 Q1 (with start and end date)",
            "deposited": 12500.00,
            "spent": 0.00,
            "remaining": 12500.00,
            "startDate": "2025-01-01",
            "endDate": "2025-04-01",
            "status": "active",
            "createdAt": "2025-04-08T10:00:09+00:00",
            "updatedAt": "2025-04-08T10:00:09+00:00",
            "balanceType": "capped",
            "spendType": "Onsite",
            "privateMarketBillingType": "billByRetailer"
        }
    },
    "warnings": [],
    "errors": []
}

Add or remove Balance funds

This endpoint allows to add or remove funds deposited in a specific balance

ℹ️

Only Balances created through the API can be modified through this endpoint, ie, with billing type billByRetailer

https://api.criteo.com/{version}/retail-media/accounts/{accountId}/balances/{balanceId}/add-funds

Request Body Parameters

AttributeData TypeDescription
deltaAmount*decimalDifference amount of fund to be added/removed from balance; it cannot reduce the current amount of funds deposited to less than zero

Accepted values: deltaAmountdeposited * (-1)
Writeable? N / Nullable? N
poNumberstring New purchase order number

Accepted values: up to 32-char strings
Writeable? Y / Nullable? Y
memo*stringA memo note that should be set in the balance together with this modification

Accepted values: up to 250-char strings
Writeable? Y / Nullable? Y

* Required

Sample Request

curl -L -X PATCH 'https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544/add-funds' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -H 'Authorization: Bearer <MY_ACCESS_TOKEN>' \
    -d '{
            "data": {
                "attributes": {
                    "deltaAmount": -2500.00,
                    "poNumber": "PO 12346",
                    "memo": "Reduced balance for campaigns in 2025 Q1"
                }
            }
        }'
import requests
import json

url = "https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544/add-funds"

payload = json.dumps({
    "data": {
        "attributes": {
            "deltaAmount": -2500.00,
            "poNumber": "PO 12346",
            "memo": "Reduced balance for campaigns in 2025 Q1"
        }
    }
})
headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)

OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("application/json");

RequestBody body = RequestBody.create(mediaType, "{\"data\":{\"attributes\":{\"deltaAmount\": -2500.00,\"poNumber\":\"PO 12346",\"memo":\"Reduced balance for campaigns in 2025 Q1\"}}}");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544/add-funds")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/accounts/18446744073709551616/balances/697385288434028544/add-funds');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
    'follow_redirects' => TRUE
));
$request->setHeader(array(
    'Content-Type' => 'application/json',
    'Accept' => 'application/json',
    'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));
$request->setBody('{"data":{"attributes":{"deltaAmount":-2500.00,"poNumber":"PO 12346","memo":"Reduced balance for campaigns in 2025 Q1"}}}');
try{
    $response = $request->send();
    if ($response->getStatus() == 200) {
        echo $response->getBody();
    }
    else {
        echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
        $response->getReasonPhrase();
    }
}
catch(HTTP_Request2_Exception $e) {
    echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "id": "697385288434028544",
    "type": "BalanceResponseV2",
    "data": {
        "attributes": {
            "name": "Balance 2025 Q1",
            "poNumber": "PO 12346",
            "memo": "Reduced balance for campaigns in 2025 Q1",
            "deposited": 10000.00,
            "spent": 0.00,
            "remaining": 10000.00,
            "startDate": "2025-01-01",
            "endDate": "2025-04-01",
            "status": "active",
            "createdAt": "2025-04-08T10:00:09+00:00",
            "updatedAt": "2025-04-08T10:00:09+00:00",
            "balanceType": "capped",
            "spendType": "Onsite",
            "privateMarketBillingType": "billByRetailer"
        }
    },
    "warnings": [],
    "errors": []
}

Get all Campaigns on a specific Balance

This endpoint lists all campaigns on the specified balance.

Results are paginated using pageIndex and pageSize query parameters; if omitted, defaults to 0 and 25, respective - see API Response

https://api.criteo.com/{version}/retail-media/balances/{balanceId}/campaigns?pageIndex={pageIndex}&pageSize={pageSize}

Response Body Parameters

AttributeData TypeDescription
idstringCampaign ID, respective to the campaign(s) currently appended to the balance

Accepted values: string of int64
Writeable? N / Nullable? N

Sample Request

curl -X GET "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns?pageIndex=0&pageSize=25" \
    -H "Authorization: Bearer <MY_ACCESS_TOKEN>"
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("text/plain");

RequestBody body = RequestBody.create(mediaType, "");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns?pageIndex=0&pageSize=25")
  .method("GET", body)
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
import requests

url = "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns?pageIndex=0&pageSize=25"

payload={}
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns?pageIndex=0&pageSize=25');
$request->setMethod(HTTP_Request2::METHOD_GET);
$request->setConfig(array(
  'follow_redirects' => TRUE
));
$request->setHeader(array(
  'Accept' => 'application/json',
  'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));

try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}

catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "data": [
        {
            "id": "8343086999167541140",
            "type": "RetailMediaCampaign"
        },
        {
            "id": "3683145960016759663",
            "type": "RetailMediaCampaign"
        }
    ],
    "metadata": {
        "totalItemsAcrossAllPages": 2,
        "currentPageSize": 25,
        "currentPageIndex": 0,
        "totalPages": 1,
        "nextPage": null,
        "previousPage": null
    }
}

Add Campaigns to a specific Balance

This endpoint adds one or more campaigns to the specified balance. The results are provided in a single page. In this example, a campaign had already existed on the balance before two new additions.

https://api.criteo.com/{version}/retail-media/balances/{balanceId}/campaigns/append

Request Body Parameters

AttributeData TypeDescription
idstringCampaign ID, required to define to which campaign(s) the balance should be appended or deleted

Accepted values: string of int64
Writeable? N / Nullable? N

Sample Request

curl -X POST "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/append" \
    -H "Authorization: Bearer <MY_ACCESS_TOKEN>" \
    -H "Content-Type: application/json" \
    -d '{
            "data": [
                {
                    "id": "3683145960016759663",
                    "type": "RetailMediaCampaign"
                },
                {
                    "id": "16108177282234788969",
                    "type": "RetailMediaCampaign"
                }
            ]
        }'
import requests
import json

url = "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/append"

payload = json.dumps({
  "data": [
    {
      "id": "3683145960016759663",
      "type": "RetailMediaCampaign"
    },
    {
      "id": "16108177282234788969",
      "type": "RetailMediaCampaign"
    }
  ]
})
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("application/json");

RequestBody body = RequestBody.create(mediaType, "{\"data\":[{\"id\":\"3683145960016759663\",\"type\":\"RetailMediaCampaign\"},{\"id\":\"16108177282234788969\",\"type\":\"RetailMediaCampaign\"}]\n}");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/append")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/append');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
  'follow_redirects' => TRUE
));

$request->setHeader(array(
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));

$request->setBody('{"data":[{"id":"3683145960016759663","type":"RetailMediaCampaign"},{"id":"16108177282234788969","type":"RetailMediaCampaign"}]}');
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}
catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "data": [
        {
            "id": "8343086999167541140",
            "type": "RetailMediaCampaign"
        },
        {
            "id": "3683145960016759663",
            "type": "RetailMediaCampaign"
        },
        {
            "id": "16108177282234788969",
            "type": "RetailMediaCampaign"
        }
    ],
    "metadata": {
        "totalItemsAcrossAllPages": 3,
        "currentPageSize": 3,
        "currentPageIndex": 0,
        "totalPages": 1,
        "nextPage": null,
        "previousPage": null
    }
}

Remove Campaigns from a specific Balance

This endpoint removes one or more campaigns from the specified balance. The resulting state of the balance is returned as a single page.

https://api.criteo.com/{version}/retail-media/balances/{balanceId}/campaigns/delete

Request Body Parameters

AttributeData TypeDescription
idstringCampaign ID, required to define to which campaign(s) the balance should be appended or deleted

Accepted values: string of int64
Writeable? N / Nullable? N

Sample Request

curl -X POST "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/delete" \
    -H "Authorization: Bearer <MY_ACCESS_TOKEN>" \
    -H "Content-Type: application/json" \
    -d '{
            "data": [
                {
                    "id": "16108177282234788969",
                    "type": "RetailMediaCampaign"
                }
            ]
        }'
import requests
import json

url = "https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/delete"

payload = json.dumps({
  "data": [
    {
      "id": "16108177282234788969",
      "type": "RetailMediaCampaign"
    }
  ]
})
headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Authorization': 'Bearer <MY_ACCESS_TOKEN>'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)
OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

MediaType mediaType = MediaType.parse("application/json");

RequestBody body = RequestBody.create(mediaType, "{\"data\":[{\"id\":\"16108177282234788969\",\"type\":\"RetailMediaCampaign\"}]}");

Request request = new Request.Builder()
  .url("https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/delete")
  .method("POST", body)
  .addHeader("Content-Type", "application/json")
  .addHeader("Accept", "application/json")
  .addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
  .build();

Response response = client.newCall(request).execute();
<?php
require_once 'HTTP/Request2.php';
$request = new HTTP_Request2();
$request->setUrl('https://api.criteo.com/{version}/retail-media/balances/14094543095747588032/campaigns/delete');
$request->setMethod(HTTP_Request2::METHOD_POST);
$request->setConfig(array(
  'follow_redirects' => TRUE
));

$request->setHeader(array(
  'Content-Type' => 'application/json',
  'Accept' => 'application/json',
  'Authorization' => 'Bearer <MY_ACCESS_TOKEN>'
));

$request->setBody('{"data":[{"id":"16108177282234788969","type":"RetailMediaCampaign"}]}');
try {
  $response = $request->send();
  if ($response->getStatus() == 200) {
    echo $response->getBody();
  }
  
  else {
    echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
    $response->getReasonPhrase();
  }
}

catch(HTTP_Request2_Exception $e) {
  echo 'Error: ' . $e->getMessage();
}

Sample Response

{
    "data": [
        {
            "id": "8343086999167541140",
            "type": "RetailMediaCampaign"
        },
        {
            "id": "3683145960016759663",
            "type": "RetailMediaCampaign"
        }
    ],
    "metadata": {
        "totalItemsAcrossAllPages": 2,
        "currentPageSize": 2,
        "currentPageIndex": 0,
        "totalPages": 1,
        "nextPage": null,
        "previousPage": null
    }
}

Get Balance History

This endpoint lists all changes made to a specific balance.

Results are paginated using offset and limit query parameters; if omitted, defaults to 0 and 500, respective - see API Response

Additional query parameter limitToChangeTypes can be used to inform a comma-separated list of changeType values.

📘

Modified Users

modifiedByUser - When a balance is updated via Criteo's Retail Media UI, the user login name will be provided. For instance, if “Kip Heaney” updated the balance on 2023-11-07, it indicates that Kip made the change through the Criteo Retail Media UI.

If a balance is updated through the Criteo API, the name of the API application responsible for the change will be shown. For example, on 2024-03-20, the balance was updated by the API application Retail Media API Application.

https://api.criteo.com/{version}/retail-media/balances/{balanceId}/history?offset={offset}&limit={limit)&limitToChangeTypes={changeTypes}

Sample Request: retrieve all changes

curl -L 'https://api.criteo.com/{version}/retail-media/balances/697385288434028544/history?offset=0&limit=25' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>'

Sample Response: retrieve all changes

{
    "meta": {
        "count": 5,
        "offset": 0,
        "limit": 25
    },
    "data": [
        {
            "dateOfModification": "2025-02-15T12:24:49-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "BalanceCreated",
            "changeDetails": {
                "previousValue": null,
                "currentValue": "12500.00000000",
                "changeValue": null
            },
            "memo": "Balance for campaigns in 2025 Q1"
        },
        {
            "dateOfModification": "2025-02-15T12:25:40-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "EndDate",
            "changeDetails": {
                "previousValue": null,
                "currentValue": "2025-04-01T23:59:59-04:00",
                "changeValue": null
            },
            "memo": "Balance for campaigns in 2025 Q1 (with start and end date)"
        },
        {
            "dateOfModification": "2025-02-15T12:25:40-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "PoNumber",
            "changeDetails": {
                "previousValue": null,
                "currentValue": "PO 12345",
                "changeValue": null
            },
            "memo": "Balance for campaigns in 2025 Q1 (with start and end date)"
        },
        {
            "dateOfModification": "2025-02-15T12:26:16-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "BalanceRemoved",
            "changeDetails": {
                "previousValue": "12500.00000000",
                "currentValue": "10000.00000000",
                "changeValue": "-2500.00000000"
            },
            "memo": "Reduced balance for campaigns in 2025 Q1"
        },
        {
            "dateOfModification": "2025-02-15T12:26:16-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "PoNumber",
            "changeDetails": {
                "previousValue": "PO 12345",
                "currentValue": "PO 12346",
                "changeValue": null
            },
            "memo": "Reduced balance for campaigns in 2025 Q1"
        }
    ]
}

Sample Request: retrieve only changes in balance values (added / removed)

curl -L 'https://api.criteo.com/{version}/retail-media/balances/697385288434028544/history?offset=0&limit=25&limitToChangeTypes=BalanceAdded,BalanceRemoved' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>'

Sample Response: retrieve only changes in balance values (added / removed)

{
    "meta": {
        "count": 2,
        "offset": 0,
        "limit": 25
    },
    "data": [
        {
            "dateOfModification": "2024-02-15T12:10:11.2807153-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "BalanceRemoved",
            "changeDetails": {
                "previousValue": "12500.00000000",
                "currentValue": "10000.00000000",
                "changeValue": "-2500.00000000"
            },
            "memo": "Reduced balance for campaigns in 2025 Q1"
        },
        {
            "dateOfModification": "2024-03-03T16:30:21.2807153-04:00",
            "modifiedByUser": "Retail Media API Application",
            "changeType": "BalanceAdded",
            "changeDetails": {
                "previousValue": "10000.00000000",
                "currentValue": "15000.00000000",
                "changeValue": "5000.00000000"
            },
            "memo": "Increased balance for campaigns in 2025 Q1"
        }
    ]
}

Responses

ResponseTitleDetailTroubleshooting
🟢 200Call executed with success
🟢 201Balance request created with success
🔴 400Error deserializing requestField xyz is not validReview the value of field xyz provided in the request
🔴 400Change data capture type xxx is not supportedChange data capture type xxx is not supportedThe value of limitToChangeTypes is not supported for /balances/{balanceId} /history - ensure to use a comma-separated list of availablechangeType values defined above
🔴 400Invalid nameBalance name should be unique. There exists balance with the specified name. Balance creation/update has been canceledCheck value of name in the request trying to create/edit a balance. In case of editing, either provided a new name value or omit this parameter to maintain its same value
🔴 400Invalid deltaamountCan not decrease funds to less than zeroReview value of deltaAmount and make sure it's greater than current balance's deposited * (-1)
🔴 400Invalid operationCan only change the field xxx of a balance not billed by retailer.Cannot edit balances not created through the API; only balances with billing type billByRetailer can be modified
🔴 400Invalid operationCan not add funds to a balance not billed by retailer.Cannot edit balances not created through the API; only balances with billing type billByRetailer can be modified
🔴 403Authorization errorResource access forbidden: does not have permissionsOne of the permission levels was not respected. Make sure that the respective API app has access to:

- Read/Manage the domain "Balance" (depending on the requested action). Review the Types of Permissions in Authorization Requests
- the accountId or balanceId provided in the request