What it does
This is the SKILL.md that runs the weekly analysis behind the automate LinkedIn ads management build. Instead of writing a fresh prompt every Monday, you drop this one file into your repo and the account gets read the same disciplined way every time: what to load first, how to classify before counting, which metric is valid for which format, and the one rule it never breaks, propose but never execute.
The reason it exists is that LinkedIn's flat numbers lie. A blended account CPC averages your cold prospecting layer, your retargeting layer, and any agency spend into one figure that cannot answer a real question. Conversation Ads bill on sends and report zero impressions, so a naive read shows them as free. CTV cannot be clicked at all. Judge a video ad on landing-page clicks and you will kill your best performer. So the skill categorizes first: layer from the campaign-group name, format from the ad-set field, true CPC over clickable formats only. That discipline is what makes the recommendation trustworthy enough to act on.
The output is not prose. It is a ranked list of proposed changes, each written as an executable action with the evidence attached: an object, an ID, a field, a from value, a to value, and a one-line reason you can approve on in a glance. It reads the tail of an insights log first so it never re-proposes a change you already rejected, and it appends a dated entry every run so the account keeps one honest memory of what was observed and decided.
It stops at the proposal on purpose. The skill never writes to your account; the write is your one-line approval. Pair it with a hard cap on how far any single change can swing a budget or bid, and you get an agent that does the knowing and the ranking while you do the deciding. The full build shows how to add write access and close the loop so an approved change is enacted in seconds instead of sitting in a log for six days.
Inputs & outputs
Inputs
- A LinkedIn Marketing API token with r_ads and r_ads_reporting scopes, and the ad account granted to the app (required)
- Your positioning and ICP (strategy/positioning.md, strategy/icp.md) so the analysis maps to what you sell
- An insights-log.md the skill reads and appends to, so it remembers what was already tried or rejected
- A campaign-group naming convention it can read the layer from (Create / Capture / Capture RT / other)
- Optional: a configured cap for the maximum swing any single budget or bid change may propose
Outputs
- A per-layer, week-over-week read with anything that moved more than 20% flagged and drilled to the ad level
- A ranked list of proposed changes, each as { object, id, field, from, to, one-line reason }, ready to approve
- True CPC computed over clickable formats only, with spend reconciled back to the account
- A dated entry appended to the insights log every run, never rewriting prior weeks
How to set it up
Drop the SKILL.md in your repo (or a Project)
Save the SKILL.md below as .claude/skills/linkedin-ads-optimization/SKILL.md in the repo where your fetcher and strategy files live, and Claude Code loads it on demand whenever you ask it to run the analysis. If you work in claude.ai instead, create a Project named "LinkedIn Ads" and paste the SKILL.md body into its custom instructions. The repo route is the one to use, because the skill wants to read your positioning, ICP, and insights log as files.
Get read access to the LinkedIn Marketing API
The skill is only as good as the data it can pull. Create an app in the LinkedIn Developer Portal linked to your company Page, request the Advertising API (Development tier is read-only reporting, which is all the analysis needs), grant your ad account to the app explicitly, give the authenticating member a Campaign Manager role, and generate a token with the r_ads and r_ads_reporting scopes. The use-case walkthrough has the exact click sequence, including the one grant that fails silently and reads nothing if you skip it.
Point it at your positioning, ICP, and insights log
Before it looks at a single number the skill reads strategy/positioning.md and strategy/icp.md so the recommendations map to what you sell, and it reads the tail of insights-log.md so it never re-litigates a change you already rejected. Seed those three files once. The insights log can start empty; the skill appends to it every run and it becomes the account's memory.
Run it, and put the weekly pass on a routine
Ask Claude to "run the linkedin ads analysis" and it pulls the window, classifies by layer and format, flags the 20% swings, and hands back the ranked proposed-actions list. Wrap that read-and-propose pass in a Claude Code routine set to Monday morning so the list is waiting before you open your laptop. Schedule the reading, never the execution.
Approve in one line, add write access when you're ready
You read the ranked list, approve the ones you agree with, and strike the rest. The skill proposes; your approval is the only thing that authorizes a write. When you want the approved changes enacted automatically, apply to LinkedIn for Standard tier and the rw_ads scope, then let the loop execute inside your guardrails. The full build covers the write tier, the budget cap, and the approval gate.
The SKILL.md
Save this as SKILL.md inside a folder named linkedin-ads-optimization in your repo's skills directory, or paste the body into a Claude Project. Then ask Claude to run the linkedin ads analysis.
---
name: linkedin-ads-optimization
description: Analyze a LinkedIn ad account by layer and format, flag what moved, and return a ranked list of changes to approve. Triggered by "run linkedin ads analysis" or any LinkedIn performance question.
---
# LinkedIn Ads Optimization
You analyze a LinkedIn ad account and return a ranked, evidence-backed list of
changes to approve. You never execute a change yourself. The reading and the
ranking are yours; the write is the human's one-line approval.
## Before you look at a single number
1. Read the positioning and ICP (strategy/positioning.md, strategy/icp.md).
2. Read the tail of insights-log.md so you know what has already been tried,
flagged, or rejected. Never re-propose a rejected change.
3. Pull fresh data for the window at campaign + ad-set + ad grain, plus a
cumulative pass for reach, frequency, and audience penetration.
## Classify BEFORE you count (flat numbers lie)
Layer, from the campaign-group name prefix:
- "Create -" -> CREATE (cold prospecting, top of funnel)
- "Capture -" -> CAPTURE (mid / bottom of funnel)
- "Capture RT -" -> CAPTURE-RT (retargeting) -- test THIS before bare Capture
- anything else, agency programs included -> OTHER (never fold into CREATE)
Format, from the ad-set format field, with two exceptions matched by name:
- Thought Leader Ads report as STANDARD_UPDATE
- Connected TV (CTV) reports as SINGLE_VIDEO
Valid metric per format (judging on the wrong one is the classic mistake):
- Conversation Ads -> sends and open rate. They report zero impressions.
- Video -> completion rate. Never landing-page clicks.
- CTV -> not clickable at all. Never clicks, never post-click.
- Single Image / TLA / Document / Text / Carousel -> clicks and CTR.
True CPC is computed over clickable formats only. Spend still covers every
format, so spend always reconciles to the account.
## The analysis, in order
1. Build per-layer tables, week over week (Monday-start weeks).
2. Flag anything that moved more than 20% week over week.
3. Drill to the ad level on the anomalies.
4. Write it up: high-level -> driver -> root cause.
5. Turn each conclusion into an executable, evidence-backed proposed change:
{ object, id, field, from, to, one-line reason }.
6. Append a dated entry to insights-log.md. Never rewrite prior weeks.
## Guardrails
- Propose, never execute. The output is a list a human approves in one line.
- No single change may move a budget or a bid more than the configured cap.
- Read audience penetration from the cumulative pull, never a daily row (null there).
- If a number looks wrong, check in order: the token, the UTF-16 decode, the
dedupe key (date, Campaign ID, Ad Set ID), RT-before-Capture, agency-in-OTHER,
and that true CPC used clickable formats only.
Run it from the terminal
Example run
The Monday routine fires, the skill reads the account by layer and hands back a ranked list, and a one-line approval turns the approved lines into API writes (write access is the separate Standard-tier step in the full build).
CLAUDE CODE ROUTINE FIRES (Monday 09:00) -> WEEKLY ANALYSIS:
Layer read: Cold CPC +38% w/w (flag). Retargeting CPL under target, pacing out by noon. Agency spend held in OTHER.
Catch: 'Messaging Tests' group is PAUSED at group level but its ad sets are still spending. Manual exports miss this.
PROPOSED ACTIONS (pending-actions.md):
[
{"object":"ad_set","id":"...:5210","field":"status","from":"ACTIVE","to":"PAUSED",
"reason":"Cold layer, single-image, 3 weeks, spend $2.1k, zero clickable conversions."},
{"object":"ad_set","id":"...:5233","field":"status","from":"ACTIVE","to":"PAUSED",
"reason":"Inside a group paused at group level, still spending $60/day unnoticed."},
{"object":"campaign","id":"...:4104","field":"dailyBudget","from":"$150","to":"$180",
"reason":"Retargeting, CPL 28% under target, exhausting budget by noon. +20%, within cap."}
]
HUMAN (one line, 09:12):
approve 1,2 - edit 3 -> $170 - (all inside guardrails)
AGENT EXECUTES via API (09:12:04):
[OK] ad_set 5210 -> PAUSED
[OK] ad_set 5233 -> PAUSED (group-level paused leak stopped)
[OK] campaign 4104 dailyBudget $150 -> $170
Logged 3 changes to insights-log.md with before/after + reason.
EXECUTION LAG: decision to enacted change = 4 seconds (was ~6 days by hand).
Related resources
- Use case: Automate LinkedIn Ads Management with AI →The full build this skill runs inside: read access, the write tier, the guardrails, and the one-line approval loop.
- LinkedIn Ads targeting guide →Who to put the budget in front of. The skill optimizes spend; targeting decides where it lands.
- LinkedIn Ads interest targeting →Interest audiences to layer into the campaigns this skill reads and ranks.