Creative DNA for Ad Archives
Search every scene of every ad you have run, and find the ones that keep earning spend
Applies empty and reads Your own ad exports. Connect your own data before querying.
- scenes across 271 source videos
- 2,306
- face documents from the same archive
- 8,168
- brands in the corpus
- 9
- creatives carrying ad copy, out of 271
- 38
- median scene length
- 2.5s
- click to first answer
- 8.2 s
How these were measured
- 2,306 scenes across 271 source videos: Measured 2026-09-11 by paging the live ads-scenes collection (col_a96402bd17) to exhaustion. Median 5 scenes per video, mean 8.5, max 19; 39 of 271 videos yield exactly one scene.
- 8,168 face documents from the same archive: Live count on col_6cdf041caf, face_identity_extractor@v1 with scrfd_2.5g detection and quality scoring on.
- 9 brands in the corpus: 13 distinct brand_slug values, of which four are re-mints of the same brand under a -2 suffix, so nine brands by name.
- 38 creatives carrying ad copy, out of 271: Live count on ads-text (col_e682a390cd), measured 2026-09-11: 38 documents, 38 distinct ad_id, one chunk each, median 184 characters. Copy is supplied per creative rather than extracted, so coverage is whatever your own export carries. On the measured corpus that is 14%.
- 2.5s median scene length: At scene_detection_threshold 0.5 on source clips whose own median length is 29 seconds. Mean scene length 3.6s, max 29.5s.
- 8.2 s click to first answer: from clicking apply to the first search that answers, timed on production on 6 November 2026 by the harness that runs nightly
What’s inside
- Extractors
- multimodaltext
- Retrievers
- brand-scenes·feature_searchcopy-search·feature_searchcompetitor-product-monitor·feature_search → llm_filter
- Marketplace retrieversHosted checks that run beside it, no key
- IP screening: faces and brand marksCelebrity face matchBrand logo matchTrademarked slogan match
- Solution
- Creative DNA →
See it work
- ads holding the product up to camerabrand-scenes
scene embedding over the ad archive, filtered by brand_slug
- which ads open on a price claimcopy-search
the spoken and on-screen copy, searched as text
cleared- ads-scenes and ads-faces, both fed from the same object
- Face indexed
- Scene embedded
- route: cleared
How it's wired
Applies 1 buckets, 2 collections and 3 retrievers today; dashed parts ship commented out
Drag nodes to rearrange. Drag the background to pan, ctrl or cmd plus scroll to zoom. Click a node for its settings.
What it costs
Processing is priced the way a batch is charged: each bucket's minutes, images, pages or tokens times its rate card lines. The batch dry run in Studio gives the binding quote.
Models
- Multilingual E5 Large InstructServed on MixpeekEmbedding
- Vertex AI multimodalembedding@001Provider APIEmbedding
Collections and their documents
Reads video from creative_asset_url
| Field | Type | Required |
|---|---|---|
| document_id | string | set by Mixpeek |
| collection_id | string | set by Mixpeek |
| ad_id | string | yes |
| brand_slug | string | yes |
| brand_name | string | yes |
| ad_type | string | no |
| source | string | no |
| source_url | string | no |
| headline | string | no |
| primary_text | string | no |
| published_at | string | no |
mixpeek://multimodal_extractor@v1/vertex_multimodal_embeddingfrom Vertex AI multimodalembedding@001
Reads text from primary_text
| Field | Type | Required |
|---|---|---|
| document_id | string | set by Mixpeek |
| collection_id | string | set by Mixpeek |
| ad_id | string | yes |
| brand_slug | string | yes |
| brand_name | string | yes |
| ad_type | string | no |
| source | string | no |
| source_url | string | no |
| headline | string | no |
| primary_text | string | no |
| published_at | string | no |
mixpeek://text_extractor@v1
Retrievers
- brand-scenesfeature_search over ads-scenes; input query
- copy-searchfeature_search over ads-text; input query
- competitor-product-monitorfeature_search then llm_filter over ads-scenes; input query, document_id
Spend limits
- Daily spend budget in the manifest
- 1 USD a day; new batches pause once it is spent
- Acceptance run ceiling
- 1 USD
- Model cost
- Unmeasured for 2 of 2 models
Monthly cost at your volume is in the calculator. Served models start at zero replicas, so an idle namespace runs no model.
Reward signals
Nothing in this template writes interaction signals back, and it is worth saying so rather than implying a flywheel that is not wired.
How reviewer decisions move the thresholds
None. No thumbs, no saves, no relevance marks are recorded by the template as shipped.
None from the application. Mixpeek records retriever executions server-side in mxp_retriever_executions, which is telemetry about queries rather than feedback about results.
mxp_retriever_executionsAdding a learning loop means posting to /v1/retrievers/interactions when a user opens a result. That is a deliberate next step, not something this template does for you.
An ad archive you can query by what is on screen. Every creative is cut into scenes at ingest and the ad copy is searchable beside them, so a question like 'the hook where someone holds the product to camera' returns the moment rather than the file.
- One reference query returns ranked scenes with their ad, brand, start and end time, so a result is something you can open at the right second.
- Face search, finding every ad one creator appears in, runs on a dedicated or customer-hosted deployment. Face recognition is off on Mixpeek Cloud, so one click there deploys scenes and copy, and the face step ships commented out in the manifest.
- The ad copy lives in its own collection, so search reaches what was WRITTEN as well as what was SEEN, for the creatives whose export carries copy.
- Clusters group scenes into recurring visual themes per brand, which is what turns an archive into a view of what a brand keeps making.
For: Performance marketing and creative teams sitting on an ad archive nobody can search
- ad-creatives
- One object per creative: the video plus the ad metadata. ad_id, brand_slug and brand_name are required; the copy, headline, source and publish date are optional and travel with it.
- ads-scenes
- Scene-level documents with a multimodal embedding, timing, and the brand and ad they came from.
- ads-faces
- Face identity vectors, one document per detected face, with a quality score. Dedicated or customer-hosted deployments only; it is not created on Mixpeek Cloud.
- ads-text
- Chunked ad copy, searchable on its own or beside the visual match. Populated only for creatives whose export carries copy.
One file spins up the namespace. Generated from the workflow above. Also served at /templates/creative-dna.namespace.yaml.
# creative-dna: one manifest spins up the namespace.
# Platform manifest schema (GET /v1/discovery/schema). Validate with POST /v1/manifest/validate,
# apply with POST /v1/manifest/apply or the Deploy button. Wiring comes from the flow diagram:
# edges are bucket -> collection sources, collection -> retriever scope, retriever -> view.
# Applying a SECOND time, over a namespace this template already created: use
# POST /v1/manifest/apply?mode=create_missing, which creates what is missing and leaves
# what exists alone. The default, create_only, fails the WHOLE apply and rolls it back if
# any resource already exists, so an upgrade looks like a dead end without this. Use
# mode=upsert to also patch resources that exist but have drifted from this file.
version: '1.0'
metadata:
name: creative-dna
description: "Namespace template creative-dna. Generated from the flow diagram on mixpeek.com/templates/creative-dna."
namespaces:
- name: creative-dna
description: "Everything below lives in this namespace."
feature_extractors:
- name: multimodal_extractor
version: v1
- name: text_extractor
version: v1
# Daily spend budget, in dollars of what you are charged (1,000 credits a dollar).
# New batches pause once the namespace has spent this much today; queries are not capped.
# Raise it later on the namespace: PATCH /v1/namespaces/<id> {"spend_budget": {"daily_usd": N}}.
budget:
daily_usd: 1
# Data sources. A storage connection carries credentials, so it is created in Studio
# (or POST /v1/organizations/storage-connections) and synced into the bucket named here.
# ad-library-pull: manual, one-shot, your ad creatives and their copy -> bucket ad-creatives
buckets:
- name: ad-creatives
namespace: creative-dna
description: "Meant to be fed by ad-library-pull (manual, one-shot): upload files here or write them through the API. Nothing arrives until you do."
schema:
properties:
ad_id:
type: string
required: true
brand_slug:
type: string
required: true
brand_name:
type: string
source:
type: string
source_url:
type: string
headline:
type: string
primary_text:
type: string
required: true
ad_type:
type: string
creative_asset_url:
type: video
required: true
published_at:
type: string
collections:
- name: ads-scenes
namespace: creative-dna
description: "multimodal_extractor@v1 over bucket ad-creatives. Feeds competitor-product-monitor, competitor-product-shown, brand-scenes, creative-patterns."
source:
type: bucket
bucket: ad-creatives
feature_extractor:
name: multimodal_extractor
version: v1
input_mappings:
video: creative_asset_url
field_passthrough:
- source_path: ad_id
required: true
- source_path: brand_slug
required: true
- source_path: brand_name
required: true
- source_path: ad_type
- source_path: source
- source_path: source_url
- source_path: headline
- source_path: primary_text
- source_path: published_at
alert_applications:
- alert: competitor-product-shown
input_mappings:
- input_key: query
source: {source_type: constant, value: "competitor product shown"}
- input_key: document_id
source: {source_type: document_field, path: _internal.document_id}
enabled: true
- name: ads-text
namespace: creative-dna
description: "text_extractor@v1 over bucket ad-creatives. Feeds copy-search."
source:
type: bucket
bucket: ad-creatives
feature_extractor:
name: text_extractor
version: v1
input_mappings:
text: primary_text
field_passthrough:
- source_path: ad_id
required: true
- source_path: brand_slug
required: true
- source_path: brand_name
required: true
- source_path: ad_type
- source_path: source
- source_path: source_url
- source_path: headline
- source_path: primary_text
- source_path: published_at
enabled: true
# NOT APPLIED. Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
# The block below is correct and is emitted commented out so the rest of this
# manifest applies; an apply is all-or-nothing and would otherwise roll back.
# - name: ads-faces
# namespace: creative-dna
# description: "face_identity_extractor@v1 over bucket ad-creatives. Feeds cast-identification."
# source:
# type: bucket
# bucket: ad-creatives
# feature_extractor:
# name: face_identity_extractor
# version: v1
# input_mappings:
# video: creative_asset_url
# field_passthrough:
# - source_path: ad_id
# required: true
# - source_path: brand_slug
# required: true
# - source_path: brand_name
# required: true
# - source_path: ad_type
# - source_path: source
# - source_path: source_url
# - source_path: headline
# - source_path: primary_text
# - source_path: published_at
# enabled: true
retrievers:
- name: brand-scenes
namespace: creative-dna
description: "Searches ads-scenes across 1 feature index."
collections:
- ads-scenes
input_schema:
query:
type: text
required: true
description: "What to look for; searched across every index below"
stages:
- stage_name: search
stage_id: feature_search
parameters:
searches:
- feature_uri: "mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding"
query:
input_mode: text
value: "{{INPUT.query}}"
top_k: 100
fusion: rrf
final_top_k: 100
tags:
- template:creative-dna
# NOT APPLIED. Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
# - name: cast-identification
# namespace: creative-dna
# description: "Searches ads-faces across 1 feature index."
# collections:
# - ads-faces
# input_schema:
# query:
# type: text
# required: true
# description: "URL of the image or frame to match; encoded by the index below and compared against it"
# stages:
# - stage_name: search
# stage_id: feature_search
# parameters:
# searches:
# - feature_uri: "mixpeek://face_identity_extractor@v1/insightface__arcface"
# query:
# input_mode: content
# value: "{{INPUT.query}}"
# top_k: 50
# fusion: rrf
# final_top_k: 50
# tags:
# - template:creative-dna
- name: copy-search
namespace: creative-dna
description: "Searches ads-text across 1 feature index."
collections:
- ads-text
input_schema:
query:
type: text
required: true
description: "What to look for; searched across every index below"
stages:
- stage_name: search
stage_id: feature_search
parameters:
searches:
- feature_uri: "mixpeek://text_extractor@v1"
query:
input_mode: text
value: "{{INPUT.query}}"
top_k: 50
fusion: rrf
final_top_k: 50
tags:
- template:creative-dna
- name: competitor-product-monitor
namespace: creative-dna
description: "The alert's check: does the ad scene just written show a product from a brand other than the advertiser's. The search is scoped to that one scene, and an llm_filter watches it and keeps it only when it does. Materialises into competitor-product-shown."
collections:
- ads-scenes
input_schema:
query:
type: text
required: true
description: "What to look for; searched across every index below"
document_id:
type: text
required: true
description: "The written document this run is scoped to, supplied by the alert attachment"
stages:
- stage_name: search
stage_id: feature_search
pre_filters:
AND:
- field: _internal.document_id
operator: eq
value: "{{INPUT.document_id}}"
parameters:
searches:
- feature_uri: "mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding"
query:
input_mode: text
value: "{{INPUT.query}}"
top_k: 5
fusion: rrf
final_top_k: 5
- stage_name: judge
stage_id: llm_filter
parameters:
criteria: "Keep this ad scene only if it clearly shows a product or its packaging from a brand other than {{DOC.brand_name}}, the advertiser. Discard scenes that show no product, or only {{DOC.brand_name}} products."
include_media: true
tags:
- template:creative-dna
clusters:
- name: creative-patterns
namespace: creative-dna
description: "Groups ad scenes across brands by what they show, so recurring visual patterns can be browsed and named."
source_collections:
- ads-scenes
cluster_type: vector
vector_config:
feature_uris:
- mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding
clustering_method: kmeans
algorithm_params:
n_clusters: 6
llm_labeling:
enabled: true
provider: google
model_name: gemini-2.5-flash
labeling_inputs:
input_mappings:
- {input_key: text, source_type: payload, path: headline}
- {input_key: image_url, source_type: payload, path: thumbnail_url}
alerts:
- name: competitor-product-shown
namespace: creative-dna
description: "Emails the org notifications address (Settings -> Notifications; unset means opted out) when a newly indexed ad scene shows a competitor's product."
source: retriever
retriever: competitor-product-monitor
trigger_on: results
notification_config:
channels:
- channel_type: email
include_matches: true
include_scores: true
enabled: false
# Eight public-domain television commercials from the Prelinger Archives on
# archive.org, ingested by one bounded batch after apply, so a first deploy
# can search real ads. Each seed object carries the bucket's required fields
# beside its video. Two brands share a product in three pairs (coffee, cereal,
# soap and detergent), which is what the competitor and copy searches compare.
seed:
- bucket: ad-creatives
max_objects: 8
objects:
- blobs: {creative_asset_url: "https://archive.org/download/NorelcoSpeed/NorelcoSpeed_512kb.mp4"}
ad_id: sample-norelco-speedshaver
brand_slug: norelco
brand_name: "Norelco"
source: archive.org
source_url: "https://archive.org/details/NorelcoSpeed"
headline: "Norelco Speedshaver"
primary_text: "A vintage television commercial for the Norelco Speedshaver electric shaver."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/folgers_10/folgers_10_512kb.mp4"}
ad_id: sample-folgers-coffee
brand_slug: folgers
brand_name: "Folgers"
source: archive.org
source_url: "https://archive.org/details/folgers_10"
headline: "Folgers Coffee"
primary_text: "A vintage television commercial for Folgers Coffee."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/sanka_coffee/sanka_coffee_512kb.mp4"}
ad_id: sample-sanka-coffee
brand_slug: sanka
brand_name: "Sanka"
source: archive.org
source_url: "https://archive.org/details/sanka_coffee"
headline: "Sanka Coffee"
primary_text: "A vintage television commercial for Sanka, a decaffeinated coffee."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/tide/tide_512kb.mp4"}
ad_id: sample-tide-detergent
brand_slug: tide
brand_name: "Tide"
source: archive.org
source_url: "https://archive.org/details/tide"
headline: "Tide"
primary_text: "A vintage television commercial for Tide laundry detergent."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/ivory_soap/ivory_soap_512kb.mp4"}
ad_id: sample-ivory-soap
brand_slug: ivory
brand_name: "Ivory"
source: archive.org
source_url: "https://archive.org/details/ivory_soap"
headline: "Ivory Soap"
primary_text: "A vintage television commercial for Ivory bar soap."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/kelloggs_variety_pak/kelloggs_variety_pak_512kb.mp4"}
ad_id: sample-kelloggs-variety-pak
brand_slug: kelloggs
brand_name: "Kellogg's"
source: archive.org
source_url: "https://archive.org/details/kelloggs_variety_pak"
headline: "Kellogg's Variety Pak"
primary_text: "A vintage television commercial for the Kellogg's Variety Pak of breakfast cereals."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/post_grape-nuts_flakes/post_grape-nuts_flakes_512kb.mp4"}
ad_id: sample-post-grape-nuts-flakes
brand_slug: post
brand_name: "Post"
source: archive.org
source_url: "https://archive.org/details/post_grape-nuts_flakes"
headline: "Post Grape-Nuts Flakes"
primary_text: "A vintage television commercial for Post Grape-Nuts Flakes breakfast cereal."
ad_type: video
- blobs: {creative_asset_url: "https://archive.org/download/amc_jeep/amc_jeep_512kb.mp4"}
ad_id: sample-amc-jeep
brand_slug: jeep
brand_name: "Jeep"
source: archive.org
source_url: "https://archive.org/details/amc_jeep"
headline: "AMC Jeep"
primary_text: "A vintage television commercial for the AMC Jeep."
ad_type: video
acceptance:
max_cost_usd: 1
steps:
- name: ad-scenes-indexed
kind: documents
collection: ads-scenes
expect: {min_documents: 8}
- name: ad-copy-indexed
kind: documents
collection: ads-text
expect: {min_documents: 8}
- name: brand-scenes-answers
kind: query
retriever: brand-scenes
inputs: {query: a cup of coffee}
expect: {min_results: 1}
- name: copy-search-answers
kind: query
retriever: copy-search
inputs: {query: breakfast cereal}
expect: {min_results: 1}Read from the manifest, so a part it ships commented out never shows as ready.
What each part needs
- ads-scenes
- Deploys and processes documents as they arrive.
- ads-text
- Deploys and processes documents as they arrive.
- brand-scenes
- Deploys and answers queries once its collections hold documents.
- copy-search
- Deploys and answers queries once its collections hold documents.
- competitor-product-monitor
- Deploys and answers queries once its collections hold documents.
- creative-patterns
- Deploys and processes documents as they arrive.
- ad-library-pull
- You upload these files yourself. There is no connection to sync.
- ads-faces
- Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
- cast-identification
- Face recognition is turned off on Mixpeek Cloud, so this face step does not deploy. It runs on a dedicated or customer-hosted deployment, where face_identity_extractor is served.
| Component | Role | Source | Today |
|---|---|---|---|
| multimodal_extractor@v1 | Cuts each creative into scenes and embeds themRun with split_method scene, scene_detection_threshold 0.5 and time_split_interval 10, which is the configuration the measured numbers above came from. | Built in | Deploys ready |
| face_identity_extractor@v1 | Detects, aligns and embeds every faceNot deployed on Mixpeek Cloud, where face recognition is off. On a dedicated or customer-hosted deployment: scrfd_2.5g detection, min_face_size 20, quality scoring on, embeddings normalised. | Built in | Not available yet |
| text_extractor@v1 | Embeds the ad copyEmbeds each ad's primary_text as one chunk, which is what the manifest asks for: it sets no split_by, and the extractor default is none. To chunk, set split_by and a chunk_size counted in that splitter's own unit, so sentences with chunk_size 5 gives five-sentence chunks. | Built in | Deploys ready |
| brand-scenes | The retriever to run first: plain language in, ranked scenes out | Built in | Deploys ready |
Apply the manifest and the namespace comes up with three collections and three retrievers.
Same manifest against a customer-hosted deployment.
- Permissions
- A key with namespace create and collection create. The storage connection is a separate grant and the manifest ships it commented out, because apply live-tests a connection and would roll the whole namespace back on placeholder credentials.
- Footprint
- Scene extraction is the cost driver and is billed per minute of video. On the measured corpus, 271 source videos with a median length of 29 seconds produced 2,306 scenes and 8,168 face documents.
None ship with this template. The measured corpus above is a live namespace rather than a fixture set, and there is no golden query list to score against yet.
| Category | Rule |
|---|---|
| Scene detection | scene_detection_threshold 0.5 with a 10 second time split as the fallback. Lower it and single-take creatives stop producing one scene; raise it and cuts get merged. |
| Face quality | On a dedicated deployment, where the face step runs: min_face_size 20 pixels and detection_threshold 0.5, with quality scoring on so a low-confidence face is filterable rather than silently trusted. |
| Duplicate creatives | An archive that carries the same creative many times will produce clusters dominated by one ad. Collapse duplicates before presenting clusters as distinct themes. |
One brand-scenes execution over the measured corpus returned 24 documents in 1.09 seconds. That is a single observation rather than a distribution, and executions on the same namespace have also returned 502 and 503 under shard load, so treat about a second as the good case rather than a guarantee.
Does this search what people say in the ads?+
No. This template indexes what is on screen, who is in frame, and the ad copy you supply as a field. Spoken audio is not part of it, and the measured corpus carries no transcripts at all, so nothing on this page depends on speech.
What do the relevance scores look like?+
Cross-modal text-to-video similarity sits in a narrow band, roughly 0.11 to 0.13 on the measured corpus. That is normal for this kind of retrieval and it is why the template does not render a score as a percentage: an 11% label reads as a broken product when the ranking is working correctly. Use rank order, or a bar relative to the top result.
Why are faces a separate collection instead of a field on the scene?+
One scene can contain several faces and one face appears in many scenes, so attaching faces to scenes would force a choice about which face the scene is. Keeping them separate lets a face query return every appearance across the archive and a scene query stay about composition.
Do the clusters work out of the box?+
Clustering is a per-brand run rather than a step in the pipeline, and on the measured namespace 9 of 16 runs completed, 3 failed outright, 1 was in progress and 1 completed with errors. Plan for a brand's cluster run needing a retry, and read the cluster status before presenting themes as complete.
How much of the archive does copy-search reach?+
Ad copy is a field you supply on the object, not something the pipeline extracts from the video, so coverage is whatever your export carries. On the measured corpus it is 38 creatives out of 271, about 14%, with a median chunk of 184 characters. The retriever is correct and the index is the real multilingual one, and it is worth knowing that copy-search answers over the creatives that have copy rather than over the whole archive.
Can I point it at a public ad library instead of my own exports?+
The pipeline does not care where the video came from, only that the object carries a video URL and the copy. What changes is the shape of the media: the measured corpus is vertical ad creative, 5 to 80 seconds, which cuts into scenes readily. Longer single-take footage will produce one scene per file and the numbers on this page will not reproduce.