For growth teams doing creative research at scale

How to compare 200 ads and find the pattern

Ask every ad the same question, in the same shape, and count the answers. The method has three parts: a fixed set of fields rather than free prose, one reading per ad with the session kept, and an aggregation step that happens in your own code rather than in a model. Prose is the part that does not scale: 200 paragraphs cannot be counted, and a model asked to summarise 200 paragraphs will produce a confident sentence that nobody can check.

By The Playhead team

We build the video layer these numbers are measured on. Every figure about Playhead comes from the source file it lives in.

Published 5 min read

The short version

  • Ask for fields, not for prose. 200 paragraphs cannot be counted; 200 rows of the same eight fields can.
  • One reading per ad, and keep every session_id. A second question across the library then costs a fraction of a second pass.
  • Aggregate in your own code. A model asked to summarise 200 answers produces one confident sentence and no way to check it.
  • Read the unanswered list per ad. An empty field and an unreadable ad are different findings, and a count that mixes them is wrong in a direction nobody notices.

Ask for fields, not for prose

The single decision that makes the difference between a study and a pile of paragraphs.

A reading of one ad should be prose, because a person reads it. A reading of 200 ads is data, because a spreadsheet reads it.

So the question changes shape. Instead of asking what happens in the ad, ask for the same short list of facts every time, and phrase each one so there is exactly one right answer in the file.

The eight fields, and why each one is answerable from the video
FieldWhat it asks forWhy it is countable
hook_secondsThe second the first shot changesA cut is a measurement, not an opinion
opens_onface, product, text, or sceneFour values, decided by what fills the first frame
cuts_per_minutePace across the whole adCounted from the cut list, not estimated
offer_secondsWhen an offer first appears on screenThe frame it appears in, or absent
offer_held_secondsHow long it stays upTwo frames and the distance between them
price_showntrue or falseIt is on screen or it is not
cta_secondsWhen the call to action arrivesThe frame it appears in, or absent
aspect9:16, 1:1 or 16:9, after letterbox bars are removedA property of the picture, measured before the reading

Running it across the library

One reading per ad. Everything after that is arithmetic in your own code.

One pass over the library, keeping every sessionts
import { Playhead } from "@playhead/sdk";

const playhead = new Playhead();

const SCHEMA = `Answer only with these fields, one per line:
hook_seconds, opens_on (face|product|text|scene), cuts_per_minute,
offer_seconds, offer_held_seconds, price_shown (true|false),
cta_seconds, aspect. Write "absent" when the ad does not show it.`;

const rows = [];
for (const url of library) {          // 200 Meta Ad Library links
  const ad = await playhead.ask({ url, question: SCHEMA });
  rows.push({
    url,
    session: ad.session_id,           // the next question is cheap
    fields: parse(ad.answer),
    unread: ad.unanswered,            // never fold this into "absent"
  });
}

The session_id per ad is the part worth keeping. A second question across the whole library, asked a week later, reads what each reading already found instead of looking at 200 videos again.

What we found

Not measured yet. This section is the reason the article is not published.

What goes wrong at 200 that never goes wrong at one

The library is not one shape. A 6 second vertical bumper and a 90 second wide explainer answer cuts_per_minute on completely different scales. Group by aspect and by duration before you compare anything, or the pattern you find is a pattern about format.

An empty column is a finding about the question. When a field comes back absent for most of the library, the usual cause is that the field was not answerable from the picture. Check ten of them by hand before you report the number.

One rule tuned on one category is not a rule. A finding from 200 direct-response ads in one vertical says nothing about brand film, and the honest version of the sentence names the library it came from.

The verdict

The method is the easy half. Ask for fields, keep the sessions, count in your own code, and separate absent from unreadable. The hard half is the library: a finding is only as good as the set it came from, and 200 ads from one category on one date is a claim about that category on that date. Say so in the write-up, and the number stays true for longer than the campaign.

Common questions

How do you analyse hundreds of video ads at once?

Ask every ad the same fixed set of fields rather than for a description, run one reading per ad through an API, and aggregate the fields in your own code. The part that does not scale is prose: 200 paragraphs cannot be counted, and asking a model to summarise them produces one sentence nobody can check.

Can this run automatically on new ads?

Yes. The reading is an ordinary HTTP call, so a scheduled job can run it on whatever is new in the library and append to the same table. A long job returns an id and fires a webhook when it settles.

What does a run of 200 ads cost?

The cost follows the length of the ads and the detail level, because both decide how many frames are read. One first look per ad plus a handful of follow-ups is the shape of the bill. See the pricing page for the credit figures.

Written by

The Playhead team

We build the video layer these numbers are measured on. Every figure about Playhead comes from the source file it lives in.

How we write these articles, and how to send a correction