← All posts
5 min

27 days in: what we got wrong about CTV supply

Written by The Adbustr team

We turned on production traffic on October 16. Today is November 12. We promised ourselves we would write something honest about the first month, before the rationalizations set in. So here it is.

Junk traffic was not the biggest problem

We spent most of the summer on inbound filtering: mandatory device.ip and device.ua, a browser allow-list resolved from the user agent, adult and illegal category blocks on IAB25, IAB25-3 and IAB26, advertiser-domain blocklists on badv. We expected to spend the first month watching those filters fight for their lives.

What actually happened: they did their job quietly and nothing about them needed changing in the first month. Every rejected request landed in the log with the reason code that stopped it, we read the funnel every morning, and the shape of it barely moved.

The real problem was inventory consistency. CTV bid requests are not standardized the way mobile is. One of our larger publishers cycled through more shapes of the app.bundle field in two weeks than we had accounted for in the entire matcher. Some used reverse-domain. Some used app-store IDs. Some used freeform strings. A few sent the publisher's internal SKU. Our matchers exploded.

We shipped a normalization layer in week three. It resolves most of the format weirdness automatically. What it cannot resolve gets queued for a human to look at. We will probably be staring at queues like that for a while.

CPM range broke our floors

On mobile we knew the shape of the curve. Interstitial in one band, rewarded a little above it, banner well below. Predictable enough that a single bidfloor per zone was a defensible simplification.

On CTV the spread was not predictable. Filler inventory came in near the floor. A pre-roll on a kids edutainment FAST channel cleared an order of magnitude higher. The same publisher, the same content type, a different time of day, cleared somewhere in between. We rebuilt our floor logic twice in three weeks. The current version sets bidfloor per inventory class instead of running a single curve across the endpoint, and it is much closer to right.

White-glove onboarding is real

We had this idea that most publishers would self-onboard through the dashboard. We had a clean form, a sensible verification flow, and reasonable defaults.

Reality: every single publisher in the first batch had at least one thing wrong. A malformed app-ads.txt. A missing GDPR consent flag. Geo-IP that did not match the declared country of operation. A misconfigured SDK integration that double-fired impression pixels. None of these were anyone's fault. They were just the thousand small things that come from building integrations across an open ecosystem.

So we are doing it the slow way. White-glove onboarding while the publisher base is small. Someone on the team reviews every integration personally. We take a call with each one before we route any traffic. It is not scalable. It is the right thing to do at this stage.

Three things we would do differently

  • Ship the inventory normalization layer before going live next time. We thought we could iterate on it post-launch. We could not.
  • Build floor logic that adapts per inventory class from day one. Single-curve floors do not work in CTV.
  • Stop being optimistic about onboarding automation in year one. White-glove is the right answer until the data tells you otherwise.

We will write another one of these in 90 days. By then we should know whether the lessons stuck.

  • operations
  • ctv
  • post-mortem