The problem

Sponsorship on YouTube runs on private information. A creator with 300k subscribers has no way to know which brands already sponsor channels exactly like theirs. A brand has no way to see which creators its competitors are quietly buying every month. Both sides guess, and both sides guess badly.

The data to answer this is public — it's sitting in video descriptions and sponsor reads — but nobody has assembled it.

What it does

It maps who sponsors whom on YouTube, then answers the question each side actually has.

For creators — Sponsor Gap. Pick your channel. The tool finds your peer set (creators who share sponsors with you), takes every brand backing them, subtracts the ones already backing you, and ranks what's left by how many peers each brand reaches and how recently. The graph centres you, dims everything else, and circles the openings in grease-pencil red.

Bitdefender — sponsors 3 of your 7 closest peers. Most recently 4 weeks ago.

For brands — Creator Gap. The mirror image. Set your target audience — subscriber range, genre, country, language — and get the creators your category competitors already sponsor and you don't, ranked by fit.

Verifiability. Every claim opens to the videos behind it. Where a sponsorship came from a transcript, the link jumps to the exact second the read starts. You never have to take the tool's word for anything.

On top of that: brand momentum, dormant deals worth re-pitching, category share of voice, and where a creator is under-monetised versus their peers.

How we built it

The single most important architectural decision was to make ingestion completely offline.

YouTube's Data API gives you 10,000 quota units a day, and search.list costs 100 of them per call. A naive "user searches, we fetch" design burns the entire daily allowance in about fifty queries — across all users, not per user. Worse, YouTube blocks transcript requests from datacenter IPs, so the transcript half can't run on serverless at all.

So the pipeline runs on a laptop and produces exactly one artifact:

YouTube API → channels → videos → transcripts (Python)
                                → descriptions (no LLM)
                                → sponsor extraction (Gemini)
                                → normalisation → graph.json

The deployed app reads that committed file and makes zero network calls at request time. A clean clone runs with no API keys at all. search.list is never called; the uploads-playlist route costs 1 unit instead of 100, and the entire 100-channel corpus was built for roughly 290 quota units.

Extraction runs in two layers. Descriptions are pure string processing — URL parsing, affiliate-parameter detection, registrable-domain resolution — with no LLM and no network. Transcripts add timestamps and dedicated-read detection via Gemini, batched several videos per call. The description layer is the insurance policy, and it earned that name (see below).

Challenges

YouTube IP-blocked us mid-run. The transcript fetcher worked beautifully for 31 requests, then hit a wall — 152 consecutive IpBlocked errors, and the block was still live hours later. This is the exact failure the offline architecture was designed around, and the description layer carried the project without it.

Duplicate brands nearly ruined the graph. The first build produced 728 brand nodes for 77 creators. Digging in, 697 of them had exactly one creator — one-off outbound links, not sponsorships. Fixing it took an evidence gate (a brand must be in a curated gazetteer, carry an affiliate parameter, or appear twice), plus two ways of spotting a creator's own domain, plus the realisation that creators cite sources — Reuters, the NYT and Tom's Hardware were becoming some of the highest-degree nodes in the graph. 728 → 235.

A statistic that was confidently wrong. Brand momentum first reported 32 of 36 brands "rising", which is not a finding, it's an artifact. Ingestion caps at 40 videos per channel, so a channel uploading three times a week contributes only its last three months while a monthly uploader contributes eighteen — 58.6% of the corpus landed in the most recent 90 days against the 16.7% an even spread would give. Momentum now measures change in share of all activity, so the bias cancels. The distribution went from 32/1/3 to a believable 14 rising, 5 steady, 17 falling.

That one mattered most. Every test passed the whole time. It was only caught by checking the output against the raw data and asking whether the answer was plausible.

What we learned

Passing tests are not the same as correct answers. The three worst bugs in this project — the momentum artifact, a node-sizing bug that rendered the entire graph invisible while painting every node correctly, and a query parser that returned an empty graph for every unfiltered request — all typechecked, all linted, and none of them threw.

We also learned that the boring layer wins. The no-LLM description extractor produced the overwhelming majority of the data. The clever transcript pipeline got IP-blocked.

What's next

Widening the corpus beyond 100 channels, resolving shortened links to recover the brands currently hidden behind them, and pricing signals so a creator can see not just who to pitch but what to ask for.

Built With

Share this project:

Updates

Submission history