AdTech
ads.txt, sellers.json and the SupplyChain Object
How publishers, SSPs and buyers verify who is allowed to sell inventory, and how to debug the three files when they disagree.
Programmatic advertising grew up with an openness that fraudsters exploited. One classic scam, domain spoofing, involved selling impressions on a low-quality site while claiming to be a premium publisher. The industry responded with three related transparency standards that together let a buyer check, at bid time, whether the company asking them to spend is genuinely authorised to sell the inventory. This guide explains what each file is, how they connect, and how to troubleshoot when they do not.
Three files, three questions
| Mechanism | Published by | Question it answers |
|---|---|---|
| ads.txt (apps: app-ads.txt) | The publisher or app developer, on their own domain | Which companies are authorised to sell my inventory, and under what account? |
| sellers.json | An SSP or exchange, on its own domain | Which sellers do I represent, and are they the owner or an intermediary? |
| SupplyChain object (schain) | Added to each bid request by every intermediary | Through which systems has this specific request passed? |
ads.txt: the publisher's list
An ads.txt file is a plain text file placed at the root of a domain, for example https://publisher.example/ads.txt. Each non-comment line declares one authorised relationship. Here is a small example:
# ads.txt for publisher.example
# format: <ad system domain>, <account id>, <DIRECT|RESELLER>, <certification authority id (optional)>
ssp-one.example, 12345, DIRECT, a1b2c3d4e5f6a7b8
ssp-one.example, 98765, RESELLER, a1b2c3d4e5f6a7b8
exchange-two.example, pub-0001, DIRECT
google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0
OWNERDOMAIN=publisher.example
CONTACT=adops@publisher.example
Each record has comma-separated fields:
- Advertising system domain: the domain of the SSP or exchange the buyer will transact through.
- Publisher account ID: the publisher's account in that system. This must match what the system uses to identify the seller, and is the value that appears as
sidin the supply chain. - Relationship:
DIRECTif the publisher controls the account, meaning they have a direct contractual relationship.RESELLERif the account belongs to another party authorised to resell the inventory. - Certification authority ID (optional): an identifier, often the TAG registration ID, that helps verify the ad system. Google's line is a well-known example, with the ID
f08c47fec0942fa0.
Later versions of the specification added variable lines such as OWNERDOMAIN, MANAGERDOMAIN, CONTACT and SUBDOMAIN, which give buyers a way to identify the publisher and handle subdomains. For mobile and connected-TV apps, the equivalent file is app-ads.txt, which is hosted on the developer website listed in the app store entry.
Typical mistake: adding a new demand partner in the ad server but forgetting to update ads.txt. The partner's requests are then "unauthorised", and many buyers will not bid or will pay less, with no visible error on the publisher's side.
sellers.json: the seller's directory
On the other side, each SSP or exchange publishes a sellers.json file at the root of its domain. It lists the account IDs it holds and states who each one is:
{
"contact_email": "support@ssp-one.example",
"version": "1.0",
"identifiers": [{ "name": "TAG-ID", "value": "a1b2c3d4e5f6a7b8" }],
"sellers": [
{
"seller_id": "12345",
"name": "Publisher Example Ltd",
"domain": "publisher.example",
"seller_type": "PUBLISHER"
},
{
"seller_id": "98765",
"name": "Reseller Partner Inc",
"domain": "reseller.example",
"seller_type": "INTERMEDIARY"
}
]
}
The seller_type field is PUBLISHER when the account belongs to the content owner, INTERMEDIARY when it belongs to a reseller, or BOTH. Some sellers are marked confidential, in which case their name and domain are omitted, which buyers may treat with extra caution. The seller_id is the key that links this file to the account ID in a publisher's ads.txt.
schain: the route a request took
When a bid request passes through several intermediaries, each adds a node to the schain object so the buyer can see the entire path:
"schain": {
"ver": "1.0",
"complete": 1,
"nodes": [
{ "asi": "ssp-one.example", "sid": "12345", "hp": 1 },
{ "asi": "exchange-two.example", "sid": "pub-0001", "hp": 1 }
]
}
complete: 1 states that the chain is complete from the originating seller to the buyer. Each node includes the ad system (asi), the seller account (sid), and hp, which indicates whether that node participates in payment. A shorter chain generally means fewer fees taken and fewer places for something to go wrong, which is why buyers often favour direct paths.
How a buyer verifies a request
- Read the requestThe buyer sees the site domain and the schain nodes, such as ssp-one.example with seller ID 12345.
- Fetch ads.txtThe buyer's system retrieves publisher.example/ads.txt (usually crawled and cached) and looks for a line naming ssp-one.example with account 12345.
- Check the relationshipA DIRECT line confirms the publisher's own account. A RESELLER line confirms an authorised partner.
- Fetch sellers.jsonThe buyer checks ssp-one.example/sellers.json for seller ID 12345 and compares its type and domain with the claim.
- Validate each hopFor multi-node chains, each node's ad system must have authorised the next, and every
sidshould appear in the relevant sellers.json. - DecideA buyer's policy may bid, bid lower, or skip the request when something does not match.
Common failures and how to find them
| Problem | Why it happens | Fix |
|---|---|---|
| File not found, or served as HTML | Placed in the wrong directory, or a 404 page returned with status 200 | Serve as text/plain at the root of the exact domain, with a real 200 |
| Redirect problems | Redirect to a different domain, or chains of several redirects | Keep it to the same registrable domain and minimise hops |
| Typos in account ID | Extra spaces, wrong characters, or ID from the wrong account | Copy from the partner's dashboard; trim whitespace |
| Wrong relationship | Marked DIRECT for an account the publisher does not own, or the reverse | Follow the partner's instructions exactly |
| Missing line after onboarding | Partner added in the ad server only | Add the process step: new partner means ads.txt update, then verify |
| Subdomain not covered | Traffic on news.publisher.example but file only on the root | Publish per subdomain or use the SUBDOMAIN directive appropriately |
| sellers.json mismatch | Seller ID not present, or listed under a different domain | Ask the SSP to correct their file; check you use the correct account ID |
| Incomplete schain | An intermediary drops or fails to append its node | Raise with the partner; check the request payload for the nodes |
| Encoding and size problems | Byte-order marks, strange characters, or very large files | Save as UTF-8 without BOM; remove duplicates and unused lines |
A debugging routine
Start from the outside and work in. These commands use standard tools, and only need read access to public files:
# Check status, content type and redirects
curl -sSIL https://publisher.example/ads.txt
# Fetch the file and look for a specific partner
curl -sS https://publisher.example/ads.txt | grep -i "ssp-one.example"
# Fetch an SSP's sellers.json and find a seller id (requires jq)
curl -sS https://ssp-one.example/sellers.json | jq '.sellers[] | select(.seller_id=="12345")'
- Confirm the ads.txt URL returns a 200 status with a plain-text content type and the expected lines.
- For each demand partner, find the line, and check the account ID against the partner's dashboard.
- Look up that ID in the partner's sellers.json. It should exist, with the type you expect.
- Capture a real bid request and confirm the schain nodes and
sidvalues match steps 2 and 3. - If everything lines up and bids are still low, the problem is elsewhere: timeouts, floors or creative quality. Return to the staged troubleshooting sequence.
Keep it maintainable
Treat ads.txt as a managed asset rather than a file someone edits by hand. Store it in version control, review changes, run a validator in your deployment pipeline, schedule a periodic audit that removes defunct partners, and keep a record of who requested each line. Removing stale lines matters as well: each unused entry is an authorisation you no longer control.
Key takeaways
- ads.txt is the publisher saying who may sell; sellers.json is the seller saying who they are; schain is the path this request took.
- Each ads.txt line needs the right domain, account ID, relationship and, where applicable, certification authority ID.
- Buyers cross-check all three, and a mismatch can reduce bids or exclude the inventory entirely.
- The file must be served at the root as plain text with a 200 status. A friendly 404 page served with a 200 is a classic failure.
- Authorisation does not equal performance. Valid files will not fix timeouts, floors or broken creatives.
Related guides
Further reading
- IAB Tech Lab: ads.txt and app-ads.txt
- IAB Tech Lab: sellers.json
- IAB Tech Lab: OpenRTB and SupplyChain
About this guide. Written by the Kensaltensi engineering and QA team for educational purposes. Examples are illustrative, and standards evolve, so check the primary sources above before relying on a detail in production. If you spot an error or an out-of-date statement, email hello@kensaltensi.org and we will review and correct it.