install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/adarshvmore/meta-ads-collector" ~/.claude/skills/openclaw-skills-meta-ads-collector && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/adarshvmore/meta-ads-collector" ~/.openclaw/skills/openclaw-skills-meta-ads-collector && rm -rf "$T"
manifest:
skills/adarshvmore/meta-ads-collector/SKILL.mdsource content
Meta Ads Collector Skill
Purpose
Scans the Meta Ad Library API to find active advertisements for a given brand. Extracts the number of active ads, ad formats used, ad types, and the longest-running ad duration. This collector feeds into the Marketing Audit Pipeline to populate the Paid Ads Strategy section of the final report.
Input Schema
// Function signature collectMetaAds(brandName: string, domain?: string): Promise<MetaAdsData> // brandName: The brand name to search for in the Ad Library (e.g. "Gymshark") // domain: Optional domain to refine search (e.g. "gymshark.com"). Used to filter // results and improve relevance when the brand name is ambiguous.
Output Schema
interface MetaAdsData { activeAds: number; // Total count of currently active ads formatsUsed: string[]; // e.g. ["image", "video", "carousel"] longestRunningAdDays: number; // Days the longest-running active ad has been live adTypes: string[]; // e.g. ["POLITICAL_AND_ISSUE_ADS", "HOUSING_ADS", "OTHER"] estimatedSpend?: string; // e.g. "$10,000 - $50,000" (if available from API) error?: string; // Present only when collector fails }
API Dependencies
- API Name: Meta Ad Library API
- Endpoint:
https://graph.facebook.com/v19.0/ads_archive - Auth:
environment variable (requires a Facebook App with Ad Library API access)META_ACCESS_TOKEN - Additional env vars:
,META_APP_ID
(used for token generation if needed)META_APP_SECRET - Cost estimate: Free (no per-request charge)
- Rate limits: Subject to Meta's standard Graph API rate limits (~200 calls/hour)
Implementation Pattern
Data Flow
- Receive
and optionalbrandName
from the pipelinedomain - Call
which queries the Ad Library APImetaAdsService.getMetaAds(brandName, domain) - Process the returned ads array to extract metrics
- Map processed data to the
interfaceMetaAdsData
API Query Parameters
{ access_token: process.env.META_ACCESS_TOKEN, search_terms: brandName, ad_reached_countries: "['US']", // Default to US; can be expanded ad_active_status: "ACTIVE", // Only fetch currently active ads ad_type: "ALL", // Include all ad types fields: "id,ad_creation_time,ad_creative_bodies,ad_creative_link_captions,ad_creative_link_titles,ad_delivery_start_time,ad_snapshot_url,page_name", limit: 100 // Max results per page }
Metrics Calculation
Active Ads Count:
- Count the total number of ads returned from the API response
Formats Detection:
- Analyze
or creative fields to classify formatad_snapshot_url - Categories:
,"image"
,"video"
,"carousel"
,"dynamic""collection" - Deduplicate into a unique list
Longest Running Ad:
const now = new Date(); const longestRunningAdDays = Math.max( ...ads.map(ad => { const startDate = new Date(ad.ad_delivery_start_time); return Math.floor((now.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)); }) );
Ad Types:
- Extract unique
values from the responsead_type - Common types:
,"POLITICAL_AND_ISSUE_ADS"
,"HOUSING_ADS"
,"CREDIT_ADS"
, general/uncategorized"EMPLOYMENT_ADS"
Estimated Spend:
- Only available for political/issue ads (Meta requirement)
- For other ad types, this field will be
undefined - If available, format as a range string:
"$10,000 - $50,000"
Domain Filtering
When
domain is provided:
- Filter results to only include ads where the creative body, link caption, or link title references the domain
- This improves accuracy for brands with common names
Error Handling
- Entire function wrapped in
try/catch - On failure, return
withEMPTY_META_ADS_DATA
field set:error
return { ...EMPTY_META_ADS_DATA, error: 'Meta Ads data unavailable: <reason>' };
- Never throw -- always return a valid
objectMetaAdsData - Log errors with Winston logger including brandName and error details:
logger.error('Meta Ads collector failed', { brandName, domain, err });
- Common failure scenarios:
- Access token invalid, expired, or lacking Ad Library permissions
- Brand name returns zero results (not necessarily an error -- return zeroed data without error flag)
- Rate limit exceeded (Meta Graph API throttling)
- Network timeout
Example Usage
import { collectMetaAds } from '../collectors/metaAdsCollector'; // Successful collection const data = await collectMetaAds('Gymshark', 'gymshark.com'); // Returns: // { // activeAds: 47, // formatsUsed: ["image", "video", "carousel"], // longestRunningAdDays: 182, // adTypes: ["OTHER"], // estimatedSpend: undefined, // } // No ads found (not an error) const noAds = await collectMetaAds('TinyLocalShop'); // Returns: // { // activeAds: 0, // formatsUsed: [], // longestRunningAdDays: 0, // adTypes: [], // } // Failed collection (graceful degradation) const failedData = await collectMetaAds('Gymshark'); // Returns: // { // activeAds: 0, // formatsUsed: [], // longestRunningAdDays: 0, // adTypes: [], // error: "Meta Ads data unavailable: Access token expired" // }
Notes
- The collector depends on
for the actual API communication. The collector handles only data aggregation and metric calculation.metaAdsService.ts - Meta Ad Library API requires a Facebook App registered with Ad Library access. The app must be reviewed and approved by Meta for production use.
- The API only returns publicly available ad data. Spend data is only available for political/issue ads as mandated by Meta's transparency policies.
- Zero active ads is a valid result (small or new brands may not run Meta ads) and should be returned without an error flag.
- The
constant is defined inEMPTY_META_ADS_DATA
and should be imported for fallback returns.src/types/audit.types.ts - This collector must never block the pipeline. Even a complete failure returns valid typed data with an error flag.
- Pagination: the Meta API returns a maximum of 100 results per page. For brands with many ads, pagination via the
cursor may be needed. For audit purposes, the first page (100 ads) is sufficient.after