Use Facebook Political & Commercial Ad Transparency Extractor with Python
Search the Facebook Ad Library by keyword, advertiser Page ID, Ad ID, or funding-entity name, and get ad text, media, run dates, platforms, and spend or impressions for political and issue ads.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"adActiveStatus": "active",
"adType": "all",
"includeEuDetails": false,
"maxItems": 3,
"mediaType": "all",
"mode": "search",
"onlyTotal": false,
"rehostMedia": false,
"resultsPerSearch": 3,
"searchTerms": [
"coffee"
],
"searchType": "keyword_unordered",
"sortBy": "relevance"
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | optional | What to scrape. Leave empty to process every target list you filled in (searchTerms, pageIds, adIds, bylines) in one run. |
searchTerms | array | optional | Keywords to search for in the Ad Library, one search per term. Example: ["coffee", "nike"]. |
pageIds | array | optional | Facebook Page IDs to fetch all ads from (numeric IDs, or URLs containing view_all_page_id= or profile.php?id=). Each page is searched separately. |
adIds | array | optional | Ad Library IDs to fetch directly (numeric IDs or Ads Library URLs containing ?id=). All other filters are ignored in this mode. |
bylines | array | optional | Funding-entity / committee names from the "Paid for by" disclaimer on political and issue ads. Matching is exact (case-insensitive fallback): use the name as it appears in the ad. Byline targets always search the political & issue archive, since the Ad Library ignores this filter elsewhere. |
country | string | optional | Country to search ads in, or ALL for every country. Ignored when the Countries multi-select below is set. |
countries | array | optional | One or more country codes, searched together as a union (unlike the single Country field). Takes precedence over Country when set. |
adActiveStatus | string | optional | Filter by ad active status. |
adType | string | optional | Filter by ad category. Note: the political-and-issue archive excludes EU/EEA countries, so EU political searches automatically use All Types (EU transparency stream). |
mediaType | string | optional | Filter by the ad's creative media type. |
publisherPlatforms | array | optional | Only return ads shown on these platforms. Leave empty for all platforms. |
contentLanguages | array | optional | 2-letter ISO language codes (e.g. en, de, es) to restrict ads by creative language. |
searchType | string | optional | How keyword search terms are matched (mode=search only). |
startDate | string | optional | Only include ads that had impressions on or after this date (YYYY-MM-DD). Combined with End Date it forms a delivery window. |
endDate | string | optional | Only include ads that had impressions on or before this date (YYYY-MM-DD). |
sortBy | string | optional | Order results by relevance (Facebook's default) or by total impressions. |
resultsPerSearch | integer | optional | Maximum number of ads to extract per search term / page / ad / byline. |
maxItems | integer | optional | Stop the whole run after this many ads, across all search targets. |
includeEuDetails | boolean | optional | Fetch per-ad detail data: EU total reach, age/country/gender reach breakdown, targeting, payer/beneficiary, advertiser Instagram profile and page About. Adds one extra request per ad. |
rehostMedia | boolean | optional | Copy each ad's primary media into this run's key-value store and output the permanent URL (Facebook's own media URLs expire after a few days). The original signed URL is kept in media_url_original. |
onlyTotal | boolean | optional | Skip ad scraping and return only the total number of results per search target (one lightweight page request, no browser). Counts respect the keyword, country, status, ad type, media type and date window; Facebook caps displayed totals at 50,001 (50,000+). Not available for byline searches. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/6Dh4fWQXIee4jhMOE/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
One record per Facebook Ad Library ad, with creative text, media, run dates, platforms, political spend/impressions and EU DSA transparency fields. Records where an ad did not receive a field (e.g. spend on a commercial ad) simply omit it.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build WuTbehzZYnu3x7Fwe and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.