From our own campaigns
Journal
What we learned buying display ads with our own money: supply and buying tests, field notes, and dated scorecards. A launched test is not a finding. A judged result is.
Start with
- Lab: Supply · 12 min readFour months of our own money on the open exchangeWe spent $2,671.30 of our own money on display ads from May 14 to September 23, 2026. Much went to slots that did nothing. Most of that waste was fixable.
- Notes · 2 min readCTR is a trapClick-through rate looks like a quality score on remnant display. On our flights it mostly measured how clicky the inventory was, not whether a person arrived.
- Lab: Measurement · 2 min readHow to judge display trafficUse IVT and click-to-page-view, not CTR. Zero invalid impressions means nothing is measuring if the creative has no vendor tag.
Everything, newest first
87 pieces
- Four months of our own money on the open exchangeWe spent $2,671.30 of our own money on display ads from May 14 to September 23, 2026. Much went to slots that did nothing. Most of that waste was fixable.Lab: Supply · 12 min read
- Build the web Worker before you deploy itwrangler deploy inside the web app can ship the existing OpenNext build. A new route then 404s on the live domain even though the laptop rendered it.Notes · 1 min read
- Incomplete coverage is not a clean bill of healthA scan can finish with zero findings and still have reviewed none of your delivery. Only a covered verdict may be shown as a real audit.Lab: Measurement · 1 min read
- IVT shows in Beeswax before it shows in the dashboardWith HUMAN FraudSensor attached, invalid impressions stopped reading zero. Neon rollups still do not store those fields. Viewability is still unmeasurable.Lab: Measurement · 1 min read
- Plan for more than one creative per campaignA larger adops seat ran a median of five creatives per line item. The public API supports associations. One banner is a test, not a rotation policy.Lab: Ideas · 1 min read
- Require AUTH_DIRECT and AUTH_RESELLERPlanads.txt is a targeting key with four values. All our past campaigns accepted unauthorized and unknown. A 5/5 test is running. This is a plan, not a result.Lab: Buying · 1 min read
- Split the key before you exclude itStored findings look like OPENX|ox/558246019. The exchange wants ox/558246019. The un-split key matches nothing and fails open.Lab: Supply · 1 min read
- The reporting user is not the other Beeswax loginTwo logins on one instance can see different advertisers. Falling back to the other username does not fix a 401. It silently audits someone else's book.Notes · 1 min read
- Why an applied finding came back openThe unique index only covered open and acknowledged. The night after apply, the scan inserted a second open row and un-made the decision.Notes · 1 min read
- Why audited spend uses Math.max, not a suminventory_agg and domain_agg are two views over the same auctions. The first production run reported $630.49 against $309.82 of real snapshot spend.Lab: Supply · 1 min read
- A compute bill from a one-minute cronA one-minute cron made 147 database transactions a minute with no user traffic. Fixes cut it to 24. Such crons do not belong on a scale-to-zero database.Notes · 2 min read
- A lifetime cap that silently became one dayAn optional lifetime budget became the daily budget on every Beeswax write path. The campaign looked uncapped in our database and stopped after day one.Lab: Buying · 2 min read
- ads.txt did not save this buy99.85 percent of observed Supply Bench spend was already ads.txt authorized. Unauthorized was $0.48. ads.txt did not remove the waste we actually found.Lab: Supply · 2 min read
- Agent-written creatives, deterministic claim checksAI generated ad creative can draft variants. They stay claim-guarded until landing-page claim extraction exists. Freeform claim_sources are not evidence.Lab: Ideas · 2 min read
- Attach measurement before you optimizeHUMAN FraudSensor (vendor 7, $0.05 CPM, add-on 44) went on the live test creatives on 2026-09-08. IVT became non-zero. Viewability is still unmeasurable.Lab: Measurement · 1 min read
- Authorized inventory can still be junkads.txt authorization answers who may sell a domain. It does not answer whether the traffic is usable. 99.85 percent of this buy was already authorized.Lab: Supply · 2 min read
- Bid down a bad placement instead of killing itPlanBid modifiers can scale a bid, for example multiplier 0.3, on placement, site, or inventory_source. That path is seat-verified and not productized.Lab: Supply · 1 min read
- Budget fields have to move togetherBeeswax rejects a line item with daily above lifetime. The first pacer moved only lifetime, and five campaigns were rejected. The job still read succeeded.Lab: Buying · 2 min read
- Click contamination watchlistDomains with implausible remnant click rates on our flights. msn.com repeated. Seven held 41.3 percent of Supply Bench clicks. Not a certified IVT list.Benchmarks · 2 min read
- Click macros that actually expandLive-proven on 2026-09-05. 195 optimize-daily landings expanded PLACEMENT_ID, INVENTORY_SOURCE, EXCHANGE_HANDLE, and DOMAIN. /rest/macro cannot enumerate them.Lab: Measurement · 2 min read
- Click-to-page-view benchmarks we trustMain-seat brand-name display flights ran 15.85 percent click-to-page-view when usable and 3.75 to 6.68 percent when not. We do not name the advertisers.Lab: Measurement · 2 min read
- Click-to-page-view by offerPlanPlan for a per-offer visit rate once the /sb join is complete for a judged wave. No invented offer rates on this page.Benchmarks · 1 min read
- Clicks, visits, and conversions are different metersExchange clicks, click tokens, landing visits, and conversions are four columns. Swap them and a dashboard shows engagement that never reached a page.Lab: Measurement · 2 min read
- Consumer probes vs product adsSupply-bench pages are not Adprime signup funnels. Success is a human completing an on-screen task. Mixing them poisons the quality read and product test.Lab: Ideas · 2 min read
- CPM now, CPA when the payload is honestPlanBeeswax CPA requires cpa_target, cpa_event, and max_bid. Live tests run as CPM with conversion measurement attached. CPA pacing is not write-verified.Lab: Buying · 1 min read
- Creative, audience, and page as one matrixMultivariate creative testing here is a nested experiment tree, not one big A/B. Each layer gets an ID, purpose, and stop rule so the stack stays readable.Lab: Ideas · 2 min read
- CTR is a trapClick-through rate looks like a quality score on remnant display. On our flights it mostly measured how clicky the inventory was, not whether a person arrived.Notes · 2 min read
- CTR is the trapClick-through rate on remnant display measures how clicky the inventory was. On our flights that was not the same as whether a person arrived.Lab: Measurement · 2 min read
- Curiosity that still tells the truthEthical quiz ads can be curious but cannot fake iPhone prizes or scarcity. Use real quizzes, calculators, and polls we render. The page must match the ad.Lab: Ideas · 2 min read
- Daily pacing, not lifetime pacingLifetime pacing on this seat requires an end date, and Adprime Zero campaigns may have no lifetime cap. Daily pacing needs no end date and returned 200.Lab: Buying · 2 min read
- Dayparting is not on the targeting catalogTargeting-keys has 50 keys and no clock. Bid modifiers have 66, including time_of_week and user_time_of_week. We missed dayparting by checking one catalog.Lab: Buying · 2 min read
- Dayparting uses the viewer's clockuser_time_of_week is the viewer's local time. It cannot deterministically spread fleet spend across the UTC day. time_of_week's zone is unresolved.Lab: Buying · 2 min read
- Deals vs open exchange on a small daily budgetPlanThis child seat has historically had zero deals. We will not invent a private-marketplace result. This page is the test we still owe.Lab: Supply · 2 min read
- Decisions need a revert commandA trafficking change that exists only in someone's head is not reversible. Write the revert command first. Applied and never judged is the failure.Notes · 2 min read
- Delivery modifiers as budget weightsPlanDelivery modifier terms are targeting, rank, and weight, plus a required fallback_weight. This seat has had zero delivery modifier objects.Lab: Supply · 1 min read
- Do not put a derived hour in a dedupe keyBeeswax bid_hour is a naive America/New_York clock. Appending Z stamped rows four hours early, duplicated an hour, and doubled spend from $24.46 to $48.92.Lab: Measurement · 2 min read
- Domain waste after we filter exchangesExchange excludes do not remove domain waste. Domain findings need seat-level reference lists. That is an honest product limit, not a custom_list myth.Lab: Supply · 2 min read
- Even pacing that never reached the exchangecampaigns.pacing was stored and shown, but CreateBeeswaxLineItemArgs had no pacing field. The buyer's even request never reached Beeswax for four months.Lab: Buying · 2 min read
- Four times we said the seat could not do itFour capabilities we published as absent after checking one endpoint. Placement excludes, dayparting, pacing, and schema were all on a different surface.Notes · 2 min read
- Freshness is not generated_atgenerated_at is when our API responded. It is not provider freshness. Use freshness_status, metric sources, and Beeswax reporting sync timestamps.Lab: Measurement · 2 min read
- Grain-level waste mixPlanRecurring plan. After each covered Supply Audit run, publish spend mix by exchange, placement, and site. This page does not invent a new run.Benchmarks · 1 min read
- How to judge display trafficUse IVT and click-to-page-view, not CTR. Zero invalid impressions means nothing is measuring if the creative has no vendor tag.Lab: Measurement · 2 min read
- How to traffic a campaign you can actually judgeAttach HUMAN FraudSensor add-on 44 at $0.05 CPM, request IVT and viewability, and judge on IVT and click-to-page-view. Never judge remnant display on CTR.Lab: Buying · 2 min read
- IVT after FraudSensor, viewability still unmeasuredFirst dated IVT window after add-on 44. September 8 was 0.388 percent. September 9 so far was 1.222 percent. Viewable impressions stayed 0.Benchmarks · 1 min read
- Label-cased report keysBeeswax async report rows come back with label-cased keys. campaign_id arrives as Campaign ID. A snake_case parser reads every metric as zero.Lab: Measurement · 2 min read
- Lead magnets that match the adA lead magnet for paid traffic is the secondary CTA after the promise. Send the prompt pack or checklist the banner named, not a generic newsletter.Lab: Ideas · 2 min read
- measurable_invalid_impressions is not the rateStrict IVT is invalid_impressions over impressions. Here, measurable_invalid_impressions often equals nearly every impression, so it gives a nonsense rate.Lab: Measurement · 2 min read
- Most placements we bought did nothingOn the 2026-09-02 Supply Bench pull, 93.4 percent of placements had zero clicks and still took 64 percent of observed spend.Lab: Supply · 2 min read
- msn.com showed up twicemsn.com ran about 3.7 percent CTR on the July 2026 learning flight and 5.35 percent on Supply Bench. The host repeated. The creative did not have to.Lab: Supply · 2 min read
- Name the campaign so a stranger can read the stackUse angle__targeting__inventory__pageTemplate__wave. A generic name like growth test hides what was isolated. Later operators should not need the chat.Lab: Ideas · 2 min read
- One promise from ad to page to CTAIsolated funnel ads repeat one sentence from banner to hero to button. The six angle keys are the first portfolio. Mixing them on a page teaches nothing.Lab: Ideas · 2 min read
- One trafficking change per dayAbout 5,000 impressions and 20 clicks a day is too little signal if two things moved. Write the revert command first. REVERT is a normal outcome.Lab: Buying · 2 min read
- Open exchange waste, mapped by grainThere is no Beeswax object named supply route. Reports show waste by placement, site, domain, and exchange. Only some grains can be excluded today.Lab: Supply · 2 min read
- OpenAI 429 is not always a rate limitHTTP 429 covers a transient rate limit and an exhausted credit balance. Classify insufficient_quota before you retry. A missing GPT signal is never a pass.Notes · 2 min read
- OPTIONS, not /fieldsBeeswax /rest/v2 object /fields 404s everywhere. DRF OPTIONS on the object returns actions.POST with real field metadata, including 66 bid-modifier keys.Notes · 2 min read
- Placement vs site vs domain: which exclude worksPlacement and site are direct keys. Domain and app_bundle need seat-level reference lists. The old custom_list line blocked 59 percent of findable waste.Lab: Supply · 2 min read
- Prepaid runway vs a paused portfolioauto_paused_daily_cap is a healthy state for a fixed-daily fleet. Zero running campaigns after the cap is correct. Read prepaid runway with auto-reload.Lab: Buying · 2 min read
- Prompt to campaign objectsAn AI prompt can launch ads by writing the same objects the dashboard, API, and MCP share. The prompt is the interface. The objects are the product.Lab: Ideas · 2 min read
- QPS is not targetingFront-end QPS filters decide which auctions enter the bidder and consume contracted QPS. Line-item targeting decides which already-received requests may bid.Lab: Supply · 2 min read
- Quarterly remnant quality letterPlanPlan for a calendar-quarter letter that restates what we measured, what we still cannot measure, and what we changed. No invented quarter yet.Benchmarks · 1 min read
- Reporting that doubled spend and paused the fleetEastern time stamped as UTC, plus a dedupe key on the derived hour, duplicated rows. Summed spend doubled from $24.46 to $48.92 and the fleet paused.Notes · 3 min read
- Reseller paths clicked 2.3x more than directAuthorized-Reseller CTR was 0.423 percent vs Authorized-Direct at 0.184 percent on the same creative. That is an arbitrage signature, not a quality win.Lab: Supply · 2 min read
- Resume day burst, then the sliceResume re-pushes the full envelope. The pacer must null paced_lifetime_budget_cents so the next tick writes the slice, or the day can burn in one hour.Lab: Buying · 2 min read
- Same creative, 28x CTR spread by SSPOPENX ran 0.111 percent CTR on 44 percent of spend. SMARTADSERVER ran 3.074 percent. THIRTY_THREE_ACROSS ran 18.18 percent. Same creative, same format.Lab: Supply · 2 min read
- Scan then exclude, since you cannot pick the list first1,073 zero-click placements cannot be known before a flight. The working order is deliver, read grain, then exclude at a key the seat can compile.Lab: Supply · 2 min read
- Segment targeting from a real catalogLiveRamp and Data Marketplace segments on this product resolve against 43,175 real third-party rows across 13 provider IDs. Unknown keys are rejected.Lab: Buying · 2 min read
- Seven domains produced two fifths of the clicksSeven domains produced 41.3 percent of Supply Bench clicks from 4.7 percent of impressions. That is a concentration finding, not a certified IVT verdict.Lab: Supply · 2 min read
- Six angles we are willing to pay to learnThe first paid portfolio is six promises, including AI chat, no sales call, and API quickstart. Each is a question we will spend to answer, not a slogan.Lab: Ideas · 2 min read
- Stop sending paid clicks to the homepageThe homepage is too general for a paid click. An isolated funnel is one ad promise, one page promise, one CTA. It is the June 2026 copy plan, not a result.Lab: Ideas · 2 min read
- Supply Bench wave scorecardDated remnant scorecard from August 2026 Supply Bench waves. Partial coverage. No IVT vendor. Not an industry average.Benchmarks · 2 min read
- Targeting and bid-modifier keys are different listsThis seat's targeting catalog has 50 keys. Bid modifiers have 66, including both clocks. Treating them as one list hid dayparting from us for three months.Notes · 2 min read
- Template variants inside one promisePlanLanding page template tests fix the angle and change the persuasion model. Short-direct, interactive-planner, and pain-led are styles, not new promises.Lab: Ideas · 1 min read
- Ten interaction pages we actually flewWave 2 went live August 27, 2026 on ten /sb paths, from confirm-seen through name-generator. These are consumer interaction landings, not product signup pages.Lab: Ideas · 2 min read
- The sentence that cost four monthsThe error "Bidding strategy CPM is not enabled for pacing" is true for CPM. The reach-out line is boilerplate. CPM_PACED accepts daily even pacing.Notes · 2 min read
- Time of week is counted in minutesuser_time_of_week counts minutes from Sunday midnight, 0 to 10079. [9, 12] is three minutes, not 9 to 12. An hours version would compile and never deliver.Lab: Buying · 2 min read
- Two spend sources, one auctionbeeswax hourly rows and beeswax_snapshot lifetime rows cover the same auctions. A plain sum doubles spend. Use greatest of max snapshot and sum of non-snapshot.Lab: Measurement · 2 min read
- Two things pacing at once stops deliveryOur pacer shrinks lifetime to a slice and exchange pacing spreads the daily budget. Both at once exhaust the slice mid-day. Only one mechanism may pace.Lab: Buying · 2 min read
- We built a pacer because we misread one errorPacing on CPM returns 400 because CPM is not paced. The seat has CPM_PACED. The reach-out line is boilerplate. Misreading it cost four months and a pacer.Lab: Buying · 2 min read
- We were measuring nothing and reading zero as fineCreatives had empty creative_addons. invalid_impressions and viewable_impressions read zero because nothing was measuring, not because traffic was clean.Notes · 2 min read
- Web-only still bought browser gamesenvironments=['web'] held. app_agg rows were 0. The flight still bought worldofsolitaire.com, wheelofnames.com, and tftcomps.gg.Lab: Supply · 2 min read
- What a 300x250 can honestly ask a stranger to doDisplay banner creative ideas we already flew are honest asks, like vote, hold for three seconds, scratch with no prize, or pick dinner. Not a fake win.Lab: Ideas · 2 min read
- What we learned by buying our own remnantWe bought open-exchange display to study supply for consumer advertisers. CTR, empty measurement add-ons, and a few domains lie with a straight face.Notes · 2 min read
- What we mean by a supply pathThere is no Beeswax object named supply route. A path is targeting plus lists plus deals plus modifiers plus reports, read back after delivery.Lab: Supply · 2 min read
- When to copy a campaign instead of editing itIterate ads without mixing tests by copying the campaign and changing one layer. Do not pause or remove the final active creative.Lab: Ideas · 2 min read
- Why a dashboard can show zero clicksbeeswax_snapshot writes clicks as 0. The performance endpoint skips it for clicks. Real clicks come from source=beeswax reporting. Request clicks, plural.Lab: Measurement · 2 min read
- Why an empty allowlist serves nothinginventory_source is an allowlist. An empty one matches no inventory, so the apply path returns 409 rather than ship a campaign that cannot serve.Lab: Supply · 2 min read
- Zero clicks that were a field nameRequest Beeswax Reporting delivery clicks as clicks, plural, not click. ad_click_tokens are redirect plumbing, not delivery truth. Corrected June 26, 2026.Notes · 2 min read