app-ads.txt Enforcement and Implementation: Why Your File Is Not Being Recognized

app-ads.txt is now mandatory for new AdMob apps. This is the operator guide to file format, developer URL setup, crawl failures, and the sellers.json cross-check.

app-ads.txt tells DSPs and SSPs which sellers are authorized to sell your app's inventory. Google made it mandatory for new AdMob app submissions in January 2025. The file must sit at the root of the developer domain listed in your store entry. The most common reason it is not recognized: the developer URL in the Play Store or App Store listing is missing, points to a redirect, or resolves to a domain where the crawler cannot find the file. Format errors, UTF-8 BOM, and robots.txt blocking are the next most common causes.

The current state of enforcement

The January 2025 AdMob enforcement change is not a soft gate. New apps submitted to AdMob cannot fully serve ads until app-ads.txt verification passes. This is a hard block on ad serving, not a warning state you can ignore while impressions keep flowing.

For apps that predate the mandate, Google has been rolling out verification requirements gradually through 2025, with proactive notifications to accounts that need to act. If you have not received a notification yet, that does not mean your file is correct. It means the rollout has not reached your account yet. The direction is universal enforcement.

DSP-side enforcement compounds the AdMob gate. The Trade Desk, DV360, and AppNexus have documented enforcement posture: when a bid request comes from a seller not listed in the publisher's app-ads.txt file, enforcement-aware DSPs filter the impression or deprioritize the supply path. The impression is requested, the DSP checks the file, and the bid does not come in. No error message is logged in your ad network dashboard.

The supply concentration data from PubMatic illustrates the fill consequence: 61% of app ad spend went to authorized inventory despite only 24% of monetized apps being authorized. Compliant publishers are competing for a larger share of demand-side budget. Non-compliant publishers are competing for a shrinking fraction of it, and they are doing so invisibly.

The specific pain is not a visible error. A publisher with a broken app-ads.txt does not get a banner in the AdMob console saying "fill rate dropped because of this file." They see CPMs softer than benchmarks, or fill rate lower than expected, with no obvious cause. This is the enforcement problem worth understanding before going into the mechanics.

For a full picture of what AdMob now requires at submission, see the AdMob Approval Guide.

The IAB spec in detail: what goes in the file

Each line in a valid app-ads.txt file has four comma-separated fields. The format is not flexible; parsers expect exactly this structure.

Field 1: Domain of the advertising system (google.com, applovin.com, liftoff.io).
Field 2: Your publisher account ID on that system (e.g., pub-0123456789012345 for AdMob).
Field 3: Relationship type. Either DIRECT or RESELLER.
Field 4 (optional, strongly recommended): Certification authority ID (e.g., f08c47fec0942fa0 for Google's TAG-certified status).

A correct AdMob DIRECT entry looks like this:

google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0

DIRECT vs RESELLER. This distinction matters for how DSPs evaluate your supply path.

DIRECT means your account has a direct commercial relationship with that advertising system, and that system sells your inventory. Use DIRECT for every ad network and exchange you signed up with personally: AdMob, AppLovin, Liftoff.

RESELLER means another authorized party resells your inventory through that advertising system. If a mediation layer routes your inventory through an exchange you did not sign up with directly, that exchange gets a RESELLER entry.

Getting this wrong has fill consequences. Buyers running supply path optimization (SPO) deprioritize RESELLER paths and look for the shortest DIRECT path to the publisher. An incorrect DIRECT entry for a supply path that is actually a reseller relationship creates a mismatch with the network's sellers.json, which SPO-aware buyers treat as a supply chain integrity problem.

Optional record types from the spec.

The CONTACT record specifies a contact address for the publisher. Format: contact=email@example.com. Useful for SSPs doing outreach.

The SUBDOMAIN record delegates to a subdomain's app-ads.txt. Format: subdomain=sub.example.com. The crawler then fetches sub.example.com/app-ads.txt. Use this only when that subdomain has an actual file at its root. Do not use it to route around file hosting constraints.

The OWNERDOMAIN record declares the parent domain of the publisher. Primarily used by CTV platforms declaring an ownership relationship. Most mobile app publishers do not need this record.

The INVENTORYPARTNERDOMAIN record is an extension for CTV and streaming platforms that represent multiple publishers. Not relevant for most mobile publishers.

Encoding. The spec requires plain text, UTF-8 encoded, without a byte order mark (BOM). Lines beginning with # are comments. Whitespace is trimmed by compliant parsers, but encoding artifacts (particularly the invisible BOM from Windows text editors) cause parse failures that are difficult to spot. Store the file with consistent lowercase for all domains and uppercase for DIRECT/RESELLER.

Where Google looks for your file: the developer URL chain

This is the most common source of silent failures, and it is worth understanding the full chain rather than the simplified version in Google's support pages.

Google Play. The developer URL is set in the Play Console under Store presence, Store listing contact details, Website. This field is the starting point for Google's crawler. If it is empty, the crawler has no domain to fetch from. If it points to a social media page, a redirect service, or a domain you do not control, the file will never be found regardless of where it is hosted.

Apple App Store. The relevant field is the Marketing URL in App Store Connect. This is where Google's crawler expects to find the developer website for iOS apps. The Support URL field is not used for this purpose. If the Marketing URL field is blank or set incorrectly, AdMob cannot locate the file.

The crawl path. Once Google has the developer domain from the store listing, it fetches https://[developer-domain]/app-ads.txt and http://[developer-domain]/app-ads.txt. Both variants are attempted.

The www subdomain rule. The IAB spec excludes www. from automatic cross-checking. If your developer URL is set to www.example.com, the crawler fetches www.example.com/app-ads.txt. It does not automatically also check example.com/app-ads.txt. Conversely, if your developer URL is the apex domain (example.com), the crawler does not check www.example.com. The file must be at the exact domain the store listing specifies. The safest approach is to set the developer URL to the domain where the file actually lives, and use a single 301 redirect if you need to serve from either variant.

The same logic applies to m. subdomains. A developer URL set to m.example.com resolves only to that subdomain.

Redirect handling. The crawler follows a limited number of redirects. A single 301 from www.example.com to example.com is typically followed. Multi-hop redirect chains (domain A to domain B to domain C) are less reliably followed and can cause the file to appear not found. Keep the redirect chain to one hop or eliminate it entirely.

robots.txt blocking. The crawler respects robots.txt. If robots.txt on the developer domain disallows crawling of /app-ads.txt or has a blanket disallow for any user agents, the file will not be fetched. Check this before diagnosing anything else.

Crawl delay. After setting or changing the developer URL, allow up to 24 hours for AdMob to complete the crawl and update the verification status. The AdMob console shows the current verification state in the app listing settings. Do not interpret a failure within the first 24 hours of a change as a persistent problem.

Common implementation mistakes

Each mistake below follows the same structure: what it is, why it breaks silently, and the specific fix. If you are auditing a file, run through this list in order.

Mistake 1: UTF-8 BOM in the file. The byte order mark is an invisible sequence of bytes (0xEF, 0xBB, 0xBF) that some text editors insert at the start of a UTF-8 file. Notepad on Windows and some older VS Code configurations do this by default. The IAB spec requires plain UTF-8 without BOM. A BOM-prefixed file causes the first record to have a non-printing character before the first field value, breaking parsing. The symptom: the file is reachable, the developer URL is correct, but AdMob reports the file as "formatted incorrectly." Fix: save the file with UTF-8 encoding without BOM. VS Code (with an explicit setting), Sublime Text, and Vim handle this correctly. When in doubt, validate with a hex viewer or a UTF-8 lint tool.

Mistake 2: Wrong MIME type. Web servers and CDNs may not serve .txt files with the correct MIME type. The IAB spec requires text/plain. Some hosting configurations return application/octet-stream or text/html for .txt files, causing parsers to reject the response. The symptom is a download prompt in the browser instead of plain text when you fetch the URL. Fix: set the MIME type explicitly. For Firebase Hosting, add a "mimeType": "text/plain" entry in firebase.json for the /app-ads.txt path. For Nginx: types { text/plain txt; }. For Apache: AddType text/plain .txt in .htaccess.

Mistake 3: File at the wrong path. The file must be at the root of the domain: /app-ads.txt. Not /ads/app-ads.txt, not /static/app-ads.txt, not /public/app-ads.txt. The crawler only fetches the root path. A file anywhere else returns 404. Fix: confirm the file is reachable at https://example.com/app-ads.txt before investigating anything else.

Mistake 4: Developer URL not set in the store listing. If the field is empty, the crawler has no domain. AdMob reports "Missing developer website." The fix requires a store listing update, which does not take effect immediately. Allow 24-48 hours after the listing update for AdMob to re-crawl.

Mistake 5: Developer URL points to a social profile or redirect service. Some developers set the developer URL to a Twitter/X profile, a Linktree URL, or a similar redirect service. The crawler fetches twitter.com/username/app-ads.txt and finds nothing. Fix: use a domain you control where you can place a file at the root. Firebase Hosting with a custom subdomain works. So does a minimal static site on GitHub Pages, Netlify, or Vercel.

Mistake 6: Account ID mismatch. The publisher account ID in the file (Field 2) must match the account ID in the advertising platform's records. For AdMob, this is the numeric publisher ID (pub-0123456789012345). A common error is using the app-level ID rather than the publisher-level ID, or copying an example value without replacing it. Fix: check AdMob Settings, Account information and compare the publisher ID character by character against what is in the file. One character off means the entry is syntactically valid but semantically wrong.

Mistake 7: Entries for networks the app does not use, or missing entries for networks it does. Extra lines for networks that do not bid on your inventory are ignored by parsers, but they create a maintenance burden and introduce stale entries over time. The more critical mistake is missing entries for networks the app actually uses because the file was copied from a template. That is silent fill loss. Audit the file against your actual mediation stack. See the Mediation SDK & Adapter Compatibility Guide for a reference on keeping the full network stack current.

Mistake 8: Using a SUBDOMAIN record incorrectly. A SUBDOMAIN record pointing to a non-existent path causes the crawler to return a 404 for the delegated domain and may cause the parent file to be treated as incomplete. Use SUBDOMAIN only when the subdomain has an actual app-ads.txt file at its root.

Running the AdMob Approval Checker on your app catches most of these before they cost you fill.

Verifying your file

The AdMob console. After publishing the file, check the app listing in the AdMob console under Apps, select your app, then App settings. The app-ads.txt verification status shows one of several states: Verified, Not found, Formatted incorrectly, or similar. The state updates after each crawl cycle, up to 24 hours. The console also has an option to request an expedited crawl when you need to confirm a fix quickly.

Google Play app status. The Play Console Developer Page surfaces app-ads.txt status for apps submitted under the January 2025 mandate. If the app has been submitted but ads are not serving, check both consoles.

Fetch the file directly. Navigate to https://[developer-domain]/app-ads.txt in a browser. If you see a download prompt instead of plain text, the MIME type is wrong. If you see a 404, the file path is wrong or the file does not exist. If you see a redirect, trace where it goes. This is the fastest first check.

Check the Content-Type header. Using browser developer tools (Network tab), check the Content-Type header on the response for /app-ads.txt. It should be text/plain. Any other value is a MIME type mismatch.

Check robots.txt. Fetch https://[developer-domain]/robots.txt and confirm there is no rule that disallows /app-ads.txt or all paths for all user agents.

Confirm the publisher ID. Cross-reference the pub- ID in the file against the value in AdMob Settings, Account information. One character off means the entry passes format validation but fails the account verification.

External validators. adstxt.guru/validator/ accepts URL, file upload, or paste, and detects formatting errors, invalid exchange domains, duplicate records, and case inconsistencies. AppBrain's app-ads.txt lookup (appbrain.com/app-ads-txt) shows which networks are declared for a given app and whether entries match known network IDs in AppBrain's database.

The AdMob Approval Checker runs these checks programmatically, including the MIME type check and developer URL resolution. The Mediation SDK Checker is the companion tool for auditing SDK and adapter compatibility alongside the file.

The sellers.json relationship

app-ads.txt and sellers.json are complementary transparency mechanisms. Understanding only one of them gives you half the picture on supply chain integrity.

What sellers.json does. Ad networks and exchanges publish a sellers.json file at their domain root (e.g., google.com/sellers.json). This file lists every publisher account on their platform that is authorized to transact, with a seller type designation: PUBLISHER, INTERMEDIARY, or BOTH.

The cross-reference chain. When a DSP receives a bid request, it can check the publisher's app-ads.txt to confirm the selling network is authorized. It can then check that network's sellers.json to confirm the publisher account ID is registered on that network. The two files together create an auditable chain from advertiser to publisher.

When a DIRECT entry in app-ads.txt corresponds to a publisher account ID that appears as PUBLISHER in the network's sellers.json, the supply path is clean. When a DIRECT entry in app-ads.txt corresponds to an account ID that appears as INTERMEDIARY in sellers.json, or an account ID that does not appear in sellers.json at all, SPO-aware buyers treat the path as suspect and may decline to bid.

What this means for AdMob publishers. For apps on AdMob, the sellers.json cross-reference works automatically as long as the publisher account ID in app-ads.txt matches the account ID registered in AdMob. Google's sellers.json is public at google.com/sellers.json and updated regularly.

For publishers using mediation. Each network in the app-ads.txt file maintains its own sellers.json. If a mediation network adds or changes publisher account IDs and the publisher's app-ads.txt is not updated, the cross-reference breaks for that network. The DSP sees an authorized-looking entry that does not resolve correctly in sellers.json, and the impression gets filtered. No error is logged on the publisher side.

For context on how the mediation layer affects supply path decisions, see Mediation Waterfall vs In-App Bidding, which covers the SPO implications of waterfall vs bidding architecture.

If you are monetizing across more than three or four ad networks through mediation and you are not certain whether your app-ads.txt entries match what those networks list in their sellers.json files, that is a supply chain audit gap. It costs fill every day without producing any error you can see. The free initial conversation is the right starting point: Book a free 30-minute call.

Multiple platforms: keeping the file consistent across Play Store, App Store, and web

One developer URL, one file. The app-ads.txt file lives at the developer domain root and covers all apps listed under developer accounts that point to that domain. A single file on example.com/app-ads.txt can cover an iOS app, an Android app, and a web property, as long as all store listings point to example.com as the developer URL.

Where each platform looks.

Google Play uses the URL from the developer's website field in the Play Console.

Apple App Store: Google's crawler uses the Marketing URL field in App Store Connect. If the Marketing URL field is blank, only the Support URL is available, and it may or may not be used. Set the Marketing URL explicitly.

CTV and streaming (Roku Channel Store, Amazon Fire TV): the app-ads.txt spec's INVENTORYPARTNERDOMAIN extension applies to CTV platform aggregators. For individual CTV app publishers, the principle is the same as mobile: the developer URL in the store listing points to the domain where the file lives. For Roku, the developer website in Channel Details is the starting point. Amazon Fire TV uses the website URL in the Amazon Developer Console app metadata.

The consistency problem. A publisher with apps on both iOS and Android sometimes uses different developer accounts or different store listing URLs. If the iOS app's Marketing URL and the Android app's developer website URL point to different domains, both domains need an app-ads.txt file. Publishers who fix the file on one domain and leave the other unupdated have one platform verified and one platform not.

Multiple apps under one developer account. All apps under the same developer account share the same developer URL. The file covers all of them. If you add a new network for one app, that network's entry needs to be in the shared file. There is one file per developer domain, covering all apps.

Format consistency. The file format is identical across platforms. There is no iOS-specific or Android-specific syntax. The only platform-specific element is where you set the developer URL.

When the file gets rejected silently and how to diagnose

This section is for the case where you have done everything "right" but AdMob still shows an error or the verification does not pass. Work through these steps in order. The most likely cause comes first.

Step 1: Confirm the developer URL is set in the store listing. Fetch the store listing for the app and navigate to the developer's page on that store. Confirm the developer website field is present and points to a domain you control. If the field is blank, nothing else in this list matters.

Step 2: Fetch the file directly. Navigate to https://[developer-domain]/app-ads.txt in a browser. Download prompt means wrong MIME type. 404 means wrong file path or missing file. Redirect loop means a server configuration problem. Plain text means the file is reachable.

Step 3: Check the Content-Type header. Using browser developer tools (Network tab), check the Content-Type header on the response for /app-ads.txt. It should be text/plain. Any other value is a MIME type mismatch.

Step 4: Check for UTF-8 BOM. Open the file in a hex editor or paste the raw content into a UTF-8 BOM checker. If the first three bytes are EF BB BF, the file has a BOM and needs to be re-saved without it.

Step 5: Check robots.txt. Fetch https://[developer-domain]/robots.txt. Confirm no rule disallows /app-ads.txt or all paths for all user agents.

Step 6: Check the redirect chain. Run curl -I https://[developer-domain]/app-ads.txt to see the response headers. If you receive a 301 or 302, trace where the redirect goes. More than two hops: flatten the chain.

Step 7: Confirm the publisher ID. Open the file. Compare the account ID in Field 2 of the Google entry against the publisher ID in AdMob Settings, Account information. Every character must match.

Step 8: Check the AdMob console status. Navigate to Apps, select your app, then App settings. The app-ads.txt section shows the current verification state and the last crawl time. If the state says "Not found" after the developer URL is correctly set, allow 24 hours from the last change and try the expedited crawl option.

Step 9: Check for subdomain or www mismatch. If the developer URL is set to www.example.com, confirm the file is at www.example.com/app-ads.txt. The apex (example.com/app-ads.txt) is not automatically checked. Add a redirect or serve the file from the exact subdomain specified in the store listing.

The AdMob Approval Checker runs the majority of these checks programmatically. If you want to work through the diagnostic without going through each step manually, that is the tool to use.

The CTV and OTT extension

The app-ads.txt spec was extended to cover connected TV and over-the-top inventory. Streaming apps on Roku, Fire TV, Samsung Tizen, and LG webOS face the same inventory spoofing risks as mobile apps, and the file structure is identical.

Implementation principle. The streaming app's store listing must include a developer URL, and that domain must host the file at its root. The format is the same as for mobile. No CTV-specific syntax exists.

The INVENTORYPARTNERDOMAIN record. This extension is used by CTV platform aggregators and content delivery networks that represent multiple publishers. Individual CTV app publishers do not typically use this record. It is for platforms acting as inventory intermediaries for a portfolio of channels.

Store-specific developer URL fields. Roku's Channel Store includes a developer website field in Channel Details. This is the URL the crawl uses. Amazon Fire TV uses the website URL in the Amazon Developer Console app metadata. Samsung and LG app stores have varying developer URL fields; the principle is the same but the exact console location differs by platform. Some CTV stores do not have a formal developer URL field in the same way Play Store does. For those platforms, the crawl mechanism may rely on the publisher's primary domain as registered with the ad network.

Buyer adoption. CTV-focused DSPs including The Trade Desk and Freewheel validate app-ads.txt for CTV inventory. Smaller CTV buyers are not yet uniformly enforcing it. The direction follows the mobile pattern: gradual enforcement with compliant publishers getting access to more demand-side budget.

Multi-platform publishers. If your mobile and CTV apps share the same developer URL, one file covers both. If the CTV app is under a different developer account with a different URL, that domain needs its own file. Add all relevant networks' CTV-specific entries to the file. Some networks have separate account IDs for CTV inventory versus mobile inventory. Confirm with each network before assuming the same pub- ID covers both.

For SKAN attribution on the iOS side of a multi-platform app setup, see SKAdNetwork 4.0 Conversion Value Setup for the measurement architecture that runs alongside your supply chain configuration.

Frequently Asked Questions

Why isn't my app-ads.txt being recognized by AdMob?

The most common cause is that the developer URL field is missing or incorrect in the Play Store or App Store listing. Google's crawler starts from that URL and fetches [developer-domain]/app-ads.txt. If the field is blank, points to a social profile, or resolves to a redirect chain the crawler cannot follow, the file is never found regardless of where it is hosted. The second most common causes are: the file exists but has a wrong MIME type (should be text/plain), the file has a UTF-8 byte order mark that breaks parsing, or robots.txt on the developer domain blocks the crawler. Allow 24 hours after any change for AdMob to re-crawl before diagnosing a persistent failure.

What goes in a valid app-ads.txt entry?

Each line in app-ads.txt requires four comma-separated values: the advertising system's domain, your publisher account ID on that system, the relationship type, and an optional certification authority ID. For AdMob, the correct DIRECT entry is: google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0. Replace the publisher ID with the one shown in AdMob Settings under Account information. The relationship type is DIRECT when you have a direct account with the ad network, and RESELLER when a third party is selling your inventory through that network. For each network in your mediation stack, use the exact entry that network specifies in its documentation, with your own publisher account ID substituted where indicated.

Where does Google look for my app-ads.txt file?

Google's crawler fetches the file from the developer URL registered in your app store listing. For Android apps, this is the website URL in the Play Console under Store presence, Store listing contact details. For iOS apps, this is the marketing URL in App Store Connect. Once Google has that domain, it fetches https://[domain]/app-ads.txt and http://[domain]/app-ads.txt. The file must be at the root of that domain, not in a subdirectory. The crawler does not automatically check both www.example.com and example.com; it checks whichever variant the store listing specifies. If the store listing field is blank, the crawl cannot begin and AdMob will report a missing developer website error.

Do I need separate app-ads.txt files for iOS and Android?

No. If your iOS and Android apps share the same developer URL in their respective store listings, one app-ads.txt file at that domain's root covers both. The file covers all apps listed under developer accounts that resolve to that domain. If your iOS app uses a different developer URL than your Android app, because of different developer accounts or different store listing URLs, then each domain needs its own file. The file format is identical across platforms; there is no iOS-specific or Android-specific syntax. For publishers also running on CTV platforms such as Roku or Fire TV, the same file at the same domain covers those apps as well, provided the CTV store listing points to the same developer URL.

What is the difference between DIRECT and RESELLER in app-ads.txt?

DIRECT means your publisher account has a direct commercial relationship with that advertising system and that system sells your inventory without an intermediary. Use DIRECT for every ad network and exchange you are personally signed up with, such as AdMob, AppLovin, or Liftoff. RESELLER means a third party that is itself authorized in your file is reselling your inventory through that advertising system. Use RESELLER for networks that access your inventory indirectly through a mediation layer or SSP you did not sign up with directly. The distinction matters because buyers running supply path optimization prefer the shortest DIRECT path to the publisher. An incorrect DIRECT label on a reseller relationship creates a mismatch with the network's sellers.json file, which signals a supply chain integrity problem and may cause SPO-aware buyers to avoid the path entirely.

How do I verify my app-ads.txt is working?

Start by fetching https://[developer-domain]/app-ads.txt in a browser and confirming you see plain text, not a download prompt or a 404 error. Check the Content-Type response header in browser developer tools and confirm it is text/plain. In the AdMob console, navigate to Apps, select your app, go to App settings, and check the app-ads.txt verification status. If it shows Not found or a formatting error after the file is in place, use the expedited crawl option in the console and wait up to 24 hours. For format validation, paste your file content into adstxt.guru's validator. For publisher ID verification, compare the pub- ID in the file character by character against the value in AdMob Settings under Account information.

A valid file is not the finish line

A file that passes format checks is a baseline, not a clean bill of health. The publishers leaving the most fill on the table are not usually the ones with obviously broken files. They are the ones with files that pass validation but have stale entries, wrong relationship types, or a sellers.json mismatch that enforcement-aware DSPs are quietly filtering. The error does not appear anywhere in the publisher's dashboard. The fill just comes in lower than it should.

If you want to know where your supply chain actually stands across all the networks in your mediation stack, that is what the free conversation is for. Book a free 30-minute call.