Ads.txt for CTV Explained

Ads.txt has helped protect the programmatic ecosystem from fraud and bad actors for nearly eight years now. The IAB Tech Lab published the original Ads.txt specification in 2017. Since then, it has enabled publishers to declare who is authorized to sell their inventory, providing DSPs with a tool to help eliminate counterfeit inventory from their platforms.

So, why am I writing an article about Ads.txt in 2025? When it comes to ads.txt, I thought it was a simple concept I could learn once, implement, and then forget about. However, the added complexity of CTV and, more recently, the growing sophistication of DSP enforcement have compelled me to take a closer look at how the specification has evolved.

Initially developed for the web only, ads.txt has undergone modifications over the years to keep up with behavioral trends shifting from the web to apps, mobile apps to CTV apps, and the business trend of syndicating video content outside a publisher's owned and operated apps. 

Publishers, buyers, or ad tech vendors who want to understand these developments and how they apply to CTV would have to pore through the various iterations of the Ads.txt spec and associated documents released by the IAB Tech Lab. To write this, I had to slog through:

Ads.txt and its menagerie of complimentary standards has quietly evolved into one of the more complex concepts that anybody working in programmatic should understand. The IAB Tech Lab has woven a wicked web of documentation to untangle and has successfully produced the most dry reading that I’ve ever had the displeasure of laying my eyes on. 

Fortunately for you, I’ve developed a complete immunity to dry ad tech documentation and created this guide to help you quickly understand how ads.txt applies to CTV. 

While this explainer cannot serve as a replacement for anyone implementing these technical standards (sorry, you have to read the documentation), I hope it will provide anyone who wants to learn with a solid understanding of how publishers and programmatic monetization managers set up ads.txt for CTV and how DSPs validate supply paths.

One Minute Ads.txt Refresher

We need to cover some basics before delving into more advanced CTV and ads.txt concepts. Here is your one-minute ads.txt refresher.

DSPs use Ads.txt to combat domain spoofing or sellers misrepresenting the origin of inventory. Sell-side platforms often offer tools to manipulate the values sent in a bid request, such as a web domain (example.com) or bundle ID (essentially a domain for web like com.example.androidTV).

Publishers do have valid reasons to manipulate these values (much to the annoyance of advertisers and DSPs). But bad actors also manipulate bid request values to either conceal the true origin of the inventory or inflate the value by pretending the request originated from a premium property.

Ads.txt enables publishers to declare who is authorized to sell their inventory publicly. DSPs then cross-reference these lists and have the option to reject bid requests from unauthorized sellers.

Let’s quickly look at a typical ads.txt workflow on the web:

  1. SSP sends a bid request for a publisher that contains a domain value and publisher.id value. Ex: publisher.com and 123456

  2. DSP takes the domain value and appends “/ads.txt” and checks that file. Ex. publisher.com/ads.txt

  3. DSP enforces internal or client rules based on ads.txt values.

In the ads.txt files, publishers list the SSPs they work with, along with their account IDs. 

Example:

pubmatic.com, 123456, DIRECT

If you see this line in a publisher’s ads.txt file, you know they work with Pubmatic, their account ID is 123456, and the publisher directly controls any inventory originating from that account.

Here is an example of a very neatly organized ads.txt file: https://www.accuweather.com/ads.txt.

AccuWeather even goes a step further, listing out each DSP they are working with by SSP (which is not a requirement). Notice the #? This symbol instructs DSP to disregard anything that follows it; it’s essentially internal commentary for Accuweather’s record-keeping purposes.

What are DSPs checking for, you ask? DSPs can either choose to apply platform-level ads.txt requirements or leave up enforcement to their clients through an account or deal targeting features. Some DSPs require a valid ads.txt, while others can leave that decision up to the clients. By “valid ads.txt” I mean:

  1. The ads.txt file exists (based on the domain sent in the bid request)

  2. The ads.txt file found on the domain from the bid request lists the account ID from the SSP initiating that same bid request.

  3. (Optional) The ads.txt listing is DIRECT (if the client or DSP chooses only to buy non-resold inventory)

Furthermore, they can check an SSP’s sellers.json to confirm the company behind the bid request.

Sellers.json is a file hosted by SSPs that lists all entities with accounts on the platform. Each entry in the file lists the entity's company name, account ID, company website, and what type of seller they are. This file enables the DSP to identify the exact company associated with an account and perform additional validations.

An example line in sellers.json for a publisher looks like this:

{

"seller_id": "123456",

"name": "Totally Legit Publisher LLC",

"domain": "publisher.com",

"seller_type": "PUBLISHER"

}

OR 

{

"seller_id": "78910",

"name": "Stinky Reseller Inc.",

"domain": "ilovearbitrage.com",

"seller_type": "INTERMEDIARY"

}

DSPs can use the additional information in the sellers.json file to further confirm whether it’s a direct publisher’s account initiating a bid request or an intermediary by validating the “seller_type” field.

DSPs can choose to use this information in further enforcement (e.g., not allowing any intermediaries). SSPs can also list accounts as “BOTH” – which some DSPs may frown upon, instead preferring to keep direct and reseller business on separate accounts for clarity.

Example sellers.json: http://pubmatic.com/sellers.json

Ok, maybe that was more than one minute, but trust me, I saved you at least an hour and a headache. 

CTV app-ads.txt

CTV leverages the app-ads.txt specification, which changes a few critical elements above. 

First, instead of using a domain value to find the ads.txt file for verification, they use an app store URL sent in a bid request to find a path to an app-ads.txt file. 

Once a DSP has an app store listing, they then need to look for a developer URL. They will use this URL to find the publisher’s app-ads.txt file by appending “/app-ads.txt” to the URL they find. 

This process should be easy if app stores follow the IAB Tech Lab guidelines and insert a piece of metadata into the HTML of an app store listing web page:

<meta name="appstore:developer_url" content="https://www.developerurl.com" />

This meta tag is a piece of metadata embedded in the HTML of a web page that is invisible to a typical web user but accessible to external systems interested in the information.

But if you navigate to those Apple app store examples above and view the page source, you’ll notice that meta tag is nowhere to be found in the web page HTML. Apple doesn’t do a small little thing to help make the programmatic advertising ecosystem a little better. Shocker!

Maybe Roku will be a little more accommodating. If we navigate to the Roku channel listing for Disney+:

We see that the very advertising-friendly company Roku added the required metadata to validate app-ads.txt:

<meta name="appstore:developer_url" content="https://disneyplus.com"/>

Any DSP validating Disney+ bid requests can then use the URL on the Roku channel listing page to build the URL to the Disney app-ads.txt file: disneyplus.com/app-ads.txt. From this point, a DSP can perform all the necessary validation as described in the prior section. 

This workflow is ideal if the CTV platform supports public app store listings for streaming apps; however, this is not always the case. For example, there are no public store listings for streaming apps on the Sony Playstation store — so DSPs cannot validate inventory originating from this platform.

As for the Apple App Store listing, DSPs would need to build a custom web page scraper to extract the developer URL from the "Developer Website" link on App Store pages.

SupplyChain Object

The SupplyChain object is a fun little diddy added in ORTB 2.6 or as an extension in ORTB 2.5. It allows DSPs to understand the provenance of any opportunity sent in a bid request and understand all the entities involved in a given transaction.

The SupplyChain object provides the DSP with insight into the number of hops between platforms or companies a transaction took, which they can use to eliminate multi-hop inventory or to understand exclusive reseller paths (where the publisher outsources monetization to another company).

DSPs can find the supply chain in the “source” object in an ORTB bid request. Here’s a multi-hop example with a reseller (resellerssp.com) and a publisher (publisherssp.com). 

"source": {
    "ext": {
      "schain": {
        "ver": "1.0",
        "complete": 1,
        "nodes": [
          {
            "asi": "publisherssp.com",
            "sid": "12345",
            "hp": 1
          },
          {
            "asi": "resellerssp.com",
            "sid": "6789",
            "hp": 1
          }
        ]
      }
    }
  }

The DSP can see a multi-hop sale and choose to ignore it, focusing instead on direct inventory, or they can also use this in conjunction with the two other ads.txt values below to gain a deeper understanding of a given transaction.

OWNERDOMAIN + MANAGERDOMAIN

These two values were incorporated into the ads.txt spec to establish a stronger connection between sellers.json files and to express how some multi-hop supply chain objects represent the most direct path to inventory.

OWNERDOMAIN is the website of the corporate entity that owns a website or app. Often, companies own many websites and apps, which may not always be tied back to a sellers.json file.

In an SSP’s sellers.json file, a publisher’s listing will typically only list the corporate entity itself, not all the websites or apps they own. So by placing an “OWNERDOMAIN” value in ads.txt files, it can allow a DSP to confirm the company that owns the property and link it back to the entity sending bid requests from an SSP via that SSP’s sellers.json file. 

MANAGERDOMAIN is another value a publisher can place in their ads.txt file to broadcast to the world that they have outsourced programmatic monetization to a separate company. They are informing everyone who wants to verify the ads.txt file that they have authorized a separate company to send bid requests for inventory on the publisher’s property. 

Publishers authorizing a MANAGERDOMAIN allow a DSP to confirm that it is the most direct path to the inventory despite the manager being a reseller. Let’s look at a multi-hop supply chain example that represents the most direct path to inventory:

A DSP receives a bid request from account ID 1234 (manager.com) from an SSP (ssp.com) for an opportunity on a CTV app. The bid request contains the following supply chain:

"source": {
    "ext": {
      "schain": {
        "ver": "1.0",
        "complete": 1,
        "nodes": [
          {
            "asi": "manager.com",
            "sid": "5678",
            "hp": 1
          },
          {
            "asi": "ssp.com",
            "sid": "1234",
            "hp": 1
          }
        ]
      }
    }
  }

After inspecting the app store URL of the CTV app from the bid request, the DSP extracts a developer URL of “ctvapp.com” and visits “ctvapp.com/app-ads.txt.” The DSP then finds these contents in the ads.txt file:

OWNERDOMAIN = publisher.com
MANAGERDOMAIN = manager.com

manager.com, 5678, DIRECT
ssp.com, 1234, RESELLER

From this, the DSP can ascertain that “publisher.com” owns the CTV app and is outsourcing programmatic monetization to manager.com. The DSP can then visit the sellers.json files of both the Manager (manager.com/sellers.json) and the SSP who sent the original bid request (ssp.com/sellers.json).

Even though the manager isn’t an SSP themselves, this situation calls for them to host a sellers.json file to allow DSPs to verify the corporate entities with which they do business and create a linkage between the publisher’s ads.txt file. 

manager.com/sellers.json:

{

"seller_id": "5678",

"name": "Publisher LLC",

"domain": "publisher.com",

"seller_type": "Publisher"

}

ssp.com/sellers.json:

{

"seller_id": "1234",

"name": "Manager Inc.",

"domain": "manager.com",

"seller_type": "Intermediary"

}

A DSP will now know that:

  1. Manager Inc. is the seller initiating the bid request (seller ID 1234 on ssp.com/sellers.json = Manager Inc.)

  2. Manager Inc. is the valid manager of Publisher LLC inventory, as ssp.com/sellers.json domain for seller ID 1234 = MANAGERDOMAIN listed in ctvapp.com/app-ads.txt (manager.com).

  3. Publisher LLC (publisher.com) has an authorized and direct relationship with Manager Inc. (manager.com) since Publisher LLC listed MANAGERDOMAIN = manager.com on ctvapp.com/app-ads.txt, and there is a DIRECT entry matching the seller ID in the supply chain object on manager.com/sellers.json.

As I mentioned at the beginning of the article, this is a complex process. Hopefully, you are still with me because we have one last ads.txt value to cover. 

Inventorypartnerdomain

CTV streamers run owned and operated apps, but they can also license their content to other apps, such as vMVPDs (e.g., Pluto, Tubi, Sling, etc.). These deals typically include inventory shares, where the vMVPDs will send ad requests to a content owner’s ad system, allowing them to monetize the opportunity. 

In this scenario, the opportunity is technically "direct," as the content owner owns the opportunity, as defined in an inventory share agreement. The vMVPD would send an ad request to the content owner’s SSP, which then initiates a bid request to a DSP.

However, things can get messy since the DSP will check the app store listing in the bid request, which will be the listing of a vMVPD app, not the publisher’s app. The DSP would then find the vMVPD's developer URL and check the app’s app-ads.txt file hosted there. 

The vMVPD would then be responsible for listing all of the content owner’s SSP seats and account IDs and marking them as 'DIRECT' to indicate that this inventory is owned directly by the content owner. This workflow is less than ideal for a couple of reasons:

  1. It creates confusion since the account IDs in each line in the vMVPDs app-ads.txt file would be a smattering of all the various content owners they license content from and have inventory sharing agreements with (along with their own).  

  2. It requires the vMVPD to update their app-ads.txt file every time a content owner changes or adds a new ad system.

Inventorypartnerdomain solves this challenge. The vMVPD can instead list a single line that points to the content owner’s app-ads.txt file.

Example:

inventorypartnerdomain=contentowner.com

The content owner will also pass this value in the site or app object of the bid request they initiate for inventory on the vMVPD app:

"app": {
    "bundle": "1234",
    "storeurl": "https://appstore.com/details/vMVPDapp/1234/",
    "inventorypartnerdomain": "contentowner.com",
    "publisher": {
      "id": "5678"
    }
  }

Now, the DSP first checks the vMVPD’s app-ads.txt file, which should contain a line listing “inventorypartnerdomain=contentowner.com.” Then they can proceed with checking contentowner.com/app-ads.txt, and there they should find:

ssp.com, 5678, DIRECT

That authorizes the original bid request sent from ssp.com and account = 5678. The content owner can now handle all ads.txt updates in their file independently of the vMVPD. 

Congratulations, you now have a rudimentary understanding of how ads.txt works in the context of CTV. As you may have noticed, creating a valid and verifiable CTV supply path is a complex process that involves many steps and the coordination of several parties. However, the result is a much safer programmatic ecosystem with fewer bad actors and illicit supply chains.

Reply

or to participate.