Skip to main content
Thirdwatchthirdwatch
Social media

Build an Ad Creative Database With Facebook Ads in 2026

Build a structured swipe file of Facebook and Instagram ad creatives from Meta's Ad Library. Extract ad text, CTAs, platforms, and launch dates at scale.

May 26, 2026 · 5 min read · 1,162 words
See the scraper →

Thirdwatch's Facebook Ad Library Scraper pulls active ad creatives from Meta's public Ad Library for any brand -- ad copy text, call-to-action buttons, start dates, and platform placement across Facebook, Instagram, Messenger, and Audience Network. No login needed. Built for creative strategists, performance marketers, and agencies building structured swipe files from real competitor ads.

Why build an ad creative database from Facebook ads

Ad creative is the highest-leverage variable in paid social performance. According to Meta's own research on creative best practices, creative quality drives up to 56% of auction outcomes on Meta platforms. Yet most teams rely on manual browsing of the Meta Ad Library or expensive third-party tools like AdSpy ($149/month) to find creative inspiration. Neither approach scales when you need to track 20-50 brands across multiple markets.

The job-to-be-done is a structured creative library. A performance agency needs a swipe file of 500+ active DTC creatives to brief designers before a client sprint. A brand marketing lead wants to catalog every CTA variant a competitor has tested in the last quarter. A growth consultant pitching a new vertical needs to show the prospect what the top five players in their category are running right now. All of these require extracting text, CTAs, platform mix, and launch timing from the Ad Library at scale and storing it in a queryable format.

How does this compare to the alternatives?

Three paths to building an ad creative database:

Approach Pricing Coverage Setup time Maintenance
AdSpy / BigSpy / PowerAdSpy $49-$249/month subscription Large historical databases, but subscription-locked Instant Vendor-managed
Manual Ad Library browsing Free All active ads, but no export or structure Zero Manual copy-paste per ad
Thirdwatch Facebook Ad Library Scraper Pay per result All active ad categories, structured JSON output 5 minutes Thirdwatch tracks Meta changes

Subscription tools give you historical depth but lock you into monthly fees regardless of usage. Manual browsing works for one-off research but breaks at scale. The Thirdwatch actor sits in the middle: structured, on-demand, pay-per-result extraction that feeds directly into your database or spreadsheet.

How to build an ad creative database in 4 steps

Step 1: How do I set up my Apify API token?

Sign in at apify.com (free tier, no credit card), open Settings, then Integrations, and copy your API token:

export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"

Step 2: How do I pull creatives for multiple brands at once?

Pass all target brands in the brands array. Set maxCreatives high to capture a deep creative sample per brand.

import os, requests, json

ACTOR = "thirdwatch~fb-ad-library-scraper"
TOKEN = os.environ["APIFY_TOKEN"]

# DTC footwear competitive set
brands = [
    "allbirds", "on running", "hoka", "brooks running",
    "new balance", "asics", "saucony", "mizuno",
    "salomon", "altra running"
]

resp = requests.post(
    f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
    params={"token": TOKEN},
    json={
        "brands": brands,
        "country": "ALL",
        "adType": "all",
        "maxCreatives": 100,
    },
    timeout=900,
)

results = resp.json()
total_creatives = sum(r.get("creativeCount", 0) for r in results)
print(f"{len(results)} brands, {total_creatives} total creatives collected")

Ten brands with 100 creatives each yields up to 1,000 ad samples in a single run. The country parameter filters by market -- use US, IN, GB, or ALL for worldwide.

Step 3: How do I flatten creatives into a queryable table?

Explode the nested creatives array into a flat CSV or DataFrame suitable for filtering, tagging, and analysis.

import pandas as pd

rows = []
for brand_data in results:
    for creative in brand_data.get("creatives", []):
        rows.append({
            "brand": brand_data["brand"],
            "total_active_ads": brand_data["adCount"],
            "country": brand_data["country"],
            "text": creative.get("textSnippet", ""),
            "cta": creative.get("cta", ""),
            "start_date": creative.get("startDate", ""),
            "platforms": ", ".join(creative.get("platforms", [])),
            "ad_library_url": brand_data["url"],
            "scraped_at": brand_data["scraped_at"],
        })

creative_db = pd.DataFrame(rows)

# Tag creatives by messaging theme
creative_db["mentions_sustainability"] = creative_db["text"].str.contains(
    r"sustainab|eco|carbon|plant.based|recycl", case=False, na=False
)
creative_db["mentions_comfort"] = creative_db["text"].str.contains(
    r"comfort|cushion|soft|lightweight|breathab", case=False, na=False
)

creative_db.to_csv("ad_creative_database.csv", index=False)
print(f"Saved {len(creative_db)} creatives to ad_creative_database.csv")
print(f"Sustainability mentions: {creative_db.mentions_sustainability.sum()}")
print(f"Comfort mentions: {creative_db.mentions_comfort.sum()}")

The tagging step is where the swipe file becomes actionable. Regex-based theme tagging at this level catches broad patterns; for deeper analysis, pass textSnippet through an LLM classifier to extract tone, offer type, and audience segment.

Step 4: How do I keep the database fresh with scheduled runs?

Set up a weekly schedule to append new creatives and track how the competitive landscape changes over time.

import requests, os

TOKEN = os.environ["APIFY_TOKEN"]

schedule = requests.post(
    f"https://api.apify.com/v2/schedules",
    params={"token": TOKEN},
    json={
        "name": "ad-creative-db-refresh-weekly",
        "cronExpression": "0 8 * * 3",
        "timezone": "America/New_York",
        "isEnabled": True,
        "actions": [{
            "type": "RUN_ACTOR",
            "actorId": "thirdwatch~fb-ad-library-scraper",
            "runInput": {
                "brands": ["allbirds", "on running", "hoka", "brooks running",
                           "new balance", "asics", "saucony", "mizuno",
                           "salomon", "altra running"],
                "country": "ALL",
                "adType": "all",
                "maxCreatives": 100,
            }
        }]
    }
)
print(f"Schedule created: {schedule.json().get('data', {}).get('id')}")

Every Wednesday at 8 AM, a fresh batch of creatives lands in your Apify dataset. Append to the existing CSV or database table and deduplicate on brand + textSnippet + startDate + cta.

Sample output

Two creative records from a single brand lookup:

{
    "brand": "on running",
    "country": "ALL",
    "adType": "all",
    "url": "https://www.facebook.com/ads/library/?active_status=active&ad_type=all&country=ALL&q=on+running",
    "active": true,
    "adCount": 87,
    "creatives": [
        {
            "startDate": "May 1, 2026",
            "platforms": ["Facebook", "Instagram", "Audience Network"],
            "cta": "Shop Now",
            "textSnippet": "The Cloudmonster 2. Maximum cushioning meets race-day energy return."
        },
        {
            "startDate": "Apr 15, 2026",
            "platforms": ["Instagram"],
            "cta": "Learn More",
            "textSnippet": "Run on clouds. Swiss-engineered performance for every stride."
        }
    ],
    "creativeCount": 50,
    "scraped_at": "2026-05-26T08:00:00.000Z"
}

adCount tells you the brand's total active ad volume. creatives gives you the actual ad copy samples. platforms reveals placement strategy -- an Instagram-only creative signals a different audience than a cross-platform blast. startDate shows creative longevity; ads running for 30+ days are likely proven winners.

Common pitfalls

Three mistakes when building creative databases from the Ad Library. Not deduplicating across runs -- the same creative appears in consecutive weekly snapshots if it is still active. Deduplicate on brand + textSnippet + startDate + cta before aggregating counts or running analysis. Ignoring country restrictions -- in the EU, UK, and US, Meta only surfaces political, issue, housing, employment, and credit ads. A search for "Nike" in the US may return zero commercial ad results. Use country: "ALL" or target non-restricted markets for commercial brand creatives. Treating text snippets as full copy -- textSnippet captures the visible portion of the ad text in the Library card view. Longer ad copy gets truncated. The snippet is enough for pattern analysis and messaging categorization, but do not treat it as the complete creative brief.

Thirdwatch's actor handles page rendering and anti-bot protections so you receive clean JSON without managing browser sessions or parsing DOM changes.

Related use cases

Frequently asked questions

How many ad creatives can I collect per brand?

Up to 200 creatives per brand using the maxCreatives parameter. The default is 20. For building a large swipe file, set maxCreatives to 100 or 200 and pass multiple brands in one run.

Can I get ad images and videos from the Ad Library?

This actor returns text, CTA, platforms, and start dates. Creative media such as images and videos are available on request for custom integrations. The text-level data is sufficient for copy analysis and messaging pattern research.

Does the database include inactive or past ads?

No. Meta's Ad Library only shows currently active ads. To build a historical database, schedule recurring scrapes and accumulate results over time. Each run captures the active snapshot at that moment.

Can I filter by ad category like housing or employment?

Yes. The adType parameter accepts political_and_issue_ads, housing_ads, employment_ads, or credit_ads. Use all (the default) to capture every category. Regulated categories have extra transparency in Meta's system.

How do I deduplicate creatives across runs?

Deduplicate on a composite key of brand + textSnippet + startDate + cta. The same creative captured across weekly runs will share these four fields. Use a hash of this tuple as your unique ID.

Related

Try it yourself

100 free credits, no credit card.

About 30 real searches. Add the MCP to Claude or Cursor in two minutes.