Insights by ad, ads, creatives and images
Attention yardstick and result by objective
Daily curve, ranking and fatigue
Creative dashboard, alerts and AI agent
What it solves
- The best creative is picked by the campaign objective, not by everyone's CTR.
- Hook rate and hold rate have one definition per client and add up correctly in any cut.
- The tired creative shows up before cost rises, with a thumbnail that does not break on the dashboard.
Business questions it answers
Ask your agent these questions once the case is live.
What you need
- Meta Ads with ads, creatives and images synced, plus TikTok or Google when present
- The hook rate and hold rate definition agreed with the creative team
- A place to store each ad's thumbnail with a stable URL
What you get
- A daily curve per ad with age, frequency and cumulative impressions
- Creative ranking by objective, with thumbnail and link
- Tired creative alert with a stable key
- An AI agent that answers which creative to replace and why
Where to activate
Each creative's curve and the ranking by objective, with the thumbnail.
One task per ad, with a stable key, reassessed after a week.
Answers which creatives to replace and which to scale, with the reason.
The metrics are well defined. The work is the stable thumbnail, the real format of each ad and agreeing on the yardstick with the creative team. The detailed view has the business rules, the pitfalls and the acceptance test. Or send everything straight to your agent with the Nekt MCP.
Meta delivers video metrics as lists inside each row, the thumbnail URL expires at every sync and the same name shows up on different ads. Each agency defines hook rate its own way, and whoever stores the rate per day and averages it later delivers a number that matches no cut. Without the daily curve there is no fatigue, only a total.
How it is usually done, and the rule
Each row is a mistake that already happened in practice.
Aggregate by ad_id, never by ad_name: the same name appears on different ads, and merging them changes the ranking. The name is for display and for extracting attributes agreed with the creative team (format, length, script number) by position in a standard naming convention.
Pick one definition per client and keep it. The most common: hook rate = 3-second views (action_type video_view) / impressions, or 25% views / impressions; hold rate = ThruPlay or 15 seconds / 3-second views, or 100% views / 25% views. Write in the context which one was chosen.
Rates are not stored per row. The table keeps impressions, plays, 25, 50, 75, 95 and 100% views, ThruPlay and clicks, and the rate is computed at read time from period sums. Averaging daily rates gives the wrong number when aggregating by week or campaign.
On Meta, video metrics are lists (video_p25_watched_actions and similar) and the quartile rate uses video_plays as the base. On YouTube, the quartile rate is over impressions, and Google delivers a rate, not a count: rebuild the count by multiplying by views before combining. TikTok has 2 and 6-second views and p25 to p100.
Compare creatives within the same campaign or objective: cost per result for conversion, CPM for reach, CPC or cost per landing page view for traffic, cost per ThruPlay for video. Only creatives with spend in the period and a declared minimum of impressions count. Cost in a reach campaign reflects frequency, not the creative.
If the ad recorded video plays on any day, it is a video, even if the creative says SHARE (boosted post) or has no video_id. Evaluate over the ad's whole history, not per day, or a day with no plays reclassifies the same creative.
Keep the table by ad and day with days since launch (first day with impressions), frequency and cumulative impressions. Fatigue is results falling while saturation rises. A rule used in practice: last 7 days CTR at least 35% below the previous 7, with 5,000 impressions or more in both windows.
Meta image URLs are temporary and reissued at every sync. The creative's thumbnail_url and image_url are often empty: get the image from adimages, linked through the creative (adimages.creatives contains the ad's creative.id). For an external dashboard, copy the image right after the sync and store ad_id and your own URL.
Carousels and videos are almost never in adimages; a sponsored post that never went to the feed has no embed; a LinkedIn sponsored message has no image or impressions and is compared by sends and opens; a Google ad brings no text or image through the connector. Say it is a source limitation and never make up the ad text.
If the client reports Meta with a tax markup on spend, CPM, CPC and cost per view in the creative report use the same criterion as the media report. Mixing the two makes Meta video look more expensive than YouTube.
Attention metrics
Pick one hook definition and one hold definition and declare them in the client's context.
Definitions
spend / result of the campaign objective, in the periodPitfalls that already happened
The dashboard showed images that stopped loading within a day, because Meta's URL is reissued at every sync.
Copy the image and store ad_id and your own URL.
Boosted posts came as SHARE with no video_id, and reels entered the image ranking.
Format by video delivery over the ad's history.
Grouping by name summed different ads, and the ranking winner was the sum of two.
Group by ad_id.
Hook rate stored per day and then averaged gave different numbers by week and by campaign.
Store the components and divide at read time.
The single ranking put reach ads with a low CPM at the top, ads that generated no leads at all.
Ranking within the campaign objective.
The cost per lead ranking added lead to its mirror types, and the best creative had half the real cost per lead.
Use the objective result, as in the Meta results case.
Acceptance test
The case is done when every item passes.
- For one client and month, spend and impressions in the creative table equal those in the media table for campaigns that have ad level.
- A campaign's hook rate and hold rate for the month give the same value computed from the daily table and from the period table.
- Each ad_id has a single format across its whole history.
- Every dashboard thumbnail loads 7 days after the load.
- No ranking compares creatives from different objectives.
Agent package
Paste into Claude, ChatGPT or any client with the Nekt MCP connected.
Creative performance - The creative key is ad_id. Never group by ad_name. - Hook rate and hold rate follow the client's definition (below). Always recompute from period sums; do not average rates. - Meta: video metrics are lists; quartile rate = pXX / video_plays. YouTube: quartile rate over impressions. - Format: video if the ad had plays on any day; otherwise, the creative's media type. - Best creative = lowest cost for the campaign objective (conversion: cost per result; reach: CPM; traffic: CPC). Only creatives with spend and at least <n> impressions in the period. - Fatigue: last 7 days CTR at least 35% below the previous 7, with 5,000 or more impressions in each window, or results falling while frequency and cumulative impressions rise. - No preview is a source limitation (Meta video and carousel, posts outside the feed, Google). Do not make up ad text. - Meta results follow the rules of the Meta Ads results by objective case. Per-client exceptions: - <client>: hook rate = <video_view / impressions | p25 / impressions>; hold rate = <ThruPlay / video_view | p100 / p25>. - <client>: ad_name follows <format>_<length>_<script>, separated by _. - <client>: Meta costs in the creative report <with | without> a <x>% tax.
Build the "Creative performance" case in my Nekt workspace following the rules at https://academy.nekt.com/en/industrias/agencias/performance-de-criativos.md. 1) Find the source tables and confirm the columns the rules use. 2) Create a transformation at the stated grain applying every rule. 3) Run the acceptance test and show me the result of each item. 4) Create the case's context document in the workspace, leaving my clients' exceptions open for me to fill in.
Variations
Link the ad_id to the CRM deal and rank by cost per sale.
Extract the content pillar or script number from the caption or name and compare by group.
Link the ad to the matching organic post by post ID to compare the same content in both.