La Skill Claude Qui Écrit Mes Annonces Google Ads
J'ai construit une skill Claude qui interroge directement l'API Google Ads avec GAQL, repère les annonces responsives sur le Réseau de Recherche avec un CTR faible ou un CPA élevé, et réécrit les titres et descriptions dans les limites exactes de caractères de Google (30 et 90). Elle crée la nouvelle annonce en statut PAUSE — je relis et j'active moi-même. La partie qui compte vraiment : les limites de caractères de Google sont la vraie contrainte, pas le texte. Un modèle qui ne compte pas correctement les caractères produit des annonces que Google tronque ou rejette silencieusement.
Chaque mercredi. 28 400+ opérateurs. Zéro superflu.
✓ Vérifiez votre boîte mail — cliquez sur le lien de confirmation pour finaliser l'inscription.
✓ Vous êtes inscrit !
✓ Vous êtes déjà inscrit.
[Lecture d’opérateur] J’ai déjà une skill Claude qui gère mes annonces Facebook — cette construction est ici. Google Ads a été la suite la plus difficile, pas parce que l’API est pire, mais parce que les annonces responsives sur le Réseau de Recherche ont des limites de caractères strictes que Facebook n’a pas : 30 caractères par titre, 90 par description. Une réécriture qui se lit bien mais qui est rejetée ou tronquée à la diffusion n’est pas une réécriture, c’est un bug. La majeure partie du travail sur cette skill a consisté à forcer le modèle à respecter cette contrainte plutôt que de lui faire confiance.
Table of contents
Open Table of contents
- Pourquoi Google Ads demandait une construction différente de Facebook
- Obtenir l’accès à l’API (la partie que tout le monde sous-estime)
- La structure de fichiers de la skill
- Le code TypeScript central
- Comment je l’utilise au quotidien
- Ce que le validateur ne détecte pas
- Les outils que j’utilise pour ça
- FAQ
- Le verdict de l’opérateur
Pourquoi Google Ads demandait une construction différente de Facebook
La skill Facebook lit les données de performance, réécrit le texte et publie un nouvel ensemble de publicités. Google Ads fait les trois mêmes tâches, mais deux choses changent :
- Le langage de requête est GAQL, pas des filtres REST. L’API Google Ads v18 fonctionne avec Google Ads Query Language — en forme de SQL, en lecture seule, construit exactement pour ce type d’extraction de rapports.
- Les annonces responsives ont des limites strictes. Jusqu’à 15 titres (30 caractères chacun) et 4 descriptions (90 caractères chacune) par annonce. Google ne prévient pas en douceur — il rejette l’élément à la soumission ou, pire, diffuse une version tronquée qui se lit mal pour un vrai client.
Le point 2 est celui qui casse les implémentations naïves. Demandez à un modèle d’« écrire un titre plus percutant » et il vous rendra gentiment 34 caractères. Google Ads rejette cet élément ou l’affiche coupé en plein mot. Je ne le découvre qu’en regardant une annonce en direct qui se termine par « Réservez votre rende— ».
Obtenir l’accès à l’API (la partie que tout le monde sous-estime)
Trois choses, et la troisième est celle qui vous coûte une semaine si vous ne la planifiez pas :
- Un projet Google Cloud avec des identifiants OAuth2 — client ID, client secret et un refresh token avec le scope
https://www.googleapis.com/auth/adwords. Générez le refresh token une fois, via l’OAuth playground ou un court script local ; après ça c’est une identifiant stocké, pas un flux de connexion. - Un developer token, demandé depuis votre compte gestionnaire (MCC) Google Ads, sous Administration → Centre d’API.
- Le niveau d’accès. Chaque nouveau developer token démarre avec un accès Compte de test — il ne fonctionne que contre des comptes de test Google Ads, pas contre votre vrai compte. Vous demandez le Basic Access (jusqu’à 15 000 opérations/jour, suffisant pour ça) via un formulaire que Google examine manuellement. Prévoyez quelques jours ouvrés pour l’approbation avant de vouloir passer sur un compte réel.
Stockez GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN, GOOGLE_ADS_DEVELOPER_TOKEN et GOOGLE_ADS_CUSTOMER_ID (l’ID de compte à 10 chiffres, sans tirets) dans votre .env. Si vous gérez le compte via un MCC, vous avez aussi besoin de GOOGLE_ADS_LOGIN_CUSTOMER_ID réglé sur l’ID du compte gestionnaire — les requêtes sont rejetées avec une erreur de permission sans ça, et le message d’erreur ne dit pas pourquoi.
La structure de fichiers de la skill
.claude/skills/google-ads/
SKILL.md ← instructions que Claude lit
index.ts ← l'implémentation réelle de l'outil
limits.ts ← validation des limites de caractèresSKILL.md :
# Google Ads Manager Skill
Use this skill when the user says "check my Google Ads", "run ads
report", or "rewrite underperforming RSAs". Never run this without
explicit user instruction — it touches live ad spend.
## What it can do
- Pull campaign and ad group performance via GAQL (last 7 or 30 days)
- Flag responsive search ads with CTR < 3% or CPA above the stored
target as underperformers
- Rewrite headlines (≤30 chars) and descriptions (≤90 chars) for
flagged ads in Ale's voice
- Create new ad variations as PAUSED — never enabled automatically
## What it will NOT do
- Change budgets or bids without explicit confirmation
- Enable a paused ad
- Submit any headline or description that fails the character checkLa dernière ligne de « What it will NOT do » est celle que j’ai ajoutée après que la skill a publié un titre de 32 caractères dès la première semaine. Le comptage du modèle lui-même était faux de deux caractères — assez proche pour passer un coup d’œil, assez loin pour que l’élément soit rejeté. Depuis, tout passe par un validateur strict, pas par l’auto-déclaration du modèle.
Le code TypeScript central
// .claude/skills/google-ads/index.ts
import { GoogleAdsApi } from "google-ads-api";
import Anthropic from "@anthropic-ai/sdk";
const client = new GoogleAdsApi({
client_id: process.env.GOOGLE_ADS_CLIENT_ID!,
client_secret: process.env.GOOGLE_ADS_CLIENT_SECRET!,
developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN!,
});
const customer = client.Customer({
customer_id: process.env.GOOGLE_ADS_CUSTOMER_ID!,
login_customer_id: process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID,
refresh_token: process.env.GOOGLE_ADS_REFRESH_TOKEN!,
});
interface AdPerformance {
adGroupAdId: string;
campaignName: string;
headlines: string[];
descriptions: string[];
impressions: number;
clicks: number;
costMicros: number;
conversions: number;
ctr: number;
cpa: number;
}
async function getAdPerformance(days = 7): Promise<AdPerformance[]> {
const rows = await customer.query(`
SELECT
ad_group_ad.ad.id,
campaign.name,
ad_group_ad.ad.responsive_search_ad.headlines,
ad_group_ad.ad.responsive_search_ad.descriptions,
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.conversions
FROM ad_group_ad
WHERE segments.date DURING LAST_${days === 30 ? "30" : "7"}_DAYS
AND ad_group_ad.status = 'ENABLED'
`);
return rows.map((r: any) => {
const impressions = Number(r.metrics.impressions ?? 0);
const clicks = Number(r.metrics.clicks ?? 0);
const cost = Number(r.metrics.cost_micros ?? 0) / 1_000_000;
const conversions = Number(r.metrics.conversions ?? 0);
return {
adGroupAdId: r.ad_group_ad.ad.id,
campaignName: r.campaign.name,
headlines: (r.ad_group_ad.ad.responsive_search_ad?.headlines ?? []).map((h: any) => h.text),
descriptions: (r.ad_group_ad.ad.responsive_search_ad?.descriptions ?? []).map((d: any) => d.text),
impressions,
clicks,
costMicros: Number(r.metrics.cost_micros ?? 0),
conversions,
ctr: impressions > 0 ? (clicks / impressions) * 100 : 0,
cpa: conversions > 0 ? cost / conversions : Infinity,
};
});
}
const HEADLINE_MAX = 30;
const DESCRIPTION_MAX = 90;
function validateAsset(text: string, max: number): boolean {
return text.length <= max;
}
async function rewriteRsa(
ad: AdPerformance,
targetCpa: number,
): Promise<{ headlines: string[]; descriptions: string[] }> {
const anthropic = new Anthropic();
const msg = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{
role: "user",
content: `Rewrite this Google Ads responsive search ad in Alejandro Rioja's voice: direct, operator-focused, specific. It's underperforming — CTR ${ad.ctr.toFixed(2)}%, CPA $${ad.cpa.toFixed(2)} vs a $${targetCpa} target.
Current headlines: ${JSON.stringify(ad.headlines)}
Current descriptions: ${JSON.stringify(ad.descriptions)}
Rules, non-negotiable:
- Each headline: 30 characters MAXIMUM, including spaces and punctuation
- Each description: 90 characters MAXIMUM, including spaces and punctuation
- Return 5 headlines and 4 descriptions
- Return JSON only: {"headlines": ["...", ...], "descriptions": ["...", ...]}`,
}],
});
const text = (msg.content[0] as any).text.replace(/```json\n?/, "").replace(/```/, "").trim();
const parsed = JSON.parse(text);
const headlines = parsed.headlines.filter((h: string) => validateAsset(h, HEADLINE_MAX));
const descriptions = parsed.descriptions.filter((d: string) => validateAsset(d, DESCRIPTION_MAX));
if (headlines.length < 3 || descriptions.length < 2) {
throw new Error("Model returned too many over-limit assets — rerun or revise manually.");
}
return { headlines, descriptions };
}Chaque titre et description est vérifié par rapport à la limite dans le code, après génération, avant de toucher l’API. La réécriture du modèle est un brouillon. Le validateur est le filtre.
Comment je l’utilise au quotidien
> vérifie mon Google Ads des 7 derniers joursClaude lance la requête GAQL, signale tout ce qui est sous 3 % de CTR ou au-dessus de mon objectif de CPA enregistré, et demande avant de réécrire. Je confirme. Il génère de nouveaux titres et descriptions, les passe par le validateur de caractères, écarte ce qui échoue, et crée une nouvelle annonce en statut PAUSED via l’opération mutate de l’API Google Ads. Je compare le nouveau texte à l’ancien dans Google Ads, j’active ceux que j’utiliserais vraiment, et je laisse le reste en pause.
Même plafond que la version Facebook : environ 20 minutes au lieu d’un après-midi entier dans l’interface, parce que la partie mécanique — extraire les données, rédiger dans une contrainte stricte, préparer le changement — est exactement ce qu’un modèle devrait faire à ma place.
Ce que le validateur ne détecte pas
Le comptage de caractères est mécanique. La pertinence ne l’est pas. Un titre peut faire 28 caractères, passer toutes les vérifications, et rester quand même le mauvais message pour le terme de recherche auquel il est associé. La skill ne peut pas savoir si un groupe d’annonces peu performant a une mauvaise offre, un décalage avec la landing page, ou un texte réellement faible — elle peut seulement me dire que le texte est faible par rapport aux annonces qui l’entourent. Si toutes les annonces d’une campagne ont un CTR faible, c’est un problème de ciblage ou de landing page, et donner à la skill plus de cycles de réécriture sur une intention de recherche mal associée ne produit qu’un texte plus conforme et toujours aussi ignoré.
Je relis moi-même la sortie GAQL avant d’approuver un lot de réécritures, comme je le fais côté Facebook. La skill supprime la frappe et le comptage de caractères. Elle ne supprime pas le jugement sur ce qui est réellement cassé.
Les outils que j’utilise pour ça
Claude est ce sur quoi tournent les deux skills publicitaires — celle de Facebook et celle-ci — partagent le même schéma réécrire-puis-valider, juste contre une API différente et un budget de caractères plus serré.
Google Ads API (paquet npm google-ads-api) — le wrapper TypeScript autour de l’API brute gRPC/REST. Il gère le rafraîchissement OAuth et la construction des requêtes GAQL pour éviter de bricoler du protobuf à la main.
FAQ
Ai-je besoin du Basic Access, ou l’accès Compte de test suffit-il ?
L’accès Compte de test ne fonctionne que contre des comptes de test Google Ads — utile pour le développement, inutile pour un vrai compte avec de vraies dépenses. Il faut le Basic Access (à demander dans le Centre d’API de votre compte gestionnaire) avant que cette skill puisse toucher une campagne en direct. L’approbation est un examen manuel, pas instantané, donc demandez-la avant d’en avoir besoin, pas la semaine où vous voulez lancer.
Pourquoi 5 titres et 4 descriptions au lieu des maximums de 15 et 4 ?
Google autorise jusqu’à 15 titres, mais l’Ad Strength comme les tests réels suggèrent des rendements décroissants au-delà de 8 à 10 angles vraiment distincts — la plupart des comptes finissent avec plusieurs titres quasi-identiques qui gonflent le compte. Je limite la sortie du modèle à 5 nouveaux titres par lot de réécriture pour examiner des options distinctes, pas filtrer neuf variations de la même phrase.
Que se passe-t-il si la réécriture du modèle échoue à la vérification de caractères à chaque tentative ?
Le validateur lève une erreur plutôt que de tronquer ou soumettre silencieusement un élément hors limite. En pratique, cela veut dire que je relance le prompt une fois, et si ça échoue deux fois j’écris ce titre précis à la main. Ça arrive plus souvent sur les descriptions — 90 caractères, c’est un espace plus serré pour une phrase complète qu’il n’y paraît.
À lire aussi : pour des rapports en lecture seule sur GA4, Search Console, Bing, Ahrefs et Core Web Vitals dans un seul connecteur Claude plutôt que de construire une skill sur mesure par plateforme, j’utilise SiteWide MCP — la même idée de connecter Claude à de vraies données marketing, avec une surface plus large qu’une seule plateforme publicitaire.
Le verdict de l’opérateur
Si vous écrivez à la main des variations d’annonces responsives et comptez vous-même les caractères, c’est exactement le genre de tâche mécanique et basée sur des règles qu’un modèle devrait prendre en charge. La partie qui demande du vrai jugement — un problème de texte ou de ciblage — reste la vôtre. Construisez la skill, laissez-la rédiger et valider par rapport aux limites de Google, et gardez le diagnostic pour vous.
Chaque mercredi. 28 400+ opérateurs. Zéro superflu.
✓ Vérifiez votre boîte mail — cliquez sur le lien de confirmation pour finaliser l'inscription.
✓ Vous êtes inscrit !
✓ Vous êtes déjà inscrit.
Articles liés
Tâches planifiées dans Claude Cowork : le guide
Les tâches planifiées de Claude Cowork transforment un prompt ponctuel en tâche récurrente qui se lance seule, sans code. Voici comment la configurer.
AI AgentsServices d'agents IA qu'une agence peut vendre en 2026
Six automatisations à base d'agents IA tirées de ma propre entreprise, reformulées en lignes de service qu'une agence peut cadrer, tarifer et vendre.
AI AgentsAgents IA pour SaaS : quoi automatiser en premier
Le cadre de niveaux et de ROI pour les décisions d'agents IA, appliqué aux flux de travail SaaS qui méritent vraiment d'être automatisés en premier.
Recevez le guide IA dans votre boîte mail
Chaque mercredi. 28 400+ opérateurs. Zéro superflu.
Vérifiez votre boîte mail.
Nous vous avons envoyé un e-mail de confirmation — cliquez sur le lien pour finaliser votre inscription. Vérifiez les spams si vous ne le voyez pas d'ici une minute.
Vous êtes inscrit.
Bienvenue — la prochaine édition arrive bientôt dans votre boîte mail.
Vous êtes déjà inscrit — guettez-la chaque mercredi.