> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Targeting Options

> List, add, update, and delete targeting options and keep their Context IDs up to date with the Context Targeting API.

A targeting option (option) is a named set of Context IDs in a context targeting, such as "Sports" with `sport`, `baseball`, and `soccer`. In API paths and fields, an option is a `category`, and its Context IDs are `contexts`.

An option has one of two types:

* `atomic`: a list of Context IDs.
* `collection`: an option that matches its own Context IDs and those of its sub-options. See [Option groups](/integrations/context-targeting/option-groups#what-is-an-option-group).

Paths on this page are relative to `https://lake.adrop.io`. For authentication and request format, see [Basic Information](/integrations/context-targeting/overview#basic-information).

***

## Option Object

The endpoints on this page return options in this shape. The object holds the number of Context IDs, not the Context IDs themselves. To read them, use [Get an Option](#get-an-option).

| Field | Type | Description |
| - | - | - |
| `_id` | string | Option ID. A 26-character ULID issued by the server |
| `name` | string | Option name |
| `type` | string | `atomic` or `collection` |
| `contextCount` | number | Number of Context IDs the option matches. For an option group, the number of distinct Context IDs across its own Context IDs and those of its sub-options |
| `ownContextCount` | number | Option group only. Number of the option group's own Context IDs |
| `exposeChildren` | boolean | Option group only. Whether sub-options are shown to advertisers. `false` if it was never set |
| `children` | string\[] | Option group only. IDs of the first 50 sub-options, in the option group's order |
| `childCount` | number | Option group only. Total number of sub-options. If it's larger than the length of `children`, read the rest with the `parent` parameter of [List Options](#list-options) |
| `parent` | string | Sub-option only. ID of the option group the option belongs to |
| `createdAt` | string | Time the option was created (ISO 8601, UTC) |
| `updatedAt` | string | Time the option was last updated (ISO 8601, UTC) |

Fields that don't apply to an option are omitted.

<CodeGroup>
  ```json Regular option theme={null}
  {
    "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
    "name": "Sports",
    "type": "atomic",
    "contextCount": 3,
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```

  ```json Option group theme={null}
  {
    "_id": "01JAB3XK5SB1C2D3E4F5G6H7J8",
    "name": "Sports",
    "type": "collection",
    "contextCount": 3,
    "ownContextCount": 1,
    "exposeChildren": false,
    "children": ["01JAB3XK5TC1D2E3F4G5H6J7K8", "01JAB3XK5TC1D2E3F4G5H6J7K9"],
    "childCount": 2,
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```

  ```json Sub-option theme={null}
  {
    "_id": "01JAB3XK5TC1D2E3F4G5H6J7K8",
    "name": "Baseball",
    "type": "atomic",
    "contextCount": 1,
    "parent": "01JAB3XK5SB1C2D3E4F5G6H7J8",
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```
</CodeGroup>

***

## List Options

Returns the options of a targeting page by page, in ascending `_id` order (roughly the order they were created). Option groups and sub-options are included. Each item is an [option object](#option-object).

### Request

```http theme={null}
GET /context-targeting/{targetingId}/categories
```

Requires the `context-targeting:read` scope.

### Path Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `targetingId` | string | Yes | Targeting ID |

### Query Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `next` | string | No | Cursor for the next page. Pass the `next` value of the previous response. Omit it for the first page |
| `limit` | number | No | Maximum number of options per page, from 1 to 1,000. Default 1,000 |
| `parent` | string | No | Option group ID. Returns only the sub-options of that option group |

### Example Request

```bash theme={null}
curl "https://lake.adrop.io/context-targeting/01JAB3XK5Q8V7M2N4P6R9T0W1Y/categories?limit=1" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### Example Response

```json theme={null}
{
  "items": [
    {
      "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
      "name": "Sports",
      "type": "atomic",
      "contextCount": 3,
      "createdAt": "2026-10-13T01:23:45.000Z",
      "updatedAt": "2026-10-13T01:23:45.000Z"
    }
  ],
  "next": "01JAB3XK5RA1B2C3D4E5F6G7H8"
}
```

To get the next page, send the same request with `next=01JAB3XK5RA1B2C3D4E5F6G7H8`.

* The last page has no `next`. If you follow `next` until it's absent, you get every option exactly once.
* A page can have fewer items than `limit` even when more pages follow. Stop only when `next` is absent.
* There's no search by name. Filter the items by `name`, `parent`, or `children` on your side.

**Errors**

| Status | `errorCode` | When |
| - | - | - |
| 404 | `TARGETING_NOT_FOUND` | The targeting doesn't exist, belongs to another project, or isn't a context targeting |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | `parent` isn't an option of this targeting |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | `parent` isn't an option group |

***

## Get an Option

Returns an option with a page of its Context IDs.

### Request

```http theme={null}
GET /context-targeting/category/{categoryId}
```

Requires the `context-targeting:read` scope.

### Path Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `categoryId` | string | Yes | Option ID |

### Query Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `offset` | number | No | Position of the first Context ID to return, from 0 to 50,000. Default 0 |
| `limit` | number | No | Maximum number of Context IDs to return, from 1 to 10,000. Default 10,000 |

### Example Request

```bash theme={null}
curl "https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H8?offset=0&limit=10000" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### Example Response

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
  "name": "Sports",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T01:23:45.000Z",
  "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y",
  "contexts": ["sport", "baseball", "soccer"],
  "offset": 0,
  "limit": 10000
}
```

The response is the [option object](#option-object) with these fields added:

| Field | Type | Description |
| - | - | - |
| `targeting` | string | ID of the targeting the option belongs to |
| `contexts` | string\[] | Context IDs in this page |
| `offset` | number | `offset` of this page |
| `limit` | number | `limit` of this page |

* To read all Context IDs of an option, increase `offset` by `limit` until `offset` reaches `contextCount`.
* For an option group, `contexts` holds only the option group's own Context IDs, so page through them up to `ownContextCount`. To read the Context IDs of a sub-option, get the sub-option.

**Errors**

| Status | `errorCode` | When |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | The option doesn't exist, belongs to another project, or isn't in a context targeting |

***

## Add an Option

Adds an option to an existing targeting. You can add a regular option, an option group, or a sub-option of an existing option group.

### Request

```http theme={null}
POST /context-targeting/{targetingId}/category
```

Requires the `context-targeting:write` scope. The response is `201` with the new [option object](#option-object).

### Path Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `targetingId` | string | Yes | ID of the targeting to add the option to |

### Request Body

| Field | Type | Required | Description |
| - | - | :-: | - |
| `name` | string | Yes | Option name. It must be unique in the targeting, including option groups and sub-options. See [Name Rules](/integrations/context-targeting/errors#name-rules) |
| `contexts` | string\[] | No | Context IDs. For an option group, its own Context IDs. See [Context ID Rules](#context-id-rules) |
| `type` | string | No | `atomic` or `collection`. If omitted, the option is an option group when `children` is present (`[]` included) and a regular option otherwise |
| `children` | object\[] | No | Option group only. Sub-options to create with the option group. Each item has `name` (required) and `contexts`. Sub-options are always regular options |
| `exposeChildren` | boolean | No | Option group only. Shows sub-options to advertisers. Default `false`. See [Show Sub-options to Advertisers](/integrations/context-targeting/option-groups#show-sub-options-to-advertisers) |
| `parent` | string | No | ID of a top-level option group in the same targeting. Adds the new option as a sub-option of that option group |

The server issues all IDs. A body with `_id` or any other field not listed here fails with `400`.

These combinations fail with `400 TARGETING_CATEGORY_NOT_SUPPORTED`, and nothing is created:

* An option group with no sub-options and no Context IDs of its own
* `type: "atomic"` with `children`
* `exposeChildren` on a regular option
* `parent` with `type: "collection"`, `children`, or `exposeChildren`. An option added to an option group is always a regular option
* `parent` set to an option that isn't a top-level option group

One request can create up to 5,000 options, counting an option group and its sub-options, and can carry up to 10,000 Context IDs in total. To add many options in one request, use [Add Options in Bulk](#add-options-in-bulk).

### Example Request

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/01JAB3XK5Q8V7M2N4P6R9T0W1Y/category \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Tech",
    "contexts": ["tech", "ai", "mobile"]
  }'
```

### Example Response

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
  "name": "Tech",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T01:23:45.000Z"
}
```

<Note>
  A new top-level option can be added to the ad products that use the targeting, and to their campaigns. See [Effects on Ad Products and Campaigns](#effects-on-ad-products-and-campaigns).
</Note>

**Errors**

| Status | `errorCode` | When |
| - | - | - |
| 404 | `TARGETING_NOT_FOUND` | The targeting doesn't exist, belongs to another project, or isn't a context targeting |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | `parent` isn't an option of this targeting |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | One of the combinations above |
| 400 | `TARGETING_CONTEXT_LIMIT_EXCEEDED` | The option or the project would go over its Context ID limit. See [Context ID Rules](#context-id-rules) |
| 400 | `TARGETING_CATEGORY_LIMIT_EXCEEDED` | The targeting would have more than 100,000 options, sub-options included |
| 400 | `TARGETING_COLLECTION_CONTEXT_DUPLICATED` | One of the option group's own Context IDs is already an own Context ID of another option group in the project. See [Constraints](/integrations/context-targeting/option-groups#constraints) |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | An ad product would have more than 500 required options. See [Effects on Ad Products and Campaigns](#effects-on-ad-products-and-campaigns) |
| 409 | `TARGETING_CATEGORY_NAME_DUPLICATE` | The name is already used in the targeting, or repeats within the request. `detail.existingIds` holds the IDs of the existing options with that name. For a name that repeats within the request, `detail.duplicateOfIndex` is returned instead |

For all error codes and the error format, see [Error Codes](/integrations/context-targeting/errors#error-codes).

***

## Update an Option

Renames an option, changes its Context IDs, moves it into, out of, or between option groups, or changes whether an option group shows its sub-options to advertisers.

### Request

```http theme={null}
PATCH /context-targeting/category/{categoryId}
```

Requires the `context-targeting:write` scope. The response is `200` with the updated [option object](#option-object).

### Path Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `categoryId` | string | Yes | Option ID |

### Request Body

All fields are optional. Send only the fields you want to change. Fields you omit stay as they are.

| Field | Type | Description |
| - | - | - |
| `name` | string | New name. See [Name Rules](/integrations/context-targeting/errors#name-rules) |
| `contexts` | object | Changes to the Context IDs. Use `add`, `remove`, or both, or use `set` alone |
| `contexts.add` | string\[] | Context IDs to add. IDs already in the option are ignored |
| `contexts.remove` | string\[] | Context IDs to remove. IDs not in the option are ignored |
| `contexts.set` | string\[] | Replaces all Context IDs of the option with this list |
| `parent` | string or null | Regular options only. An option group ID moves the option under that option group. `null` detaches the option from its option group to the top level. See [Move or Detach an Option](/integrations/context-targeting/option-groups#move-or-detach-an-option) |
| `exposeChildren` | boolean | Option group only. Shows sub-options to advertisers |

These rules apply to the body:

* `contexts` must include `add`, `remove`, or `set`, and `set` can't be combined with `add` or `remove`. Otherwise the request fails with `400`.
* `parent` can't be combined with `contexts`. Move or detach an option in one request and change its Context IDs in another. `parent` can be combined with `name`. If the move fails, the name doesn't change either.
* Only `parent` accepts `null`. `null` in `name`, `contexts`, `exposeChildren`, `add`, `remove`, or `set` fails with `400`. To leave a field unchanged, omit it.
* An option group can't be moved. `parent` on an option group fails with `400 TARGETING_CATEGORY_NOT_SUPPORTED`, and so does `exposeChildren` on a regular option.
* For an option group, `contexts` changes its own Context IDs. Removing all own Context IDs of an option group that has no sub-options fails with `400 TARGETING_CATEGORY_NOT_SUPPORTED`.
* `name`, `contexts`, and `exposeChildren` are written together. If the Context ID change fails, the name and `exposeChildren` don't change. If you send both `add` and `remove`, `add` is applied first, so the added Context IDs stay even if `remove` fails.

You can rename a sub-option and change its Context IDs in the same way as any regular option. To choose between `add`/`remove` and `set`, see [Updating Context IDs](#updating-context-ids).

### Example Request

```bash theme={null}
curl -X PATCH https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H9 \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contexts": {
      "add": ["science"],
      "remove": ["mobile"]
    }
  }'
```

### Example Response

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
  "name": "Tech",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T02:10:00.000Z"
}
```

**Errors**

| Status | `errorCode` | When |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | The option doesn't exist in this project's context targetings, or `parent` isn't an option of the same targeting |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | One of the cases above, `parent` isn't a top-level option group, or the move would leave an option group with no sub-options and no Context IDs of its own |
| 400 | `TARGETING_CATEGORY_LOCKED` | You're moving a top-level option under an option group while an ad product requires it, or while a campaign that hasn't ended includes or excludes it |
| 400 | `TARGETING_CONTEXT_LIMIT_EXCEEDED` | The option or the project would go over its Context ID limit |
| 400 | `TARGETING_COLLECTION_CONTEXT_DUPLICATED` | A Context ID added to an option group is already an own Context ID of another option group in the project |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | Detaching the option would give an ad product more than 500 required options |
| 409 | `TARGETING_CATEGORY_NAME_DUPLICATE` | The new name is already used in the targeting. `detail.existingIds` holds the IDs of the options with that name |

***

## Delete an Option

Deletes an option and its Context IDs.

* Deleting an option group promotes its sub-options to the top level, and they take over the option group's settings in ad products and campaigns. If the option group had Context IDs of its own, they stop matching. See [Delete an option group](/integrations/context-targeting/option-groups#delete-an-option-group).
* You can't delete the last sub-option of an option group that has no Context IDs of its own. Delete the option group instead.

<Warning>
  Deleting an option isn't blocked when campaigns use it. Check [Effects on Ad Products and Campaigns](#effects-on-ad-products-and-campaigns) before you delete an option.
</Warning>

### Request

```http theme={null}
DELETE /context-targeting/category/{categoryId}
```

Requires the `context-targeting:write` scope. The response is `200` with the ID of the deleted option.

### Path Parameters

| Parameter | Type | Required | Description |
| - | - | :-: | - |
| `categoryId` | string | Yes | Option ID |

### Example Request

```bash theme={null}
curl -X DELETE https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H9 \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### Example Response

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9"
}
```

**Errors**

| Status | `errorCode` | When |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | The option doesn't exist in this project's context targetings. If you resent a delete, the first request already deleted it |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | The option is the last sub-option of an option group that has no Context IDs of its own |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | Deleting the option group would give an ad product more than 500 required options |

***

## Bulk Operations

The bulk endpoints add, update, or delete up to 5,000 options in one request. They require the `context-targeting:write` scope.

* Unless the request fails as a whole, each bulk endpoint returns `200` with a result for each item, in request order. Check `success` on each result. A failed item has an `error`. See [Bulk Responses](/integrations/context-targeting/errors#bulk-responses).
* A request with an invalid body fails as a whole with `400`, even if only one item is invalid. Examples are a Context ID that breaks the [Context ID Rules](#context-id-rules), `set` combined with `add`, or a field that isn't accepted.
* API writes for a project are processed one at a time. Send bulk requests one after another, not in parallel. See [Concurrent Writes](/integrations/context-targeting/errors#concurrent-writes).

Each request must stay within these limits:

| Limit | Add | Update | Delete |
| - | - | - | - |
| Items per request | 1–5,000 | 1–5,000 | 1–5,000 |
| Options created per request, sub-options included | 5,000 | – | – |
| Context IDs per request, all items combined | 10,000 | 10,000 | – |
| Option group items per request | 150 | 150 | 150 |

Option group items are:

* **Add**: items that create an option group (`type: "collection"` or `children`) and items with `parent`.
* **Update**: items with `parent`, `null` included.
* **Delete**: IDs of option groups and sub-options.

A request that breaks a limit fails as a whole with `400`, and nothing in it is applied. If a delete request has more than 150 option groups and sub-options, the error is `TARGETING_CATEGORY_TREE_ITEMS_LIMIT_EXCEEDED`, with the limit in `detail.limit` and the number of option groups and sub-options in your request in `detail.count`. To upload a large set, such as tens of thousands of options, send requests of up to 5,000 items one after another.

### Add Options in Bulk

Adds options to existing targetings. Each item can add to a different targeting.

#### Request

```http theme={null}
POST /context-targeting/category/bulk/create
```

#### Request Body

| Field | Type | Required | Description |
| - | - | :-: | - |
| `items` | object\[] | Yes | Options to add. Each item takes the fields of the [Add an Option](#add-an-option) request body |
| `items[].targeting` | string | Yes | ID of the targeting to add the option to |

A successful item has the new [option object](#option-object) in `data`.

#### Example Request

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Entertainment", "contexts": ["entertainment", "movie"] },
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Business", "contexts": ["business", "economy"] },
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Sports", "contexts": ["sport"] }
    ]
  }'
```

#### Example Response

The targeting already has an option named "Sports", so only the third item fails.

```json theme={null}
{
  "results": [
    {
      "index": 0,
      "success": true,
      "data": {
        "_id": "01JAB3XK5VD1E2F3G4H5J6K7M8",
        "name": "Entertainment",
        "type": "atomic",
        "contextCount": 2,
        "createdAt": "2026-10-13T02:20:00.000Z",
        "updatedAt": "2026-10-13T02:20:00.000Z"
      }
    },
    {
      "index": 1,
      "success": true,
      "data": {
        "_id": "01JAB3XK5VD1E2F3G4H5J6K7M9",
        "name": "Business",
        "type": "atomic",
        "contextCount": 2,
        "createdAt": "2026-10-13T02:20:00.000Z",
        "updatedAt": "2026-10-13T02:20:00.000Z"
      }
    },
    {
      "index": 2,
      "success": false,
      "error": {
        "statusCode": 409,
        "errorCode": "TARGETING_CATEGORY_NAME_DUPLICATE",
        "detail": {
          "existingIds": ["01JAB3XK5RA1B2C3D4E5F6G7H8"]
        }
      }
    }
  ],
  "succeeded": 2,
  "failed": 1
}
```

### Update Options in Bulk

Updates options. Each item can change a different option.

#### Request

```http theme={null}
POST /context-targeting/category/bulk/update
```

#### Request Body

| Field | Type | Required | Description |
| - | - | :-: | - |
| `items` | object\[] | Yes | Updates. Each item takes the fields of the [Update an Option](#update-an-option) request body |
| `items[]._id` | string | Yes | ID of the option to update |

* Each `_id` can appear only once per request. A repeated `_id` fails the whole request with `400`, so put all changes for one option into one item.
* Items are applied separately and can run at the same time. Results are still returned in request order.
* A name that an earlier item frees can be used by a later item in the same request.

A successful item has the updated [option object](#option-object) in `data`.

#### Example Request

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/update \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8", "contexts": { "add": ["basketball"] } },
      { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9", "name": "Technology" }
    ]
  }'
```

#### Example Response

```json theme={null}
{
  "results": [
    {
      "index": 0,
      "success": true,
      "data": {
        "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
        "name": "Sports",
        "type": "atomic",
        "contextCount": 4,
        "createdAt": "2026-10-13T01:23:45.000Z",
        "updatedAt": "2026-10-13T02:10:00.000Z"
      }
    },
    {
      "index": 1,
      "success": true,
      "data": {
        "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
        "name": "Technology",
        "type": "atomic",
        "contextCount": 3,
        "createdAt": "2026-10-13T01:23:45.000Z",
        "updatedAt": "2026-10-13T02:10:00.000Z"
      }
    }
  ],
  "succeeded": 2,
  "failed": 0
}
```

### Delete Options in Bulk

Deletes options. The same rules as [Delete an Option](#delete-an-option) apply to each ID.

<Warning>
  Don't delete an option group and its sub-options in the same request. Option groups and sub-options are processed in request order. If the option group comes first, its sub-options are promoted to the top level and then deleted as top-level options. If the sub-options come first and the option group has no Context IDs of its own, deleting the last sub-option fails and it stays.
</Warning>

#### Request

```http theme={null}
POST /context-targeting/category/bulk/delete
```

#### Request Body

| Field | Type | Required | Description |
| - | - | :-: | - |
| `ids` | string\[] | Yes | IDs of the options to delete |

A successful item has the ID of the deleted option in `data`.

#### Example Request

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/delete \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["01JAB3XK5RA1B2C3D4E5F6G7H8", "01JAB3XK5RA1B2C3D4E5F6G7H9"]
  }'
```

#### Example Response

```json theme={null}
{
  "results": [
    { "index": 0, "success": true, "data": { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8" } },
    { "index": 1, "success": true, "data": { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9" } }
  ],
  "succeeded": 2,
  "failed": 0
}
```

***

## Context ID Rules

These rules apply to every Context ID you send, in `contexts`, `children`, `add`, `remove`, and `set`.

| Rule | Description |
| - | - |
| Whitespace | Leading and trailing whitespace is trimmed, so `" sport "` and `sport` are the same Context ID. Spaces inside a Context ID are kept |
| Length | 1–256 bytes in UTF-8, after trimming. An empty or whitespace-only value isn't allowed |
| Commas | Not allowed |
| Characters | Any other characters are allowed, but the text must be well-formed Unicode. Unpaired surrogates aren't allowed |
| Case | Case-sensitive. `Sport` and `sport` are different Context IDs. Register them exactly as your pages send them in ad requests |
| Duplicates | Saved once. Duplicates in a request are merged, and adding a Context ID that the option already has changes nothing |

If any Context ID in a request breaks these rules, the whole request fails with `400`.

The same Context ID can be in more than one option. A request with that Context ID matches all of those options. The exception is an option group's own Context IDs, which can't be an own Context ID of another option group in the project. See [Constraints](/integrations/context-targeting/option-groups#constraints).

Context IDs are also limited by count and size:

| Limit | Value |
| - | - |
| Context IDs per option | 50,000. For an option group, this counts its own Context IDs |
| Total size per option | 8 MB, the sum of the Context IDs in UTF-8 |
| Context IDs per project | 200,000, summed over all options in all context targetings of the project. A Context ID in two options counts twice |
| Context IDs per request | 10,000, all items and `add`, `remove`, and `set` combined |

A change that would put an option or the project over its limit fails with `400 TARGETING_CONTEXT_LIMIT_EXCEEDED`. These limits are checked only when a change makes an option or the project larger, so a change that only removes Context IDs is never blocked. A request with more than 10,000 Context IDs fails as a whole with `400`. For all limits, see [Limits](/integrations/context-targeting/errors#limits).

***

## Updating Context IDs

Change Context IDs with `add`/`remove` or with `set` in [Update an Option](#update-an-option) or [Update Options in Bulk](#update-options-in-bulk).

| Method | How it works | Use it when |
| - | - | - |
| `add` / `remove` | Changes only the Context IDs you list | You get changes as they happen, such as a section being added or removed |
| `set` | Replaces all Context IDs of the option with your list | You have the full list for the option, and it has 10,000 Context IDs or fewer |

`add`, `remove`, and `set` give the same result if you send the same request again, so you can resend them after a timeout. See [Retries and Duplicate Requests](/integrations/context-targeting/errors#retries-and-duplicate-requests).

**More than 10,000 Context IDs**

A request can carry up to 10,000 Context IDs, so `set` can't replace a longer list in one request. Sync the option this way instead:

1. Read the option's current Context IDs with [Get an Option](#get-an-option), paging with `offset` and `limit`.
2. Compare them with your list.
3. Send only the differences with `add` and `remove`, up to 10,000 Context IDs per request, one request after another.

Don't split a full list into several `set` requests. Each `set` replaces all Context IDs, so a later `set` drops the Context IDs of an earlier one. Even one `set` followed by `add` requests leaves the option with only part of its list between requests, and ad serving can pick up that partial list.

Changes reach ad serving after a short delay. To check whether a change has been loaded, see [When Changes Take Effect](/integrations/context-targeting/overview#when-changes-take-effect).

***

## Effects on Ad Products and Campaigns

Ad products and campaigns select options, so changing an option can change where campaigns serve. Adding or removing Context IDs changes the pages where campaigns that include or exclude the option can serve.

**New top-level options**

When you add a top-level option, either a regular option or an option group, ad products that have settings for the targeting also get the option:

* If an ad product sets every current top-level option of the targeting to **Required Include**, the new option is also set to **Required Include**, and the product's campaigns that haven't ended include it too. **Required Exclude** works the same way.
* Otherwise, the new option is added to the ad product as an option advertisers can select. This is the case when some top-level options aren't required, or when they mix **Required Include** and **Required Exclude**. Campaigns don't change.

So for an ad product that requires every option, a new option widens where its campaigns serve (**Required Include**) or narrows it (**Required Exclude**). Once one top-level option of the targeting isn't required in the ad product, later new options are added as selectable options too. To require them, change the ad product's targeting in the console. See [Selling Targeting](/targeting/sell).

A sub-option added to an option group isn't added to ad products by itself. Instead, the option group also matches the sub-option's Context IDs. See [Add an Option to an option group](/integrations/context-targeting/option-groups#add-an-option-to-an-option-group).

**Limit on required options**

An ad product can have up to 500 options set to **Required Include** or **Required Exclude**, across all its targetings. If the rule above would put an ad product over this limit, the new top-level options aren't created and fail with `400 AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED`.

| Field | Description |
| - | - |
| `detail.limit` | Maximum number of required options per ad product |
| `detail.count` | Number of required options the ad product would have after the change. If several ad products go over the limit, the largest number |
| `detail.products` | IDs of the ad products that would go over the limit |

In a bulk request, every new top-level option for that targeting fails, and the other items are applied. Reduce the required options of the ad product in the console, then send the request again. The same limit applies when you delete an option group or detach a sub-option, because the option group's settings are copied to its sub-options.

**Deleting options**

Deleting an option isn't blocked when campaigns use it, unlike [deleting a targeting](/integrations/context-targeting/targetings#delete-a-targeting). The option is removed from the campaigns that select it. If it was the only option from the targeting that a campaign included, the campaign no longer requires any of the targeting's Context IDs. Instead of stopping, it can serve on requests with other Context IDs or with none.

Before you delete an option, check whether campaigns use it. In the console, an option with campaigns booked has the **In Use** status. See [Managing Targeting](/targeting/manage). Change or end those campaigns before you delete the option.

**Excluding Context IDs**

An option only lists the Context IDs to match. There's no condition for "not in this option". To keep ads off certain pages, put the Context IDs of those pages in an option and exclude the option in the ad product or campaign, for example with **Required Exclude** on the ad product. Don't build an option that lists every other Context ID.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.