---
updatedAt: 2026-10-01T22:27:53.000Z
agentTools:
  projectIndex: https://developer.sovrn.com/llms.txt
---

# Targeting

How targeting works and the accepted values for each attribute.

Every deal is either **targeted**, carrying one or more targeting groups, or
**[Run of Network](#run-of-network)**, which matches all eligible supply with
no targeting rules at all. Exactly one of `targeting` and `runOfNetwork` must
be present on a submission.

For targeted deals, within the targeting model:

* **Groups OR together**: a request matches if any group matches.
* **Rules within a group AND together.**
* **Values within a rule OR together.**

Each rule either includes or excludes its values via `match` (`include` is the
default; use `exclude` to negate).

A deal request carries **at most 25 targeting groups** and **at most 100
rules in total** across all groups. There is no limit on the number of values
in a rule, but the whole request body is capped at 1 MB (1,048,576 bytes), and
a larger one returns `413`. Exceeding either targeting limit returns a `400`
naming the limit:

```json
{ "message": "body/targeting at most 25 targeting groups per deal request" }
```

> 🚧 Only enumerated attributes and `publisher.id` are checked on submission
>
> A value that is not on its attribute's list is rejected with a `400` naming
> the values in question, on a dry run too. Every attribute whose
> `valueFormat` is `enumerated` is checked this way except `adUnit.size`, and
> the list for `content.categorization.v1` is the IAB's own Content Taxonomy
> 1.0 (see `content.categorization.v1` below). A 2-letter country code such
> as `US` is converted to its 3-letter code rather than rejected (see
> `device.geo.country` below). A publisher id that is not a
> positive whole number up to `2147483647` is rejected the same way (see
> `publisher.id` below).
>
> Other free-text attributes and ad sizes accept any value: one that doesn't
> match the formats below is **accepted** and then simply never matches
> traffic, with no error at any point. Send the exact formats shown here.

> 👍 Read the vocabulary live
>
> Rather than relying on this page staying current, you can read the accepted
> values live (see
> [Discovering attributes and values](#discovering-attributes-and-values)).
> `GET /targeting-attributes` lists every attribute and whether it has an
> enumerable set of values, and `GET /targeting-attributes/{attribute}/values`
> returns the values themselves. The endpoints are the source of truth; the
> tables below are a snapshot for reading. A value Sovrn adds, such as a new
> audience segment, can take up to about 30 minutes to appear there.

## Run of Network

To create a deal with no targeting rules, send `runOfNetwork: true` **instead
of** `targeting`:

```json
{
  "name": "Acme CTV Run of Network Q1",
  "adFormat": "video",
  "environments": ["ctv"],
  "runOfNetwork": true,
  "pricing": { "model": "auction", "floor": 12.0, "currency": "USD" },
  "commercial": { "takeRate": 0.15 },
  "schedule": { "startDate": "2030-01-01" },
  "buyers": [{ "partner": "thetradedesk", "seatIds": ["your-seat-id"] }]
}
```

A Run of Network deal matches **all eligible supply within its ad format and
its environments**: `adFormat` and `environments` still scope the deal; only
the targeting rules are absent.

* `runOfNetwork` and `targeting` are mutually exclusive; sending both (or
  neither) returns a `400`.
* The only accepted value is `true`; to make a deal targeted, send
  `targeting` instead.

In a single-deal response (`GET /deal-requests/{id}` and the `200` from a
`PATCH`), the deal carries a `runOfNetwork` boolean, and `targeting` is `null`
for a Run of Network deal.

**Switching modes after creation** works through `PATCH`: sending
`{ "runOfNetwork": true }` converts a targeted deal to Run of Network
(its targeting rules are removed), and sending a `targeting` array converts a
Run of Network deal back to targeted. This is also the only way to remove
targeting from a deal: `targeting` itself cannot be cleared or set to an
empty array.

## Discovering attributes and values

Two read-only endpoints expose the live vocabulary, so you don't have to track
changes to this page.

`GET /targeting-attributes` lists every attribute a rule may use, with a
`valueFormat` telling you whether its values can be enumerated:

```json
{
  "data": [
    {
      "attribute": "device.deviceType",
      "description": "OpenRTB device-type code as an integer, not a label. 3 is Connected TV.",
      "valueFormat": "enumerated",
      "valuesPath": "/sovrnos/v1/targeting-attributes/device.deviceType/values"
    },
    {
      "attribute": "site.domain",
      "description": "Registrable site domain, as free text — no scheme or path. For example \"espn.com\".",
      "valueFormat": "freeText",
      "valuesPath": null
    }
  ]
}
```

`valuesPath` is a path on `https://api.sovrn.com` that already includes
`/sovrnos/v1`. Request it on that host as it is; appending it to the base URL
would repeat `/sovrnos/v1`.

`GET /targeting-attributes/{attribute}/values` returns the accepted values for
an `enumerated` attribute, each with a label. Every value comes back as a
string, such as `"3"`, and `total` is always the number of entries in `data`.
An excerpt, cut to one value:

```json
{
  "attribute": "device.deviceType",
  "total": 1,
  "data": [{ "value": "3", "label": "Connected TV" }]
}
```

A `freeText` attribute has no list to return, so asking for its values responds
`400` rather than an empty array. Check `valueFormat` first, or follow
`valuesPath`, which is `null` exactly when there's nothing to enumerate.

## Attribute reference

### device.geo.country

Country as an **ISO 3166-1 alpha-3** code (three uppercase letters). Read the
current list from `GET /targeting-attributes/device.geo.country/values`.

Examples: `USA`, `GBR`, `CAN`, `DEU`, `JPN`, `AUS`.

A 2-letter **ISO 3166-1 alpha-2** code, such as `US`, is converted to its
3-letter code before the deal is created, so the deal is stored with `USA`
and reads back as `USA`. A value is accepted when it, or the 3-letter code
of a 2-letter one, is in that list. Anything else is rejected at submission,
on a dry run too, with a `400` naming the values as you sent them. Without
this check, a code the list lacks would pass a dry run and then fail the real
create with a `502` that doesn't name it:

```json
{
  "message": "body/targeting/0/rules/0/values country code(s) not available for targeting: \"ZZZ\", \"usa\". Send a code from GET /targeting-attributes/device.geo.country/values, or its 2-letter ISO 3166-1 form."
}
```

Codes are matched exactly, with no case folding, so `usa` and `us` are
rejected. Only the rejected values are named, each quoted as you sent it, so
a rule mixing valid and invalid codes tells you exactly which to fix.

### device.geo.region

A US state as its two-letter **postal code**, such as `CA`, `CO` or `KS`.
Read the current list from
`GET /targeting-attributes/device.geo.region/values`. A code that is not on
that list is rejected at submission, on a dry run too, with a `400` naming
it.

The region is looked up from the device's IP address and carries no country,
so put it in the same group as a `device.geo.country` rule for `USA`. Rules
within a group AND together, so the pair matches those states in the United
States only:

```json
{
  "name": "California and Colorado",
  "rules": [
    { "attribute": "device.geo.country", "values": ["USA"] },
    { "attribute": "device.geo.region", "values": ["CA", "CO"] }
  ]
}
```

### device.language

The device's language as a lowercase two-letter **ISO 639-1** code, such as
`en` or `es`. Today the list holds `de`, `en`, `es`, `fr` and `ja`; read the
current list from `GET /targeting-attributes/device.language/values`.

The code is matched exactly against the `device.language` the bid request
carries, so send it in lowercase as listed; a code that is not on the list is
rejected at submission, on a dry run too, with a `400` naming it. Example:
`["en", "es"]`.

### device.deviceType

OpenRTB device-type **code** (integer), not a label. Value discovery lists
each code as a string, such as `"3"`:

| Code | Device                    |
| ---- | ------------------------- |
| 1    | Mobile / Tablet (general) |
| 2    | Personal Computer         |
| 3    | Connected TV              |
| 4    | Phone                     |
| 5    | Tablet                    |
| 6    | Connected Device          |
| 7    | Set-Top Box               |
| 8    | Out-of-Home (OOH) device  |

Example: `[3]` targets Connected TV (listed by value discovery as `"3"`).

Its values are validated, as country codes are. A code that is not in
`GET /targeting-attributes/device.deviceType/values` is rejected at
submission, on a dry run too, with a `400` naming the codes as you sent them,
instead of passing a dry run and then failing the real create with a `502`:

```json
{
  "message": "body/targeting/0/rules/0/values device.deviceType value(s) not available for targeting: 99. Send a value from GET /targeting-attributes/device.deviceType/values."
}
```

Codes may be sent as integers or strings: `3` and `"3"` behave identically.
Only the rejected codes are named, each quoted as you sent it (`99` for an
integer, `"99"` for a string), so a rule mixing valid and invalid codes tells
you exactly which to fix.

### adUnit.size

A Sovrn size **token** (not a raw `WxH` string). Tokens are namespaced by medium
(`wd` web display, `wv` web video, `mwd` mobile web display, `mb` mobile in-app
banner, `mv` mobile video, `ctv` connected TV), as pixel tokens of the form
`{medium}.px.{width}x{height}`, plus category and aspect-ratio tokens. Send the
token exactly, e.g. `wd.px.300x250`.

<AccordionGroup>
  <Accordion title="Web display (wd)">
    `wd.px.120x600` · `wd.px.160x600` · `wd.px.200x200` · `wd.px.250x250` ·
    `wd.px.300x250` · `wd.px.300x600` · `wd.px.336x280` · `wd.px.468x60` ·
    `wd.px.728x90` · `wd.px.930x180` · `wd.px.970x90` · `wd.px.970x250`
  </Accordion>
  <Accordion title="Web video (wv)">
    `wv.px.400x225` · `wv.px.640x360` · `wv.px.854x480` · `wv.px.960x540` ·
    `wv.px.1280x720` · `wv.px.1920x1080` · `wv.square.px.600x600` ·
    `wv.square.px.1080x1080` · `wv.vertical.px.720x1280` ·
    `wv.vertical.px.1080x1920`. Category tokens: `wv`, `wv.square`, `wv.vertical`
  </Accordion>
  <Accordion title="Mobile web display (mwd)">
    `mwd.px.1x1` · `mwd.px.250x250` · `mwd.px.300x100` · `mwd.px.300x250` ·
    `mwd.px.320x50` · `mwd.px.320x100` · `mwd.px.336x280`
  </Accordion>
  <Accordion title="Mobile in-app banner (mb)">
    `mb.px.320x50` · `mb.px.300x250` · `mb.px.468x60` · `mb.px.728x90` ·
    `mb.px.320x480` · `mb.px.480x320` · `mb.fullscreen`
  </Accordion>
  <Accordion title="Mobile video (mv)">
    `mv.landscape.px.640x360` · `mv.landscape.px.854x480` ·
    `mv.landscape.px.1280x720` · `mv.landscape.px.1920x1080` ·
    `mv.square.px.600x600` · `mv.square.px.1080x1080` ·
    `mv.vertical.px.720x1280` · `mv.vertical.px.1080x1920`. Category tokens:
    `mv`, `mv.adaptive`, `mv.landscape`, `mv.square`, `mv.vertical`
  </Accordion>
  <Accordion title="Connected TV (ctv)">
    Creative: `ctv.creative.px.720x480` · `ctv.creative.px.960x540` ·
    `ctv.creative.px.1280x720` · `ctv.creative.px.1920x1080` ·
    `ctv.creative.px.3840x2160`<br />
    Player: `ctv.player.px.720x480` · `ctv.player.px.960x540` ·
    `ctv.player.px.1280x720` · `ctv.player.px.1920x1080` ·
    `ctv.player.px.3840x2160`<br />
    Aspect ratio: `ctv.ratio.4x3` · `ctv.ratio.16x9` · `ctv.ratio.21x9` ·
    `ctv.ratio.9x16`. Category tokens: `ctv`, `ctv.creative`, `ctv.player`,
    `ctv.ratio`
  </Accordion>
</AccordionGroup>

This list grows as Sovrn adds sizes. For the current set, call
`GET /targeting-attributes/adUnit.size/values`. If you need a size
that doesn't appear there, ask your Sovrn representative.

### audience.segmentids

Sovrn-assigned audience segment **IDs** (integers). Read the current list from
`GET /targeting-attributes/audience.segmentids/values`, which returns each id,
as a string, with its human-readable label. A new segment can take up to about
30 minutes to appear in that list and to be accepted in a rule.

Its values are validated, as country codes are. An id Sovrn doesn't
recognize is rejected at submission, on a dry run too, with a `400` naming
the offending ids, instead of passing a dry run and then failing the real
create with a `502`:

```json
{
  "message": "body/targeting/0/rules/0/values unknown audience segment id(s): 999999999999. Segment availability is account-specific — ask your Sovrn representative for the ids provisioned to you."
}
```

Ids may be sent as integers or strings: `366` and `"366"` behave identically.
Only the unrecognized ids are named, so a rule mixing valid and invalid ids
tells you exactly which to fix.

### content.categorization.v3

An IAB content category from the **v3** taxonomy, as its code. Read the codes
and their labels from
`GET /targeting-attributes/content.categorization.v3/values`. A code that is
not on that list is rejected at submission, on a dry run too, with a `400`
naming it.

Codes are matched exactly: a category does not include its subcategories. To
cover a whole branch, list the category and every subcategory under it, so a
rule with `"match": "exclude"` meant to keep a deal off all gaming content
lists the gaming category and each of its subcategories.

The rule takes v3 codes only. Codes from the IAB's separate v1 taxonomy, such
as `IAB9`, are not v3 codes: target them with `content.categorization.v1`.

### content.categorization.v1

An IAB content category from the **v1** taxonomy, IAB Content Taxonomy 1.0, as
its code: `IAB` and a category number from 1 to 26, such as `IAB9` (Hobbies &
Interests), or for a subcategory the category's code, a hyphen and the
subcategory's number, such as `IAB9-30` (Video & Computer Games). Read the 392
codes and their labels from
`GET /targeting-attributes/content.categorization.v1/values`. A code that is
not on that list is rejected at submission, on a dry run too, with a `400`
naming it:

```json
{
  "message": "body/targeting/0/rules/0/values content.categorization.v1 value(s) not available for targeting: \"IAB27\", \"IAB9-99\", \"iab9\". Send a value from GET /targeting-attributes/content.categorization.v1/values."
}
```

Codes are matched exactly, with no case folding, so `iab9` is rejected. A
category does not include its subcategories: `IAB9` matches content
categorized as `IAB9`, not content categorized only as `IAB9-30`. To cover a
whole branch, list the category and each subcategory under it: for Hobbies &
Interests, `IAB9` and `IAB9-1` through `IAB9-31`.

v1 and v3 are separate taxonomies with codes of their own, so a v3 code is not
a v1 code and each goes in a rule on its own attribute. A bid request's
categories come from one of the two, and a request that does not say which is
read as v1.

### geo.zip

Postal / ZIP code, as free text. Example: `["10001", "90210"]`.

### site.domain

Registrable site domain, as free text (no scheme or path). Example:
`["espn.com", "weather.com"]`.

### app.bundleId

App-store bundle identifier, as free text: an iOS numeric store ID or an Android
package name. Example: `["284035177", "com.example.app"]`.

### content.genre

Content genre, as free text. Example: `["comedy", "drama"]`. There is no list
to look up, so `GET /targeting-attributes/content.genre/values` answers `400`.

A bid request can carry several genres in its `content.genre`, separated by
commas. The rule matches when any one of those items equals one of its
values, ignoring case and the spaces around each item: `comedy` matches a
request whose genre is `Drama, Comedy`.

### publisher.id

A Sovrn publisher id: a **positive whole number**, sent as an integer or as a
string of digits, so `12345` and `"12345"` match the same publisher. The
largest publisher id is `2147483647`. Ask your Sovrn representative for the
publisher ids you want to target.

Any other value, such as `"abc"`, `12.5`, `-3`, `"0123"`, `" 123"` or
`2147483648`, is rejected at submission, on a dry run too, with a `400`
naming each value as you sent it. Without this check, a value that is not a
number at all would fail the create with a `502` that doesn't name it, and
the others would be stored and then never match:

```json
{
  "message": "body/targeting/0/rules/0/values publisher id(s) not valid: \"abc\", 12.5, -3. A publisher id is a positive whole number, such as 12345 or \"12345\"."
}
```