Keyword Approval
The keyword approval process is designed for advertisers and retailers to determine what keywords are prospects to be used for targeting on retailer inventory. When an advertiser proposes a set of keywords for review, these keywords are then forwarded to the retailer reviewer, who evaluates them based on predefined criteria such as relevance, accuracy, and compliance with guidelines. The reviewer may use tools and resources to verify the appropriateness of each keyword. If the keywords meet the standards, they are approved and added to the database. This iterative process ensures that only high-quality, relevant keywords are approved and utilized.
Endpoints
Verb | Endpoint | Description |
---|---|---|
GET | /accounts/{accountId}/keywords/in-review-report | Retrieve a Keywords Approval report to the specific supply account, with a count of keywords in review state at line-item |
GET | /line-items/{lineItemId}/keywords | Retrieve a set of all keywords in review state for the specific line-item |
POST | /line-items/{lineItemId}/keywords/review | Review keyword(s) for the specific line-item |
Keywords Approval Report Attributes
Attribute | Data Type | Description |
---|---|---|
id / lineItemId | string | Line Item ID, respective to the amount of keywords in review Accepted values: string of int64 Writeable? N / Nullable? N |
lineItemName | string | Line Item name, respective to the amount of keywords in review Accepted values: up to 255-chars string Writeable? Y / Nullable? N |
campaignId | string | Campaign ID, respective to the amount of keywords in review Accepted values: string of int64 Writeable? N / Nullable? N |
campaignName | string | Campaign name, respective to the amount of keywords in review Accepted values: up to 255-chars string Writeable? Y / Nullable? N |
accountId | string | Account ID, respective to the amount of keywords in review Accepted values: string of int64 Writeable? N / Nullable? N |
accountName | string | Account name, respective to the amount of keywords in review Accepted values: up to 255-chars string Writeable? Y / Nullable? N |
retailerId | string | Retailer ID, respective to the amount of keywords in review Accepted values: string of int64 Writeable? N / Nullable? N |
retailerName | string | Retailer name, respective to the amount of keywords in review Accepted values: up to 255-chars string Writeable? Y / Nullable? N |
countKeywords | integer | Amount of keywords in review for the respective line-item Accepted values: countKeywords ≥ 1Writeable? N / Nullable? N |
Keywords Review Attributes
Attribute | Data Type | Description |
---|---|---|
id | string | Line Item ID, respective to the amount of keywords in review Accepted values: string of int64 Writeable? N / Nullable? N |
reviewState | enum | Status of the Keyword Review (only applicable for PositiveExactMatch keywords)Accepted values: * InReview - keyword has been submitted manually, and the review is still pending* Approved - keyword was approved manually* Rejected - keyword was rejected manually* AutoApproved - keyword was approved automatically* AutoRejected - keyword was rejected automatically* Recommended - keyword was recommended by our keyword modelDefault: InReview Writeable? N / Nullable? N |
matchType | enum | The matching algorithm to be used when comparing this keyword with shopper search phrases Accepted values: * PositiveExactMatch - normalized keyword is an exact match for the normalized search phrase bid* NegativeExactMatch - normalized keyword is an exact match for the normalized search phrase to not bid* NegativeBroadMatch - normalized keyword is a substring of the normalized search phrase to not bidWriteable? N / Nullable? N |
bid | decimal | The bid override for the positive keyword. The keyword will use the default line item bid if the value is null . The currency of the bid is the default currency for the retailer.Accepted values: retailer's minBid ≤ bid ≤ retailer's maxBid Default: null Writeable? Y / Nullable? Y |
inputKeywords | object | Keywords supplied by the user matching this normalized keyword phrase binned by match type (see examples below) Parameters: * positiveExact - collection of supplied positive exact phrases* negativeExact - collection of supplied negative exact phrases* negativeBroad - collection of supplied negative broad phrasesWriteable? Y / Nullable? Y |
createdAt | timestamp | Timestamp the keyword was created in the line-item (or recommended to line-item) Accepted values: yyyy-mm-ddThh:mm:ss (in ISO-8601 )Writeable? N / Nullable? N |
updatedAt | timestamp | Last timestamp the keyword was updated in the line-item (or recommended to line-item) Accepted values: yyyy-mm-ddThh:mm:ss (in ISO-8601 )Writeable? N / Nullable? N |
Keywords Review Body Request
Attribute | Data Type | Description |
---|---|---|
keywords * | list | List of object pairs made of phrase and reviewState to approve/reject keywordsParameters: * phrase - keyword to review* reviewState - approval resultWriteable? N / Nullable? N |
phrase * | enum | Normalized keyword for approval review Accepted values: string keywords returned from previous endpoint (in "reviewState": "InReview" Writeable? N / Nullable? N |
reviewState * | enum | Approval result of the Keyword Review Accepted values: * Approved - keyword was approved manually* Rejected - keyword was rejected manuallyWriteable? N / Nullable? N |
Get Keywords Approval report
This endpoint retrieves a Keywords Approval report for the provided supply account ID (not applicable for demand accounts).
This endpoint is available only for API apps with Manage access to Campaign domain

https://api.criteo.com/preview/retail-media/accounts/{accountId}/keywords/in-review-report?offset=0&limit=25
Sample Request
curl -L -X GET "https://api.criteo.com/preview/retail-media/accounts/368471940340928512/keywords/in-review-report?offset=0&limit=25" \
-H "Authorization: Bearer <MY_ACCESS_TOKEN>"
import requests
url = "https://api.criteo.com/preview/retail-media/accounts/368471940340928512/keywords/in-review-report?offset=0&limit=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/preview/retail-media/accounts/368471940340928512/keywords/in-review-report?offset=0&limit=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();
}
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/preview/retail-media/accounts/368471940340928512/keywords/in-review-report?offset=0&limit=25")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
.build();
Response response = client.newCall(request).execute();
Sample Response
{
"meta": {
"count": 71,
"offset": 0,
"limit": 25
},
"data": [
{
"id": "4840685188706902009",
"type": "LineItemKeywordReviewReport",
"attributes": {
"lineItemId": "4840685188706902009",
"lineItemName": "LI Retailer ABC Category A",
"retailerId": "401887",
"retailerName": "Retailer ABC",
"campaignId": "124755545923269376",
"campaignName": "Campaign AAA",
"accountId": "97393138059194368",
"accountName": "Test Account",
"countKeywords": 2
}
},
{
"id": "6854840188706902009",
"type": "LineItemKeywordReviewReport",
"attributes": {
"lineItemId": "124773003405488128",
"lineItemName": "LI Retailer ABC Category B",
"retailerId": "401887",
"retailerName": "Retailer ABC",
"campaignId": "124755545923269376",
"campaignName": "Campaign AAA",
"accountId": "97393138059194368",
"accountName": "Test Account",
"countKeywords": 4
}
},
// ...
{
"id": "358669652976373760",
"type": "LineItemKeywordReviewReport",
"attributes": {
"lineItemId": "9979917896105882144",
"lineItemName": "LineItem ABC Producs Jan 2025",
"retailerId": "401887",
"retailerName": "Retailer ABC",
"campaignId": "106358893501214720",
"campaignName": "Sponsored Products Jan 2025",
"accountId": "97393138059194368",
"accountName": "Test Account",
"countKeywords": 5
}
}
],
"warnings": [],
"errors": []
}
Get Keywords to review
This endpoint retrieves the list of all Keywords review state for the provided Line-Item ID.
This endpoint evolved from the previous Keywords endpoint GET /line-items/{lineItemId}/keywords
to allow Supply Accounts considered as eligible reviewers to review keywords from line-items of other accounts not included in their consent access (applicable for Private Market networks).

https://api.criteo.com/preview/retail-media/line-items/{lineItemId}/keywords
Sample Request
curl -L -X GET "https://api.criteo.com/preview/retail-media/line-items/358669652976373760/keywords" \
-H "Authorization: Bearer <MY_ACCESS_TOKEN>"
import requests
url = "https://api.criteo.com/preview/retail-media/line-items/358669652976373760/keywords"
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/preview/retail-media/line-items/358669652976373760/keywords');
$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();
}
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/preview/retail-media/line-items/358669652976373760/keywords")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <MY_ACCESS_TOKEN>")
.build();
Response response = client.newCall(request).execute();
Sample Response
{
"data": {
"id": "124848016959148032",
"type": "RetailMediaKeywordsModel",
"attributes": {
"keywords": {
"top": {
"reviewState": "InReview",
"matchType": "PositiveExactMatch",
"bid": 2.00,
"inputKeywords": {
"positiveExact": [
"top",
"tops"
]
},
"createdAt": "2024-05-17T02:12:52.1208996",
"updatedAt": "2024-05-17T02:12:52.8112947"
},
"t-shirt": {
"reviewState": "InReview",
"matchType": "PositiveExactMatch",
"bid": 1.00,
"inputKeywords": {
"positiveExact": [
"t-shirts",
"tshirt",
"t shirt"
]
},
"createdAt": "2024-07-25T05:13:47.766958",
"updatedAt": "2024-07-25T05:13:48.2112064"
},
"top woman": {
"reviewState": "InReview",
"matchType": "PositiveExactMatch",
"bid": null,
"inputKeywords": {
"positiveExact": [
"tops for women",
"women's top"
]
},
"createdAt": "2024-07-29T06:45:17.5861897",
"updatedAt": "2024-07-29T06:45:17.8158725"
}
}
}
}
}
Review Keywords for a Line-item
This endpoint allows retailers users to review (approve/reject) one or multiple keywords for the respective Line-Item ID. The response will return the full list of Keywords Reviews (including still InReview
, Approved
and Rejected
).
Once keywords are reviewed through this endpoint, the Keywords Approval Report (available in /accounts/{accountId}/keywords/in-review-report
) will reflect the updated amount of keywords left for review.
This endpoint is available only for API apps with Manage access to Campaign domain

https://api.criteo.com/preview/retail-media/line-items/{lineItemId}/keywords/review
Sample Request
curl -L -X POST "https://api.criteo.com/preview/retail-media/line-items/358669652976373760/keywords/review" \
-H "Authorization: Bearer <MY_ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"data": {
"attributes": {
"keywords": [
{
"phrase": "top",
"reviewState": "rejected"
},
{
"phrase": "t-shirt",
"reviewState": "approved"
}
]
}
}
}'
import requests
import json
url = "https://api.criteo.com/preview/retail-media/line-items/358669652976373760/keywords/review"
payload = json.dumps({
"data": {
"attributes": {
"keywords": [
{
"phrase": "vegetable",
"reviewState": "approved"
},
{
"phrase": "tomato",
"reviewState": "rejected"
}
]
}
}
})
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\":{\"keywords\":[{\"phrase\":\"vegetable\",\"reviewState\":\"approved\"},{\"phrase\":\"tomato\",\"reviewState\":\"rejected\"}]}}}");
Request request = new Request.Builder()
.url("https://api.criteo.com/preview/retail-media/line-items/358669652976373760/keywords/review")
.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":{"keywords":[{"phrase":"vegetable","reviewState":"approved"},{"phrase":"tomato","reviewState":"rejected"}]}}}');
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": {
"type": "RetailMediaKeywordsReviewResult",
"attributes": {
"keywords": [
{
"phrase": "top",
"reviewState": "Rejected"
},
{
"phrase": "t-shirt",
"reviewState": "Approved"
},
{
"phrase": "top woman",
"reviewState": "InReview"
}
]
}
},
"warnings": [],
"errors": []
}
Responses
Response | Title | Detail | Troubleshooting |
---|---|---|---|
🟢 200 | Call executed with success | ||
🟢 201 | Entity request created with success | ||
🔴 400 | Keyword Error | One or more keywords given are not found on this line item | Double-check the value of the keyword informed in the Review request; it should contain only existing keywords with "reviewState": "InReview" retrieved from Get Keywords endpoint |
🔴 400 | Model validation error | Error converting value "xyz" to type 'Criteo.RetailMedia.' | Value "xyz" provided is not a valid value for that respective parameter. Check the error details for more information |
🔴 403 | Forbidden | The account ID provided is not suitable for those endpoints (only supply accounts), the account is not included in the consent from the API app or the API app doesn't have Manage access to the Campaign domain/scope. Review the Types of Permissions in Authorization Requests |
Updated 13 days ago