adlibrary.com Logoadlibrary.com
Share
Guides & Tutorials

Full adlibrary API Documentation and Implementation Guide

Complete guide to the AdLibrary API — search ads from Meta, TikTok, Google, and more programmatically. Includes authentication, endpoints, parameters, response formats, and integration examples. API access requires an active Business subscription. This is a living document and will be updated with further changes.

AdLibrary API Endpoint

The AdLibrary API lets you programmatically search ads across Facebook, Instagram, TikTok, Google and more. All requests go through a single base URL:

1https://adlibrary.com/api/search

All requests use POST with a JSON body.

Authentication

The API uses API key authentication. Include your key in the Authorization header with every request:

1Authorization: Bearer adl_your_api_key

Business subscription required — API access is only available on the Business plan

API keys are prefixed with adl_ and managed in your developer dashboard

1 credit per request — credits are deducted from your account balance

Rate limits: 10 requests/minute, 10,000 requests/day

Getting Your API Key

Log into adlibrary.com, navigate to your developer settings, and create a new API key. Each key is shown once at creation — store it securely. You can revoke and create new keys at any time.

Pricing

Each API request costs 1 credit, the same as a GUI search. Every response includes a _credits object showing your remaining balance:

1{
2 "_credits": {
3 "used": 1,
4 "remaining": 999,
5 "autoCharged": false,
6 "creditsAdded": 0
7 }
8}

Search Endpoint

The primary endpoint for searching ads across all supported platforms.

1curl -X POST "https://adlibrary.com/api/search" \
2 -H "Authorization: Bearer adl_your_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "keyword": "fitness app",
6 "appType": "3",
7 "geo": ["USA"],
8 "daysBack": 30,
9 "pageSize": 10
10 }'

App Types

The appType parameter determines which ad category you are searching. It is required for every request.

appType "3"E-commerce & Brand ads (default). This is the most commonly used type.

appType "1" — Gaming app ads. Supports app category and OS filters. Currently inactive (being rebuilt).

appType "2" — Non-gaming app ads (Business, Education, Finance, etc.). Supports app category and OS filters. Currently inactive (being rebuilt).

Request Body Reference

All filters are passed as fields in the JSON request body. Here is the complete reference of all supported fields:

Search & Sort

1POST /api/search
2{
3 "keyword": "fitness app",
4 "appType": "3",
5 "searchType": "0",
6 "preciseSearch": false,
7 "sortField": "-impression",
8 "daysBack": 30
9}

keyword (string) — Search keyword

excludeKeyword (string) — Exclude keyword from results

appType (string, required) — Ad category: "1" (Gaming), "2" (Apps), "3" (E-commerce)

searchType (string) — What field to search: "0" = All Text, "1" = Title, "4" = Body, "5" = Caption, "6" = Landing Page

preciseSearch (boolean) — Set to false for broad match. Default is true (exact match).

sortField (string) — Sort order. Valid values: -correlation (relevance), -first_seen (newest), -last_seen (recently active), -impression (most impressions), -like_count (most likes), -share_count (most shares), -comment_count (most comments), -days (longest running), -heat_degree (trending), -related_ads_count (most related ads)

daysBack (number) — Filter by recency: 1, 3, 7, 14, 30, 60, 90, 180, or 365. Default is 90.

dateFrom / dateTo (string, YYYY-MM-DD) — Custom date range. Takes priority over daysBack when set.

Targeting

platform (string[]) — Filter by ad platform: "facebook", "instagram", "tiktok", "twitter", "pinterest", "yahoo", "unity_ads", "admob", "youtube"

placement (string[]) — Filter by ad placement: "feed", "stories", "reels", "marketplace", "video_feeds", "search", "instream", "right_column", "instant_articles", "audience_network", "messenger"

geo (string[], ISO 3166 alpha-3) — Target countries, e.g. ["USA", "GBR", "DEU"]. Supports 50+ countries.

excludeGeo (string[]) — Exclude specific countries

language (string[]) — Filter by ad language using ISO 639 codes: "en", "de", "fr", "es", "it", "pt", "nl", "ja", "ko", "zh-CN", "zh-TW", "ar", "hi", "th", "vi", "tr", and 20+ more

os (string[]) — Filter by operating system: "1" = iOS, "2" = Android. Only applies to appType 1 and 2.

Creative

adsType (string[]) — Media type: "1" = Image, "2" = Video, "3" = Carousel, "4" = Collection

adsFormat (string[]) — Aspect ratio: "1:1", "16:9", "9:16", "4:5", "4:3"

adsSize (string) — Video quality: "hd" or "sd"

videoDurationBegin / videoDurationEnd (number, seconds) — Video duration range

callToAction (string[], single value used) — Call-to-action category: "shopping", "transform" (conversions), "traffic_acquisition", "potential_customer" (leads), "interaction" (engagement), "other"

E-commerce Filters (appType 3)

independentWebsite (string[]) — Filter by advertiser domain name

ecommercePlatform (string[]) — Filter by store platform: "shopify", "bigcommerce", "opencart", "wordpress" (WooCommerce), "magento", "wix", "squarespace", "shop.tiktok.com"

App Category Filters (appType 1 and 2)

tagIds (string[]) — App category IDs. Gaming (appType 1): 10–27 (Action, Adventure, Arcade, Board, Card, Casino, Casual, Educational, Music, Puzzle, Racing, Role Playing, Simulation, Sports, Strategy, Trivia, Word, Family). Non-gaming (appType 2): 51–72 (Books, Business, Communication, Dating, Education, Entertainment, Finance, Food, Health, Lifestyle, and more).

Engagement Filters

likeBegin / likeEnd (number) — Filter by like count range

shareBegin / shareEnd (number) — Filter by share count range

commentBegin / commentEnd (number) — Filter by comment count range

Note: Impressions, views, heat, and running days only support sorting (via the sort parameter), not filtering by range.

Special Flags

newAdsFlag (boolean) — New ads only

originalFlag (boolean) — Original (first seen) ads only

isDynamic (boolean) — Dynamic creative ads

codFlag (boolean) — Cash on delivery ads

duplicateRemoval (boolean or number) — Remove duplicate creatives (1 or 2 for different modes)

searchArbitrageFlag (boolean) — Search arbitrage ads

viewIllegal (boolean) — Include policy-violating ads

Pagination

page (number) — Page number, starting at 1

pageSize (number) — Results per page. Default is 60.

Complete Search Example

Here is a complete example with multiple filters:

1curl -X POST "https://adlibrary.com/api/search" \
2 -H "Authorization: Bearer adl_your_key" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "keyword": "skincare",
6 "appType": "3",
7 "platform": ["facebook", "instagram"],
8 "geo": ["USA", "GBR"],
9 "adsType": ["2"],
10 "sortField": "-impression",
11 "daysBack": 30,
12 "ecommercePlatform": ["shopify"],
13 "likeBegin": 100,
14 "pageSize": 20
15 }'

Response Format

A successful search response looks like this:

1{
2 "results": [
3 {
4 "ad_key": "abc123",
5 "ads_type": 2,
6 "advertiser_name": "Brand Name",
7 "app_type": 3,
8 "title": "Ad headline text",
9 "body": "Ad body copy...",
10 "message": "Main ad text",
11 "caption": "Caption text",
12 "button_text": "Shop Now",
13 "like_count": 1250,
14 "comment_count": 89,
15 "share_count": 340,
16 "view_count": 50000,
17 "impression": 120000,
18 "first_seen": 1719849600,
19 "last_seen": 1720454400,
20 "days_count": 7,
21 "heat": 850,
22 "preview_img_url": "https://...",
23 "video_url": "https://...",
24 "video_duration": 30,
25 "page_name": "Brand Page",
26 "page_id": "123456",
27 "platform": "facebook",
28 "geo": ["USA", "GBR"],
29 "fb_merge_channel": ["feed", "stories"],
30 "landing_page_url": "https://example.com/product",
31 "ecommerce_platform": "shopify",
32 "independent_website": "example.com",
33 "related_ads_count": 15
34 }
35 ],
36 "total": 4230,
37 "page": 1,
38 "pageSize": 20,
39 "_credits": {
40 "used": 1,
41 "remaining": 998,
42 "autoCharged": false,
43 "creditsAdded": 0
44 }
45}

Each ad object contains engagement metrics (like_count, comment_count, share_count, view_count, impression), date fields as Unix timestamps (first_seen, last_seen, created_at), media URLs, targeting data (geo, platform, fb_merge_channel), and e-commerce info when applicable. Additional fields may be present depending on the ad.

Each ad object contains engagement metrics (like_count, comment_count, share_count, view_count, impression), date fields as Unix timestamps (first_seen, last_seen, created_at), media URLs, targeting data (geo, platform, fb_merge_channel), and e-commerce info when applicable. Additional fields may be present depending on the ad.

Ad Detail Endpoint

Fetch detailed information about a specific ad, including audience analysis data when available. This endpoint does not require authentication and does not cost credits.

1curl -X POST "https://adlibrary.com/api/ad-detail" \
2 -H "Content-Type: application/json" \
3 -d '{
4 "creative_key": "abc123",
5 "app_type": "3"
6 }'

Parameters

creative_key (string, required) — The ad_key from a search result

app_type (string, default "3") — The app type of the ad

created_at (string, optional) — Unix timestamp. Defaults to current time if omitted.

Response

The response includes a detail object with extended engagement and cost data. When the ad has an associated page, an audience analysis breakdown is also returned:

1{
2 "detail": {
3 "ad_key": "abc123",
4 "ad_cost": 5200,
5 "associated_id": "page_assoc_id",
6 "page_id": "123456",
7 "like_count": 1250,
8 "comment_count": 89,
9 "share_count": 340,
10 "view_count": 50000,
11 "impression": 120000,
12 "days_count": 7,
13 "heat": 850,
14 "countries": ["US", "GB"],
15 "store_url": "https://example.com",
16 "fb_merge_channel": ["feed", "stories"]
17 },
18 "audience": {
19 "min_age": 18,
20 "max_age": 65,
21 "sex": "all",
22 "total_reach": 850000,
23 "sex_detail": [
24 { "type": "male", "count": 425000, "percent": 50 },
25 { "type": "female", "count": 425000, "percent": 50 }
26 ],
27 "age_detail": [
28 {
29 "type": "18-24",
30 "count": 170000,
31 "percent": 20,
32 "male": 85000,
33 "female": 85000,
34 "unknown": 0
35 }
36 ],
37 "location_detail": [
38 {
39 "code": "US",
40 "count": 595000,
41 "percent": 70,
42 "male": 297500,
43 "female": 297500,
44 "unknown": 0
45 }
46 ]
47 }
48}

The audience object is only returned when the ad has both an associated_id and page_id. The detail object may include additional fields beyond those listed here. Responses are cached for 5 minutes.

Error Codes

The API uses standard HTTP status codes. All error responses include an error message and may include a code field:

400 Bad Request — Missing required parameters (e.g. creative_key for ad-detail)

401 Unauthorized — Missing or invalid API key

402 Payment Required — Insufficient credits. Response includes balance and creditsRequired fields.

403 Forbidden — Feature not available on your plan, or permission denied

429 Too Many Requests — Rate limit exceeded. Check the Retry-After header for seconds until reset.

500 Internal Server Error — Search failed. If credits were deducted, they are automatically refunded.

1// Example 402 error response
2{
3 "error": "Insufficient credits",
4 "code": "INSUFFICIENT_CREDITS",
5 "creditsRequired": 1,
6 "balance": 0,
7 "autoChargeEnabled": false
8}
9
10// Example 429 error response
11{
12 "error": "Rate limit exceeded. Maximum 100 requests per minute.",
13 "code": "RATE_LIMIT_EXCEEDED",
14 "retryAfter": 45
15}

API Key Management

Manage your API keys via the developer endpoints. These endpoints require a web session (cookie auth) — they cannot be called with an API key.

List Keys

1POST /api/developer/keys
2// empty body
3
4// Response
5{
6 "keys": [
7 {
8 "id": "uuid",
9 "name": "Production Key",
10 "key_prefix": "adl_abc12345",
11 "is_active": true,
12 "last_used_at": "2025-06-01T12:00:00Z",
13 "requests_today": 42,
14 "requests_this_month": 1250,
15 "created_at": "2025-01-15T10:00:00Z"
16 }
17 ]
18}

Create Key

1POST /api/developer/keys
2{ "name": "My Integration Key" }
3
4// Response (the secret is only shown once!)
5{
6 "key": {
7 "id": "uuid",
8 "name": "My Integration Key",
9 "key_prefix": "adl_abc12345",
10 "is_active": true,
11 "secret": "adl_full_key_value_here"
12 },
13 "message": "API key created successfully. Save this key - it won't be shown again!"
14}

Delete Key

1POST /api/developer/keys/{id}/delete
2
3// Response
4{ "success": true, "message": "API key revoked successfully" }

Update Key

1POST /api/developer/keys/{id}
2{ "name": "Renamed Key", "is_active": false }
3
4// Response
5{
6 "key": {
7 "id": "uuid",
8 "name": "Renamed Key",
9 "key_prefix": "adl_abc12345",
10 "is_active": false,
11 "last_used_at": "2025-06-01T12:00:00Z",
12 "created_at": "2025-01-15T10:00:00Z"
13 },
14 "message": "API key updated successfully"
15}

You can create up to 10 API keys per account. Keys can be renamed, deactivated, or revoked permanently.

Credit System

Check your current credit balance:

1POST /api/credits
2// empty body
3
4// Response
5{ "credits": 950 }

Each search costs 1 credit. If you search the same keyword within one hour (via the web UI), subsequent requests for that keyword are not charged again — this allows free pagination, re-sorting, and filter changes on the same query.

If a search fails after credits are deducted, the credit is automatically refunded. You can also enable auto-charge in your account settings to automatically purchase more credits when your balance runs low.

Javascript Example

1/**
2 * AdLibrary API — Quick Start (Node.js)
3 *
4 * Search for ads and export results to CSV.
5 * No dependencies required — uses built-in Node.js APIs.
6 *
7 * Usage:
8 * node adlibrary-api-example.js
9 */
10
11const fs = require("fs");
12
13// ── Configuration ───────────────────────────────────────────────────────────
14
15const API_KEY = "adl_YOUR_API_KEY_HERE"; // Replace with your API key
16const BASE_URL = "https://adlibrary.com/api/search";
17const OUTPUT_FILE = "ads_export.csv";
18
19// ── Search & Export ─────────────────────────────────────────────────────────
20
21const CATEGORIES = {
22 Appliances: ["kitchen appliances", "Dyson", "KitchenAid", "Whirlpool"],
23 Automotive: ["car accessories", "auto parts", "AutoZone", "Tesla"],
24 "Arts & Crafts": ["art supplies", "craft supplies", "Cricut", "Canva"],
25 "Baby Products": ["baby gear", "baby stroller", "Graco", "Pampers"],
26 Beauty: ["skincare", "makeup", "Sephora", "The Ordinary", "Glossier"],
27 Books: ["books online", "audiobooks", "Kindle", "Audible"],
28 "Phones & Accessories": ["phone case", "smartphone", "Samsung Galaxy", "iPhone accessories"],
29 "Clothing & Fashion": ["fashion", "clothing brand", "Shein", "Zara", "Nike apparel"],
30 Music: ["headphones", "music streaming", "Spotify", "Bose"],
31 "Office Supplies": ["office supplies", "desk accessories", "Staples", "HP printer"],
32 Electronics: ["electronics", "laptop", "Sony", "Apple", "Samsung"],
33 "Home & Kitchen": ["home decor", "kitchen gadgets", "IKEA", "Instant Pot"],
34 "Health & Wellness": ["supplements", "vitamins", "fitness", "Athletic Greens"],
35 "Lawn & Garden": ["garden tools", "outdoor furniture", "lawn care", "Husqvarna"],
36 "Pet Supplies": ["dog food", "pet supplies", "Chewy", "BarkBox"],
37 "Sports & Outdoors": ["sports equipment", "running shoes", "Nike", "Adidas"],
38 "Toys & Games": ["toys", "board games", "LEGO", "Hasbro"],
39 "Video Games": ["video games", "gaming", "PlayStation", "Xbox", "Nintendo"],
40};
41
42const AD_TYPES = { 1: "Image", 2: "Video", 3: "Carousel", 4: "Collection" };
43
44const CSV_FIELDS = [
45 "category", "search_term", "advertiser_name", "platform", "ad_type",
46 "title", "body", "call_to_action", "impressions", "like_count",
47 "comment_count", "share_count", "days_running", "first_seen",
48 "last_seen", "landing_page", "preview_image", "ad_key",
49];
50
51function tsToDate(ts) {
52 if (!ts) return "";
53 try { return new Date(ts * 1000).toISOString().slice(0, 10); }
54 catch { return ""; }
55}
56
57function escapeCsv(val) {
58 const s = String(val ?? "");
59 if (s.includes(",") || s.includes('"') || s.includes("\n")) {
60 return `"${s.replace(/"/g, '""')}"`;
61 }
62 return s;
63}
64
65function adToRow(ad, category, searchTerm) {
66 return {
67 category,
68 search_term: searchTerm,
69 advertiser_name: ad.advertiser_name || "",
70 platform: ad.platform || "",
71 ad_type: AD_TYPES[ad.ads_type] || String(ad.ads_type || ""),
72 title: ad.title || "",
73 body: ad.body || ad.message || "",
74 call_to_action: ad.call_to_action || ad.button_text || "",
75 impressions: ad.impression || ad.all_exposure_value || "",
76 like_count: ad.like_count ?? "",
77 comment_count: ad.comment_count ?? "",
78 share_count: ad.share_count ?? "",
79 days_running: ad.days_count ?? "",
80 first_seen: tsToDate(ad.first_seen),
81 last_seen: tsToDate(ad.last_seen),
82 landing_page: ad.landing_page_url || ad.store_url || "",
83 preview_image: ad.preview_img_url || "",
84 ad_key: ad.ad_key || "",
85 };
86}
87
88async function searchAds(keyword, page = 1) {
89 const res = await fetch(BASE_URL, {
90 method: "POST",
91 headers: {
92 Authorization: `Bearer ${API_KEY}`,
93 "Content-Type": "application/json",
94 Accept: "application/json",
95 "User-Agent": "AdLibrary-API-Client/1.0",
96 },
97 body: JSON.stringify({ keyword, appType: "3", page, pageSize: 60 }),
98 });
99
100 if (res.status === 402) {
101 const err = await res.json();
102 console.log(` ✗ Out of credits. Balance: ${err.balance ?? "?"}`);
103 return null;
104 }
105
106 if (res.status === 429) {
107 const wait = parseInt(res.headers.get("Retry-After") || "10");
108 console.log(` ⏳ Rate limited. Waiting ${wait}s...`);
109 await new Promise((r) => setTimeout(r, wait * 1000));
110 return searchAds(keyword, page);
111 }
112
113 if (!res.ok) {
114 const err = await res.json().catch(() => ({}));
115 console.log(` ✗ Error ${res.status}: ${err.error || "Unknown error"}`);
116 return [];
117 }
118
119 const data = await res.json();
120 const results = data.results || [];
121 const remaining = data._credits?.remaining ?? "?";
122 console.log(`${results.length} ads — ${remaining} credits remaining`);
123 return results;
124}
125
126async function main() {
127 if (API_KEY === "adl_YOUR_API_KEY_HERE") {
128 console.error("ERROR: Please set your API key in the script (API_KEY variable).");
129 process.exit(1);
130 }
131
132 const allRows = [];
133 let creditsUsed = 0;
134
135 console.log(`AdLibrary Ad Research — ${Object.keys(CATEGORIES).length} categories\n`);
136
137 for (const [category, terms] of Object.entries(CATEGORIES)) {
138 console.log(`📁 ${category}`);
139
140 for (const term of terms) {
141 process.stdout.write(` Searching: "${term}"... `);
142
143 const results = await searchAds(term);
144
145 if (results === null) {
146 console.log("\nStopping: no credits remaining.");
147 break;
148 }
149
150 for (const ad of results) {
151 allRows.push(adToRow(ad, category, term));
152 }
153
154 creditsUsed++;
155 await new Promise((r) => setTimeout(r, 1000));
156 }
157 }
158
159 // Deduplicate
160 const seen = new Set();
161 const unique = allRows.filter((row) => {
162 if (seen.has(row.ad_key)) return false;
163 seen.add(row.ad_key);
164 return true;
165 });
166
167 // Write CSV
168 const header = CSV_FIELDS.join(",");
169 const lines = unique.map((row) =>
170 CSV_FIELDS.map((f) => escapeCsv(row[f])).join(",")
171 );
172 fs.writeFileSync(OUTPUT_FILE, [header, ...lines].join("\n"), "utf-8");
173
174 console.log(`\nDone! ${unique.length} unique ads exported to ${OUTPUT_FILE}`);
175 console.log(` Total fetched: ${allRows.length}, Dupes removed: ${allRows.length - unique.length}`);
176 console.log(` Credits used: ~${creditsUsed}`);
177}
178
179main().catch(console.error);
180


Shell Example

1#!/bin/bash
2#
3# AdLibrary API — Quick Start (Shell/cURL)
4#
5# Search for ads and save the raw JSON response.
6# Each request costs 1 credit and returns up to 60 ads.
7#
8# Usage:
9# chmod +x adlibrary-api-example.sh
10# ./adlibrary-api-example.sh
11
12API_KEY="adl_YOUR_API_KEY_HERE" # Replace with your API key
13BASE_URL="https://adlibrary.com/api/search"
14
15# ── Single search ────────────────────────────────────────────────────────────
16# Search for "Nike" ads and save the full JSON response
17
18echo "Searching for 'Nike' ads..."
19
20curl -s -X POST "$BASE_URL" \
21 -H "Authorization: Bearer $API_KEY" \
22 -H "Content-Type: application/json" \
23 -H "Accept: application/json" \
24 -H "User-Agent: AdLibrary-API-Client/1.0" \
25 -d '{"keyword":"Nike","appType":"3"}' \
26 -o nike_ads.json
27
28echo " ✓ Saved to nike_ads.json"
29
30# ── Pagination ───────────────────────────────────────────────────────────────
31# Fetch page 2 of results (costs 1 additional credit)
32
33# curl -s -X POST "$BASE_URL" \
34# -H "Authorization: Bearer $API_KEY" \
35# -H "Content-Type: application/json" \
36# -H "User-Agent: AdLibrary-API-Client/1.0" \
37# -d '{"keyword":"Nike","appType":"3","page":2}' \
38# -o nike_ads_page2.json
39
40# ── Batch search ─────────────────────────────────────────────────────────────
41# Search multiple keywords and save each to a separate JSON file
42
43KEYWORDS=("skincare" "Samsung" "LEGO" "dog food" "running shoes")
44
45echo ""
46echo "Batch searching ${#KEYWORDS[@]} keywords..."
47
48for keyword in "${KEYWORDS[@]}"; do
49 filename="${keyword// /_}_ads.json"
50 printf " Searching: %-20s" "\"$keyword\"..."
51
52 HTTP_CODE=$(curl -s -o "$filename" -w "%{http_code}" \
53 -X POST "$BASE_URL" \
54 -H "Authorization: Bearer $API_KEY" \
55 -H "Content-Type: application/json" \
56 -H "Accept: application/json" \
57 -H "User-Agent: AdLibrary-API-Client/1.0" \
58 -d "{\"keyword\":\"$keyword\",\"appType\":\"3\"}")
59
60 if [ "$HTTP_CODE" = "200" ]; then
61 echo " ✓ → $filename"
62 elif [ "$HTTP_CODE" = "402" ]; then
63 echo " ✗ Out of credits"
64 rm -f "$filename"
65 break
66 elif [ "$HTTP_CODE" = "429" ]; then
67 echo " ⏳ Rate limited, waiting 10s..."
68 rm -f "$filename"
69 sleep 10
70 else
71 echo " ✗ Error $HTTP_CODE"
72 rm -f "$filename"
73 fi
74
75 sleep 1
76done
77
78echo ""
79echo "Done! JSON files saved in current directory."
80


Python Example

We have Cloudflare in front and it is blocking uncategorized python lib requests, so you need to use a user agent, which can be arbitrary - here is a complete working example that fetches categories and brands.

1"""
2AdLibrary API — Category Ad Research Script
3=============================================
4Searches for ads across product categories and exports results to CSV.
5
6Usage:
7 python adlibrary-api-example.py
8
9Configuration:
10 - Set your API key below (API_KEY)
11 - Adjust PAGES_PER_CATEGORY to control how many pages to fetch (1 page = 60 ads)
12 - Each page costs 1 credit
13
14Output:
15 - ads_export.csv in the current directory
16"""
17
18import csv
19import time
20import urllib.request
21import json
22import sys
23
24# ── Configuration ────────────────────────────────────────────────────────────
25
26API_KEY = "adl_YOUR_API_KEY_HERE" # Replace with your API key
27BASE_URL = "https://adlibrary.com/api/search"
28PAGES_PER_CATEGORY = 1 # Pages per search term (60 ads/page, 1 credit/page). Increase for more results.
29OUTPUT_FILE = "ads_export.csv"
30
31# ── Categories & Search Terms ────────────────────────────────────────────────
32# Each category has a list of search terms — a mix of category keywords and
33# top brands — to get broad coverage of that vertical.
34
35CATEGORIES = {
36 "Appliances": ["kitchen appliances", "Dyson", "KitchenAid", "Whirlpool"],
37 "Automotive": ["car accessories", "auto parts", "AutoZone", "Tesla"],
38 "Arts & Crafts": ["art supplies", "craft supplies", "Cricut", "Canva"],
39 "Baby Products": ["baby gear", "baby stroller", "Graco", "Pampers"],
40 "Beauty": ["skincare", "makeup", "Sephora", "The Ordinary", "Glossier"],
41 "Books": ["books online", "audiobooks", "Kindle", "Audible"],
42 "Phones & Accessories": ["phone case", "smartphone", "Samsung Galaxy", "iPhone accessories"],
43 "Clothing & Fashion": ["fashion", "clothing brand", "Shein", "Zara", "Nike apparel"],
44 "Music": ["headphones", "music streaming", "Spotify", "Bose"],
45 "Office Supplies": ["office supplies", "desk accessories", "Staples", "HP printer"],
46 "Electronics": ["electronics", "laptop", "Sony", "Apple", "Samsung"],
47 "Home & Kitchen": ["home decor", "kitchen gadgets", "IKEA", "Instant Pot"],
48 "Health & Wellness": ["supplements", "vitamins", "fitness", "Athletic Greens"],
49 "Lawn & Garden": ["garden tools", "outdoor furniture", "lawn care", "Husqvarna"],
50 "Pet Supplies": ["dog food", "pet supplies", "Chewy", "BarkBox"],
51 "Sports & Outdoors": ["sports equipment", "running shoes", "Nike", "Adidas"],
52 "Toys & Games": ["toys", "board games", "LEGO", "Hasbro"],
53 "Video Games": ["video games", "gaming", "PlayStation", "Xbox", "Nintendo"],
54}
55
56# ── CSV Fields ───────────────────────────────────────────────────────────────
57# Essential fields for dashboard use. The API returns many more fields —
58# see the full API docs for the complete list.
59
60CSV_FIELDS = [
61 "category",
62 "search_term",
63 "advertiser_name",
64 "platform",
65 "ad_type",
66 "title",
67 "body",
68 "call_to_action",
69 "impressions",
70 "like_count",
71 "comment_count",
72 "share_count",
73 "days_running",
74 "first_seen",
75 "last_seen",
76 "landing_page",
77 "preview_image",
78 "ad_key",
79]
80
81AD_TYPES = {1: "Image", 2: "Video", 3: "Carousel", 4: "Collection"}
82
83
84def timestamp_to_date(ts):
85 """Convert a unix timestamp to YYYY-MM-DD, or return empty string."""
86 if not ts:
87 return ""
88 try:
89 return time.strftime("%Y-%m-%d", time.gmtime(int(ts)))
90 except (ValueError, OSError):
91 return ""
92
93
94def search_ads(keyword, page=1):
95 """Call the AdLibrary search API. Returns (results_list, total_count)."""
96 payload = json.dumps({
97 "keyword": keyword,
98 "appType": "3",
99 "page": page,
100 "pageSize": 60,
101 }).encode()
102
103 req = urllib.request.Request(BASE_URL, data=payload, headers={
104 "Authorization": f"Bearer {API_KEY}",
105 "Content-Type": "application/json",
106 "Accept": "application/json",
107 "User-Agent": "AdLibrary-API-Client/1.0",
108 })
109
110 try:
111 resp = urllib.request.urlopen(req, timeout=120)
112 data = json.loads(resp.read().decode())
113 except urllib.error.HTTPError as e:
114 body = e.read().decode()
115 try:
116 err = json.loads(body)
117 except json.JSONDecodeError:
118 err = {"error": body}
119
120 if e.code == 402:
121 print(f" ✗ Out of credits. {err.get('error', '')}")
122 print(f" Balance: {err.get('balance', '?')} credits remaining")
123 return None, 0
124 elif e.code == 429:
125 wait = int(e.headers.get("Retry-After", 10))
126 print(f" ⏳ Rate limited. Waiting {wait}s...")
127 time.sleep(wait)
128 return search_ads(keyword, page)
129 else:
130 print(f" ✗ Error {e.code}: {err.get('error', body[:200])}")
131 return [], 0
132 except Exception as e:
133 print(f" ✗ Request failed: {e}")
134 return [], 0
135
136 if data is None:
137 return None, 0
138
139 results = data.get("results", [])
140 total = data.get("total", 0)
141 credits_info = data.get("_credits", {})
142 remaining = credits_info.get("remaining", "?")
143
144 print(f" ✓ {len(results)} ads (total: {total}) — {remaining} credits remaining")
145 return results, total
146
147
148def ad_to_row(ad, category, search_term):
149 """Extract essential fields from an ad into a flat dict for CSV."""
150 return {
151 "category": category,
152 "search_term": search_term,
153 "advertiser_name": ad.get("advertiser_name", ""),
154 "platform": ad.get("platform", ""),
155 "ad_type": AD_TYPES.get(ad.get("ads_type"), str(ad.get("ads_type", ""))),
156 "title": ad.get("title", ""),
157 "body": ad.get("body", "") or ad.get("message", ""),
158 "call_to_action": ad.get("call_to_action", "") or ad.get("button_text", ""),
159 "impressions": ad.get("impression", "") or ad.get("all_exposure_value", ""),
160 "like_count": ad.get("like_count", ""),
161 "comment_count": ad.get("comment_count", ""),
162 "share_count": ad.get("share_count", ""),
163 "days_running": ad.get("days_count", ""),
164 "first_seen": timestamp_to_date(ad.get("first_seen")),
165 "last_seen": timestamp_to_date(ad.get("last_seen")),
166 "landing_page": ad.get("landing_page_url", "") or ad.get("store_url", ""),
167 "preview_image": ad.get("preview_img_url", ""),
168 "ad_key": ad.get("ad_key", ""),
169 }
170
171
172def main():
173 if API_KEY == "adl_YOUR_API_KEY_HERE":
174 print("ERROR: Please set your API key in the script (API_KEY variable).")
175 sys.exit(1)
176
177 all_rows = []
178 total_credits_used = 0
179
180 print(f"AdLibrary Ad Research — {len(CATEGORIES)} categories")
181 print(f"Pages per category: {PAGES_PER_CATEGORY} (60 ads/page, 1 credit/page)")
182 print(f"Estimated credits needed: ~{len(CATEGORIES) * len(list(CATEGORIES.values())[0]) * PAGES_PER_CATEGORY}")
183 print("=" * 60)
184
185 for category, terms in CATEGORIES.items():
186 print(f"\n📁 {category}")
187
188 for term in terms:
189 for page in range(1, PAGES_PER_CATEGORY + 1):
190 page_label = f" (page {page})" if PAGES_PER_CATEGORY > 1 else ""
191 print(f" Searching: \"{term}\"{page_label}...", end=" ")
192
193 results, total = search_ads(term, page)
194
195 if results is None:
196 # Out of credits — stop everything
197 print("\nStopping: no credits remaining.")
198 break
199
200 for ad in results:
201 all_rows.append(ad_to_row(ad, category, term))
202
203 total_credits_used += 1
204 time.sleep(1) # Be nice to the API
205
206 else:
207 continue
208 break # Break outer loop if inner loop broke (out of credits)
209 else:
210 continue
211 break
212
213 if not all_rows:
214 print("\nNo ads found. Check your API key and try again.")
215 sys.exit(1)
216
217 # Deduplicate by ad_key (same ad can appear in multiple searches)
218 seen = set()
219 unique_rows = []
220 for row in all_rows:
221 if row["ad_key"] not in seen:
222 seen.add(row["ad_key"])
223 unique_rows.append(row)
224
225 # Write CSV
226 with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
227 writer = csv.DictWriter(f, fieldnames=CSV_FIELDS)
228 writer.writeheader()
229 writer.writerows(unique_rows)
230
231 print("\n" + "=" * 60)
232 print(f"Done! {len(unique_rows)} unique ads exported to {OUTPUT_FILE}")
233 print(f" Total ads fetched: {len(all_rows)}")
234 print(f" Duplicates removed: {len(all_rows) - len(unique_rows)}")
235 print(f" Credits used: ~{total_credits_used}")
236
237
238if __name__ == "__main__":
239 main()
240



Related Articles

Related Brands