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
- google-gemini-api
- html5-canva
- next.js
- node.js
- python
- react
- tailwind
- typescript
- youtube
Log in or sign up for Devpost to join the conversation.