### Get Backblaze B2 Download Token via GET /getDownloadToken Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves a time-limited (7-day) Backblaze B2 authorization token for downloading dataset archives. Requires an access key provided via cookies. ```bash # Returns a time-limited (7-day) Backblaze B2 authorization token # for downloading dataset dump archives. curl "https://ad-archive.nexxxt.cloud/getDownloadToken" --cookie "accessKey=$ACCESS_KEY" # Response (200): # {"downloadToken": "eyJhY2NvdW50SWQiOiAi..."} ``` -------------------------------- ### GET /getDownloadToken Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Obtain a Backblaze B2 download token for accessing dataset archives. The token is valid for 7 days. ```APIDOC ## GET /getDownloadToken — Backblaze B2 download token ### Description Returns a time-limited (7-day) Backblaze B2 authorization token for downloading dataset dump archives. ### Method GET ### Endpoint https://ad-archive.nexxxt.cloud/getDownloadToken ### Parameters #### Query Parameters - **accessKey** (string) - Required - Your Backblaze B2 Application Key ID. ### Response #### Success Response (200) - **downloadToken** (string) - The Backblaze B2 authorization token. ### Response Example ```json { "downloadToken": "eyJhY2NvdW50SWQiOiAi..." } ``` ``` -------------------------------- ### Standalone Crawler Thread - crawlall.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Initializes and starts a single `Crawler` thread for direct execution. Configure with starting cursor, search parameters, page limit, and target languages. ```python from crawlall import Crawler import queing # --- Standalone: run a single crawler thread directly --- c = Crawler( after="", # Start from beginning of the library search="", # No extra filters c_limit=100, # Stop after 100 API pages (~10,000 ads at limit=100) lang="US,DE,FR" # Only fetch ads from these countries ) c.start() c.join() # Output (example): # Using token, id=9876543210 # Running link... After: # Current usage: {'call_count': 3, 'total_cputime': 4, 'total_time': 3, ...} # Inserted 100 items # Running link... After: QVFIUkZA... # ... # Finished, last Pointer: QVFIUkZA... ``` -------------------------------- ### Token Management Setup and Usage Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Demonstrates how to set up MongoDB indexes for token management and how to manually insert, retrieve, delay, and delete access tokens. ```python # Required MongoDB index setup (run once in mongo shell): # db.tokens.createIndex({"expiresAt": 1}, {expireAfterSeconds: 0}) # db.tokens.createIndex({freshAt: 1}) # Manually insert a token (e.g., for bootstrapping): from pymongo import MongoClient import os db = MongoClient(os.getenv("DBURL"))["management"] db["tokens"].insert_one({ "_id": "1234567890", # Facebook App ID "token": "EAABsbCS...", # Long-lived access token "expiresAt": ..., # datetime object from FB token expiry "freshAt": 0 # 0 = immediately available }) # --- tokens.py usage in crawler code --- import tokens # Get next available token (blocks if all are rate-limited): token = tokens.getNewToken() # Output: "Using token, id=1234567890" # Returns: "EAABsbCS..." # Apply a rate-limit delay to a specific app's token (delay in seconds): tokens.delayToken("1234567890", delay=3600) # Token won't be picked up again for 1 hour # Remove a permanently invalid token: tokens.deleteToken("EAABsbCS...") # Output: "Deleted token: EAABsbCS..." ``` -------------------------------- ### Vue.js Main Application Instance Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html The main Vue.js instance for the application. It manages application-wide data like total ads, user input, and fetched ad data. It also handles API calls for loading ads, getting download tokens, and fetching the total number of ads. ```javascript var main = new Vue({ el: "#main", data: { total_ads: "Loading...", input: "", msg: "", appIdInput: "", ad: [], downloadToken: "" }, methods: { load: function(e) { if (e) { e.preventDefault(); } this.ad = []; const vm = this; axios.get('/ad/' + this.input) .then(function(response) { vm.ad = response.data; }) .catch(function(error) { vm.msg = error.response.data; }); }, loadDownloadToken: function(e) { if (document.cookie.length) { const vm = this; axios.get('/getDownloadToken') .then(function(response) { vm.downloadToken = response.data.downloadToken; }) .catch(function(error) { alert(error) }); } } }, mounted: function() { const vm = this; axios.get('/total') .then(function(response) { vm.total_ads = response.data; }) .catch(function(error) { vm.total_ads = error.response.data; }); // hash for ad and callback access_token if (window.location.hash != "") { content = location.hash.split("="); if (content[0] == "#ad") { this.input = content[1]; this.load(); } } } }); ``` -------------------------------- ### Restore mongodump archive with zstd decompression Source: https://github.com/lejo1/facebook_ad_library/blob/master/README.md Use this command to restore a compressed BSON archive. Ensure zstd is installed for decompression. ```bash zstd -d ads.bson.zst | mongorestore --archive ... ``` -------------------------------- ### Get Total Ad Count (Public API) Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Fetches the total number of ads available in the archive. This endpoint is publicly accessible and does not require authentication. ```bash curl https://ad-archive.nexxxt.cloud/total # Response (plain text): # 184729403 ``` -------------------------------- ### Ad Document Structure Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Example structure of an ad document stored in the `ads.ads` collection. Includes fields like `_id`, `ad_creation_time`, `page_id`, `page_name`, and spending/impression ranges. ```json # Each upserted ad document looks like (see example.json): # { # "_id": "679808276335343", # "ad_creation_time": "2021-12-07", # "ad_creative_bodies": ["..."], # "page_id": "6756153498", # "page_name": "Mike Bloomberg", # "spend": {"lower_bound": "300", "upper_bound": "399"}, # "impressions": {"lower_bound": "10000", "upper_bound": "14999"}, # "_last_updated": ISODate("2024-..."), # ... # } ``` -------------------------------- ### Ad Creative Link Generation (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Generates a clickable URL for ad creative links, ensuring it starts with 'http://' or 'https://'. ```javascript makelink: function (i) { let url = " "; if (c_exists(this.data.ad_creative_link_captions, i)) { url = this.data.ad_creative_link_captions[i]; } if (!/^https?:\/\//i.test(url)) { url = 'http://' + url; } return url } ``` -------------------------------- ### Get Crawler Task - queing.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves the next available crawl task from the queue. Returns a dictionary representing the task or None if the queue is empty. ```python # Pop the next task from the queue (returns None if empty): task = queing.mayGetCrawler() # Returns: {"after": "cursor_abc123", "search": "", "c_limit": 500, "lang": "US,CA,GB"} # Or: None ``` -------------------------------- ### Currently active ads Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves a list of ads that are currently active (delivery started but not stopped). Supports pagination. Requires an `accessKey` cookie. ```APIDOC ## GET /actives ### Description Retrieves a list of ads that are currently active (delivery started but not stopped). Supports pagination. Requires an `accessKey` cookie. ### Method GET ### Endpoint /actives ### Parameters #### Query Parameters - **limit** (integer) - Optional - The maximum number of ads to return. - **offset** (integer) - Optional - The number of ads to skip. ### Request Example ```bash curl "https://ad-archive.nexxxt.cloud/actives?limit=50&offset=0" \ --cookie "accessKey=$ACCESS_KEY" ``` ### Response #### Success Response (200) - **(JSON array)**: An array of active ad objects, sorted by `ad_creation_time` descending. ``` -------------------------------- ### Restore Dataset Dumps using mongorestore and zstd Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Provides commands for restoring dataset dumps from compressed BSON archives. Supports V7+ dumps using zstd compression and V4-V6 dumps using gzip. Includes instructions for converting BSON to JSON and rebuilding recommended indexes. ```bash # V7+ dumps use mongodump + zstd compression: # Download: ads.bson.zst from the archive (requires verified access) # Restore: zstd -d ads.bson.zst | mongorestore --archive --nsInclude="facebook_ads_full.ads" # V4-V6 dumps are gzip-compressed BSON (mongodump format): # mongorestore --gzip --archive=ads.bson.gz # Convert BSON to JSON (any version): # bsondump --outFile=ads.json ads.bson # Rebuild recommended indexes after restore: # db.ads.createIndex({"page_id": 1}) # db.ads.createIndex({"ad_creation_time": 1}) # db.ads.createIndex({"page_name": "text"}) # db.ads.createIndex({"ad_delivery_stop_time": 1}) ``` -------------------------------- ### Crawler Configuration Parameters Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Displays various configuration parameters for the crawler, including API endpoint, fields, limits, and retry behavior. Ensure the DBURL environment variable is set. ```python import os import config # config.py is loaded automatically by all crawler modules. # Set required environment variable before running: # export DBURL="mongodb://localhost:27017" # The Facebook Graph API endpoint used: print(config.URL) # https://graph.facebook.com/v22.0/ads_archive # All fields that will be requested for each ad: print(config.FIELDS) # id,ad_creation_time,ad_creative_bodies,...,age_country_gender_reach_breakdown # Per-request page size (max allowed by FB is 5000, kept low to avoid errors): print(config.LIMIT) # 100 # Hourly crawl limit per non-full-crawl run: print(config.HOURLY_LIMIT) # 2000 # Full re-crawl interval (hours): print(config.GLOBAL_RECRAWL) # 168 (weekly) # Political ads-only re-crawl interval (hours): print(config.POLITICS_RECRAWL) # 96 # Retry behavior: print(config.END_RETRIES) # 10 retries before treating cursor as final print(config.END_RETRY_SLEEP) # 120s sleep between end-of-db retries print(config.ERROR_RETRIES) # 5 uncaught errors before stopping a thread # Build the country list string for the API (all ~200 countries): lang_param = ",".join(config.COUNTRIES.keys()) # "US,CA,GB,AR,AU,AT,BE,BR,... ``` -------------------------------- ### Delivery by Region Chart Initialization (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Initializes a bar chart to display ad delivery by region using the Chart.js library. Requires a canvas element with ref 'region_chart'. ```javascript if (this.data.delivery_by_region) { const ctx = this.$refs.region_chart; new Chart(ctx, { type: 'bar', data: { datasets: [{ label: 'Delivery by Region', data: this.data.delivery_by_region, backgroundColor: backgroundColors, borderColor: borderColors, borderWidth: 1 }] }, options: { plugins: { title: { display: true, text: 'Delivery by Region' } }, parsing: { xAxisKey: 'region', yAxisKey: 'percentage' } } }); } ``` -------------------------------- ### Spawn Facebook OAuth Windows Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/verification.html Initiates multiple Facebook OAuth requests by opening new windows. It handles the recursive spawning of windows and includes a mechanism to close them after a delay to prevent resource exhaustion. ```javascript function maycloseWindow(win) { try { // This only works if the site is the same again, cause of CORS restrictions. In that case spawn next window win.document; setTimeout(function (win) { win.close(); }, 10000, win); } catch (e) { setTimeout(maycloseWindow, 1000, win); } } function mayspawnWindow(lines, previous) { try { // This only works if the site is the same again, cause of CORS restrictions. In that case spawn next window previous.document; let line = lines.pop(); var win = window.open("https://www.facebook.com/v21.0/dialog/oauth?client_id=" + line + "&auth_type=rerequest&scope=public_profile,ads_read&redirect_uri=https://ad-archive.nexxxt.cloud/verification&response_type=code%20token&state=autodonate", "_blank"); // Close windows 30 secs after it was opened setTimeout(maycloseWindow, 3000, win); if (lines.length != 0) { setTimeout(mayspawnWindow, 2000, lines, win); } } catch (e) { setTimeout(mayspawnWindow, 200, lines, previous); } } ``` -------------------------------- ### Docker Compose Deployment Configuration Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Defines the services for deploying the Facebook Ad Library stack using Docker Compose. Includes configurations for the API crawler, web service, and MongoDB database. ```yaml # docker-compose.yml (based on docker-compose-example.yml) version: '3' services: api_crawler: build: ./crawler # Python image; runs crawlall.py as main.py restart: always environment: PYTHONUNBUFFERED: 1 stop_signal: SIGINT # Graceful shutdown: threads re-queue their cursors env_file: - secrets.env # Must contain: DBURL=mongodb://mongo:27017 web: build: ./web # Multi-stage Go build → scratch image restart: always environment: - GIN_MODE=release - DB_URL=mongodb://mongo:27017 - B2_APPLICATION_KEY_ID=your_b2_key_id - B2_APPLICATION_KEY=your_b2_app_key - B2_BUCKET_NAME=facebook_ads - JWT_SECRET=your_random_secret_here ports: - 8080:8080 mongo: image: mongo:7 restart: always volumes: - mongo_data:/data/db volumes: mongo_data: # Deploy: # docker compose up -d # The web server is available at http://localhost:8080 # The crawler starts pulling ads immediately after boot. ``` -------------------------------- ### Fetch Ads by Creation Date Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves ads created on a specific date. The date must be in `YYYY-MM-DD` format. Supports limiting the number of results. ```bash # Date format: YYYY-MM-DD curl "https://ad-archive.nexxxt.cloud/adsbydate/2021-12-07?limit=20" \ --cookie "accessKey=$ACCESS_KEY" # Response (200, JSON array of ads created on that date) ``` -------------------------------- ### Demographic Distribution Chart Initialization (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Initializes a stacked bar chart to display demographic distribution (gender and age) using Chart.js. Requires a canvas element with ref 'demo_chart'. ```javascript if (this.data.demographic_distribution) { const ctx = this.$refs.demo_chart; let gender_map = {}; let age_map = {}; let out = [] this.data.demographic_distribution.forEach((item, i) => { const gender = item.gender; const age = item.age; if (age_map[age] == undefined) { age_map[age] = Object.keys(age_map).length; } if (gender_map[gender] == undefined) { gender_map[gender] = Object.keys(gender_map).length; out[gender_map[gender]] = { label: gender, data: [], backgroundColor: backgroundColors[gender_map[gender]], borderColor: borderColors[gender_map[gender]], borderWidth: 1 }; } out[gender_map[gender]].data[age_map[age]] = item.percentage; }); new Chart(ctx, { type: 'bar', data: { labels: Object.keys(age_map), datasets: out }, options: { plugins: { title: { display: true, text: 'Demographic Distribution' } }, scales: { x: { stacked: true }, y: { stacked: true } } } }); } ``` -------------------------------- ### Production Crawler Spawner - main.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt The main entry point for a production environment, this script continuously fetches crawl tasks from the queue and spawns `Crawler` threads. It manages multiple threads and periodically triggers the scheduler. ```python # --- Production: queue-driven multi-threaded spawner (main entry point) --- # Run as: python main.py (crawlall.py is copied to main.py in Docker) # # import queing, config # from crawlall import Crawler # from time import sleep # # threads = [] # while True: # new = queing.mayGetCrawler() # if new: # t = Crawler(new["after"], new["search"], new["c_limit"], # new.get("lang", ",".join(config.COUNTRIES.keys()))) # t.start() # threads.append(t) # queing.mayAddCrawlers() # sleep(3) ``` -------------------------------- ### Download Facebook Ad Library Reports - load_reports.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Automates the download of Facebook Ad Library CSV reports for all specified countries using Selenium and Firefox. Requires Firefox, geckodriver, and an interactive login. Reports are saved to a specified directory. ```python import os from load_reports import load_reports # Download all country reports into ./newreports/ # Requires Firefox + geckodriver installed, and a Facebook account # with access to the Ad Library report page. download_dir = os.getcwd() + "/newreports" os.makedirs(download_dir, exist_ok=True) load_reports(download_dir) # Output: # (Browser opens https://www.facebook.com/login/) # "Please login and press enter." # (After pressing enter, iterates all AD_COUNTRIES, downloading each ZIP) # Browser quits automatically when done. # Each downloaded file follows the naming pattern: # newreports/FacebookAdLibraryReport_2022-01-16_US_lifelong.zip # └── FacebookAdLibraryReport_2022-01-16_US_lifelong_advertisers.csv ``` -------------------------------- ### Audience, Spend, and Impressions Formatting (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Formats audience size, spend, and impressions data using a 'text_bounds' function, likely for display purposes. ```javascript estimated_audience: function () { return text_bounds(this.data.estimated_audience_size) }, spend: function () { return text_bounds(this.data.spend) }, impressions: function () { return text_bounds(this.data.impressions) } ``` -------------------------------- ### Fetch Single Ad by ID Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves a specific ad using its unique ID. Requires an `accessKey` cookie for authentication. Returns a 404 error if the ad is not found. ```bash curl "https://ad-archive.nexxxt.cloud/ad/679808276335343" \ --cookie "accessKey=$ACCESS_KEY" # Response (200, JSON): # { # "_id": "679808276335343", # "ad_creation_time": "2021-12-07", # "ad_creative_bodies": ["As the quotation on the wall of the Holocaust Museum..."], # "ad_delivery_start_time": "2021-12-08", # "ad_delivery_stop_time": "2021-12-20", # "page_id": "6756153498", # "page_name": "Mike Bloomberg", # "spend": {"lower_bound": "300", "upper_bound": "399"}, # "impressions": {"lower_bound": "10000", "upper_bound": "14999"}, # "currency": "USD", # "publisher_platforms": ["instagram"], # "_last_updated": "2024-05-01T12:00:00Z" # } # Not found (404): # "Ad not found" ``` -------------------------------- ### Add Crawler Task - queing.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Schedules new crawl tasks in the distributed work queue. Use `after` for resuming crawls, `search` for API query parameters, `c_limit` to set a page limit, and `lang` to specify countries. ```python import queing import config # Add a one-off crawler task starting from a known paging cursor: queing.addCrawler( after="cursor_abc123", # FB paging cursor (empty string = start from beginning) search="", # Extra query params, e.g. "&ad_type=POLITICAL_AND_ISSUE_ADS" c_limit=500, # Stop after 500 API pages (0 = no limit) lang="US,CA,GB" # Restrict to specific countries (None = all countries) ) # Add a political-ads-only full crawl for specific countries: queing.addCrawler( after="", search="&ad_type=POLITICAL_AND_ISSUE_ADS", c_limit=0, lang=",".join(config.POLITICAL_ADS_COUNTRIES) ) ``` -------------------------------- ### Ads by creation date Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves a list of ads created on a specific date. Supports pagination. Requires an `accessKey` cookie. ```APIDOC ## GET /adsbydate/:date ### Description Retrieves a list of ads created on a specific date. Supports pagination. Requires an `accessKey` cookie. ### Method GET ### Endpoint /adsbydate/:date ### Parameters #### Path Parameters - **date** (string) - Required - The date in `YYYY-MM-DD` format. #### Query Parameters - **limit** (integer) - Optional - The maximum number of ads to return. ### Request Example ```bash curl "https://ad-archive.nexxxt.cloud/adsbydate/2021-12-07?limit=20" \ --cookie "accessKey=$ACCESS_KEY" ``` ### Response #### Success Response (200) - **(JSON array)**: An array of ad objects created on the specified date. ``` -------------------------------- ### Trigger Scheduler - queing.py Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Initiates the scheduler to check for and potentially add new crawl tasks based on elapsed time intervals. Call this periodically from the main loop. ```python # Scheduler tick — call every few seconds from main loop: # Internally triggers addCrawler() when hourly/weekly/political intervals elapse. queing.mayAddCrawlers() ``` -------------------------------- ### Import Ad Reports with Python Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Reads downloaded ZIP/CSV report files and updates page metadata and statistics in a MongoDB collection. Pages with changed ad counts are marked for re-crawling. ```python # Configure paths at top of import_reports.py before running: # REPORTPATH = "reports/" # BASEPATH = "FacebookAdLibraryReport_2022-01-16_" # ZIPEND = "_lifelong.zip" # CSVEND = "_lifelong_advertisers.csv" # Run directly: # python import_reports.py # Sample output: # Reading United States # Got 15432 pages # Got 2843910 ads in report # Got 120 page updates # Reading Germany # Got 4821 pages # ... # Got total 87234 pages # Got total 14293847 ads in reports # Got total 1834 page updates # Got 42 uncrawlable with id=0 # Resulting MongoDB document in `pages` collection: # { # "_id": "6756153498", # "page_name": "Mike Bloomberg", # "disclaimers": ["Bloomberg Philanthropies"], # "stats": { # "": { # "US": {"spent": "1200000", "amount": 3842} # } # }, # "status": "todo" # set when ad count changes # } ``` -------------------------------- ### Fetch Currently Active Ads Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves ads that are currently active, defined as having `ad_delivery_start_time` set and `ad_delivery_stop_time` as null. Results are sorted by `ad_creation_time` in descending order and support pagination. ```bash # Ads with ad_delivery_start_time set and ad_delivery_stop_time = null. curl "https://ad-archive.nexxxt.cloud/actives?limit=50&offset=0" \ --cookie "accessKey=$ACCESS_KEY" # Response (200, JSON array sorted by ad_creation_time descending) ``` -------------------------------- ### Import reports using Python script Source: https://github.com/lejo1/facebook_ad_library/blob/master/README.md This script is used to automatically load ad reports into the database. It's part of the data ingestion process for the Facebook Ad Library. ```python import_reports.py ``` -------------------------------- ### Create mongodump archive with zstd compression Source: https://github.com/lejo1/facebook_ad_library/blob/master/README.md This command creates a compressed archive of the MongoDB database using mongodump and zstd. This method is used for efficient storage and transfer of large datasets. ```bash mongodump .. --archive | zstd -6 ``` -------------------------------- ### Utility Functions for Data Handling Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Provides utility functions for formatting data ranges, checking array sizes, and determining if data exists and is not empty. These are mixed into Vue instances. ```javascript function text_bounds(d) { if (d.lower_bound) { if (d.upper_bound) { return d.lower_bound + "-" + d.upper_bound } else { return ">" + d.lower_bound } } else { return "<" + d.upper_bound } } function arrsize(arr) { if (arr && arr.length) { return arr.length } return 0 } function c_exists(data, i) { if (data && data[i] && data[i].trim().length != 0) { return true; } return false; } Vue.mixin({ methods: { c_exists: c_exists, }, }) ``` -------------------------------- ### Fetch a single ad by ID Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves details for a specific ad using its unique identifier. Requires an `accessKey` cookie for authentication. ```APIDOC ## GET /ad/:id ### Description Retrieves details for a specific ad using its unique identifier. Requires an `accessKey` cookie for authentication. ### Method GET ### Endpoint /ad/:id ### Parameters #### Path Parameters - **id** (string) - Required - The unique identifier of the ad. ### Request Example ```bash curl "https://ad-archive.nexxxt.cloud/ad/679808276335343" \ --cookie "accessKey=$ACCESS_KEY" ``` ### Response #### Success Response (200) - **(JSON object)**: An object containing the ad details. #### Not Found Response (404) - **(string)**: "Ad not found" ### Response Example (200) ```json { "_id": "679808276335343", "ad_creation_time": "2021-12-07", "ad_creative_bodies": ["As the quotation on the wall of the Holocaust Museum..."], "ad_delivery_start_time": "2021-12-08", "ad_delivery_stop_time": "2021-12-20", "page_id": "6756153498", "page_name": "Mike Bloomberg", "spend": {"lower_bound": "300", "upper_bound": "399"}, "impressions": {"lower_bound": "10000", "upper_bound": "14999"}, "currency": "USD", "publisher_platforms": ["instagram"], "_last_updated": "2024-05-01T12:00:00Z" } ``` ``` -------------------------------- ### Content Index Calculation (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Calculates an index based on the maximum available content fields for an ad, used to determine if there's any content to display. ```javascript content_index: function () { let out = 0; const max = Math.max(arrsize(this.data.ad_creative_bodies), arrsize(this.data.ad_creative_link_captions), arrsize(this.data.ad_creative_link_titles), arrsize(this.data.ad_creative_link_descriptions)) for (let i = 0; i < max; i++) { if (c_exists(this.data.ad_creative_bodies, i) || c_exists(this.data.ad_creative_link_captions, i) || c_exists(this.data.ad_creative_link_titles, i) || c_exists(this.data.ad_creative_link_descriptions, i)) { out = i + 1; } } return out } ``` -------------------------------- ### Handle Facebook OAuth Callback Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/verification.html Parses the URL hash to extract the access token and state from the Facebook OAuth callback. It then either initiates a direct donation or retrieves and stores the access key. ```javascript var main = new Vue({ el: "#main", data: { facebook_token: "", verificationError: "", access_key: "", access_key_expires: 0, formattedExpire: "", donateMsg: "", mass_app: "", }, methods: { donateToken: function (e) { if (e) { e.preventDefault(); } let vm = this; axios.post('/addToken', { token: this.facebook_token }) .then(function (response) { vm.donateMsg = response.data; }) .catch(function (error) { vm.donateMsg = error.response.data; }); }, donateMass: function (e) { e.preventDefault(); let lines = this.mass_app.split("\n"); console.log(lines); mayspawnWindow(lines, window); } }, mounted: function () { // hash for ad and callback access access_token if (window.location.hash != "") { let content = location.hash.split("="); if (content[0] == "#access_token") { this.facebook_token = content[1].split("&")[0]; // check if we have a state let entries = location.hash.split("&"); let state = entries[entries.length-1] if (state == "state=autodonate") { (new bootstrap.Collapse(this.$refs.donateCollapse)).toggle(); this.donateToken(); } else { // regular workflow: let vm = this; (new bootstrap.Collapse(this.$refs.verificationCollapse)).toggle(); axios.post('/getAccess', { token: this.facebook_token }) .then(function (response) { vm.access_key = response.data.accessKey; vm.access_key_expires = response.data.expiresAt; let dateTime = new Date(vm.access_key_expires * 1000); vm.formattedExpire = dateTime.toGMTString(); document.cookie = "accessKey=" + vm.access_key + "; expires=" + vm.formattedExpire + "; Secure; SameSite=Strict; path=/"; (new bootstrap.Collapse(vm.$refs.donateCollapse)).toggle(); }) .catch(function (error) { vm.verificationError = error.response.data; }); } } } } }); ``` -------------------------------- ### Full-text search by page name Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Performs a full-text search across ad page names. Results are sorted by relevance score. Supports pagination. Requires an `accessKey` cookie. ```APIDOC ## GET /search/:query ### Description Performs a full-text search across ad page names. Results are sorted by relevance score. Supports pagination. Requires an `accessKey` cookie. ### Method GET ### Endpoint /search/:query ### Parameters #### Path Parameters - **query** (string) - Required - The search query string. #### Query Parameters - **limit** (integer) - Optional - The maximum number of results to return. ### Request Example ```bash curl "https://ad-archive.nexxxt.cloud/search/Bloomberg?limit=10" \ --cookie "accessKey=$ACCESS_KEY" ``` ### Response #### Success Response (200) - **(JSON array)**: An array of ad objects sorted by relevance. ``` -------------------------------- ### Full-Text Search Ads by Page Name Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Performs a full-text search on ad data using a query string, matching against the `page_name` field. Results are sorted by relevance score. Supports limiting the number of results. ```bash # Uses MongoDB $text search index on page_name, sorted by text relevance score. curl "https://ad-archive.nexxxt.cloud/search/Bloomberg?limit=10" \ --cookie "accessKey=$ACCESS_KEY" # Response (200, JSON array sorted by relevance) ``` -------------------------------- ### Fetch Most Recently Inserted Ads Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves the most recently inserted ads into the database, ordered by insertion time (newest first). Supports limiting the number of results. ```bash # Uses natural MongoDB insertion order (newest first). curl "https://ad-archive.nexxxt.cloud/latest?limit=25" \ --cookie "accessKey=$ACCESS_KEY" ``` -------------------------------- ### POST /addToken Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Donate a Facebook access token for crawling. This endpoint validates the token, confirms it can make ads_archive requests, and stores it. It handles successful additions, existing tokens (upserting), and invalid tokens or missing permissions. ```APIDOC ## POST /addToken — Donate a Facebook access token for crawling ### Description Validates the token against Facebook's debug_token endpoint AND confirms it can make ads_archive requests, then stores it in MongoDB. ### Method POST ### Endpoint https://ad-archive.nexxxt.cloud/addToken ### Request Body - **token** (string) - Required - The Facebook access token to donate. ### Request Example ```json { "token": "EAABsbCS4ZCpsBO..." } ``` ### Response #### Success Response (200) - **message** (string) - A confirmation message, e.g., "Successfully Added token! Thanks!" #### Error Response (400) - **error** (string) - An error message if the token is invalid or lacks `ads_read` permission. ``` -------------------------------- ### Obtain JWT Access Key Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Exchanges a valid Facebook access token for an API access key. The provided Facebook token must be capable of querying the `ads_archive` endpoint. The response includes the `accessKey` and its expiration time. ```bash curl -X POST https://ad-archive.nexxxt.cloud/getAccess \ -H "Content-Type: application/json" \ -d '{"token": "EAABsbCS4ZCpsBO..."}' # Success (200): # { # "accessKey": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", # "expiresAt": 1783000000 # } # Failure — token can't access ads_archive (403 equivalent): # "Your token can't be used to access ads, have you validated your account? # See https://www.facebook.com/ads/library/api/ for all the necessary steps." # Store the key in a cookie for subsequent authenticated requests: ACCESS_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` -------------------------------- ### Ad List Component (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Vue component to display a list of ads fetched from an endpoint. Handles loading more ads and displaying messages for no results or errors. ```javascript Vue.component('adlist', { props: ["endpoint", "input"], template: `
Load more

You've reached the end of your query.

Loading...
`, data() { return { msg: "", ads: [], next: true, loading: false }; }, methods: { load: function () { const vm = this; this.loading = true; axios.get(this.endpoint + this.input + "?offset=" + this.ads.length) .then(function (response) { if (response.data == null) { vm.msg = "No (more) ads found!"; vm.next = false; } else { vm.ads = vm.ads.concat(response.data); if (response.data.length < 100) { vm.next = false; } } }) .catch(function (error) { vm.msg = error.response.data; vm.next = false; }) .then(function () { vm.loading = false; }); }, submit: function () { this.ads = []; this.next = true; this.load(); } } }) ``` -------------------------------- ### Restricted Download Link Component Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html A Vue.js component that creates a download link with an appended authorization token. The 'href' prop is used to construct the full URL. ```javascript Vue.component('restrictedlink', { props: ["href"], name: "restrictedlink", data: function() { return {} }, template: ``, computed: { reallink: function () { return this.href + "?Authorization=" + this.$root.downloadToken } }, }); ``` -------------------------------- ### Chart Color Definitions Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Defines arrays for background and border colors used in charts. These are constants for chart styling. ```javascript const backgroundColors = [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ]; const borderColors = [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ]; ``` -------------------------------- ### Add Facebook Access Token via POST /addToken Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Donates a Facebook access token for crawling. The token is validated against Facebook's debug_token endpoint and confirmed for ads_archive requests before being stored. Handles success, existing tokens (upserts), and invalid tokens with 400 errors. ```bash # Validates the token against Facebook's debug_token endpoint AND # confirms it can make ads_archive requests, then stores it in MongoDB. curl -X POST https://ad-archive.nexxxt.cloud/addToken \ -H "Content-Type: application/json" \ -d '{"token": "EAABsbCS4ZCpsBO..."}' # Success (200): "Successfully Added token! Thanks!" # Already exists: upserts with updated token value. # Invalid / no ads_read permission: 400 error message. ``` -------------------------------- ### Ad Form Component (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Vue component for an ad search form. It takes a page ID or search query as input and triggers the ad list loading upon submission. ```javascript Vue.component('adform', { template: `

` }) ``` -------------------------------- ### Fetch Ads by Page ID Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves ads associated with a specific Facebook page ID. Ads are sorted by `ad_creation_time` in descending order. Supports pagination (`offset`, `limit`) and field selection (`fields`). ```bash # Returns ads sorted by ad_creation_time descending. # Supports: ?offset=0&limit=100&fields=page_name,spend,ad_creation_time curl "https://ad-archive.nexxxt.cloud/adsbypage/6756153498?limit=5&fields=ad_creation_time,spend,page_name" \ --cookie "accessKey=$ACCESS_KEY" # Response (200, JSON array): # [ # {"_id": "679808276335343", "ad_creation_time": "2021-12-07", # "page_name": "Mike Bloomberg", "spend": {"lower_bound": "300", "upper_bound": "399"}}, # ... # ] ``` -------------------------------- ### Ad Link Computed Properties (Vue.js) Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html Provides various computed properties for generating different types of links related to an ad, such as its direct library link, raw link, and page link. ```javascript computed: { adlink: function () { return "/#ad=" + this.data._id }, liblink: function () { return "https://www.facebook.com/ads/library/?id=" + this.data._id }, rawlink: function () { return "/ad/" + this.data._id }, pagelink: function () { return "https://www.facebook.com/ads/library/?active_status=all&ad_type=all&country=ALL&view_all_page_id=" + this.data.page_id + "&search_type=page&media_type=all" } } ``` -------------------------------- ### Most recently inserted ads Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves a list of ads based on their insertion order into the database (newest first). Supports pagination. Requires an `accessKey` cookie. ```APIDOC ## GET /latest ### Description Retrieves a list of ads based on their insertion order into the database (newest first). Supports pagination. Requires an `accessKey` cookie. ### Method GET ### Endpoint /latest ### Parameters #### Query Parameters - **limit** (integer) - Optional - The maximum number of ads to return. ### Request Example ```bash curl "https://ad-archive.nexxxt.cloud/latest?limit=25" \ --cookie "accessKey=$ACCESS_KEY" ``` ### Response #### Success Response (200) - **(JSON array)**: An array of the most recently inserted ad objects. ``` -------------------------------- ### Total ad count (public) Source: https://context7.com/lejo1/facebook_ad_library/llms.txt Retrieves the total count of ads available in the archive. This endpoint is publicly accessible and does not require authentication. ```APIDOC ## GET /total ### Description Retrieves the total count of ads available in the archive. This endpoint is publicly accessible and does not require authentication. ### Method GET ### Endpoint /total ### Response #### Success Response (200) - **(plain text)**: The total number of ads. ### Request Example ```bash curl https://ad-archive.nexxxt.cloud/total ``` ### Response Example ``` 184729403 ``` ``` -------------------------------- ### Alert Component Source: https://github.com/lejo1/facebook_ad_library/blob/master/web/frontend.html A Vue.js component for displaying dismissible alerts. It supports different 'kinds' (e.g., 'danger', 'success') and binds to a message property. Emits a 'clear' event when dismissed. ```javascript Vue.component('alert', { model: { prop: "msg", event: "clear" }, props: ["kind", "msg"], template: `
{{ header }} {{ msg }}
`, methods: { dissmiss: function () { this.$emit("clear", ""); } }, computed: { colour: function () { return "alert-" + this.kind; }, header: function () { return ((this.kind == "danger") ? "Error:" : "Success:") } }, }); ```