Docs · Ad Library ad details
Ad Library ad details
Full detail for one ad from Meta's Ad Library, by its id or URL.
https://api.lurkapi.com /v1/facebook/adLibrary/adWhen to use this
Use it to get one ad in full after a search: its copy, every image and video, landing page, platforms and run dates. Pass its ad_archive_id as id, or an Ad Library link as url. Top-level keys here are camelCase (adArchiveID, isActive).
Two more differences from the list endpoints: publisherPlatform values are lowercase, and snapshot.body is a plain string. Ads with several versions keep their creative in snapshot.cards.
Returns 404 not_found if the ad doesn't exist.
Example request
Copy one and replace YOUR_API_KEY with your API key (get one free). The code versions read the key from the LURKAPI_API_KEY environment variable instead.
curl "https://api.lurkapi.com/v1/facebook/adLibrary/ad?id=2763572613808775" \
-H "x-api-key: YOUR_API_KEY"Paste it into a terminal, with your key in place of YOUR_API_KEY.
const params = new URLSearchParams({
id: "2763572613808775",
});
const res = await fetch(`https://api.lurkapi.com/v1/facebook/adLibrary/ad?${params}`, {
headers: { "x-api-key": process.env.LURKAPI_API_KEY },
});
const data = await res.json();
if (!data.success) throw new Error(`${data.code}: ${data.error}`);
console.log(data.publisherPlatform);Node 18+. Save it as lurk.mjs and run node lurk.mjs. It reads your key from LURKAPI_API_KEY.
import os
import requests
res = requests.get(
"https://api.lurkapi.com/v1/facebook/adLibrary/ad",
params={
"id": "2763572613808775",
},
headers={"x-api-key": os.environ["LURKAPI_API_KEY"]},
timeout=60,
)
data = res.json()
if not data["success"]:
raise RuntimeError(f"{data['code']}: {data['error']}")
print(data["publisherPlatform"])Run pip install requests, save it as lurk.py and run python lurk.py. It reads your key from LURKAPI_API_KEY.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
func main() {
params := url.Values{}
params.Set("id", "2763572613808775")
req, _ := http.NewRequest("GET", "https://api.lurkapi.com/v1/facebook/adLibrary/ad?"+params.Encode(), nil)
req.Header.Set("x-api-key", os.Getenv("LURKAPI_API_KEY"))
client := &http.Client{Timeout: 60 * time.Second}
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if data["success"] != true {
panic(fmt.Sprintf("%v: %v", data["code"], data["error"]))
}
fmt.Println(data["publisherPlatform"])
}Save it as main.go and run go run main.go. It reads your key from LURKAPI_API_KEY.
<?php
$params = http_build_query([
'id' => '2763572613808775',
]);
$context = stream_context_create(['http' => [
'header' => 'x-api-key: ' . getenv('LURKAPI_API_KEY'),
'timeout' => 60,
'ignore_errors' => true,
]]);
$body = file_get_contents('https://api.lurkapi.com/v1/facebook/adLibrary/ad?' . $params, false, $context);
$data = json_decode($body, true);
if (!$data['success']) {
throw new Exception("{$data['code']}: {$data['error']}");
}
print_r($data['publisherPlatform']);Save it as lurk.php and run php lurk.php. It reads your key from LURKAPI_API_KEY.
require 'json'
require 'net/http'
uri = URI('https://api.lurkapi.com/v1/facebook/adLibrary/ad')
uri.query = URI.encode_www_form(
'id' => '2763572613808775',
)
req = Net::HTTP::Get.new(uri)
req['x-api-key'] = ENV.fetch('LURKAPI_API_KEY')
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', read_timeout: 60) do |http|
http.request(req)
end
data = JSON.parse(res.body)
raise "#{data['code']}: #{data['error']}" unless data['success']
puts data['publisherPlatform']Save it as lurk.rb and run ruby lurk.rb. It reads your key from LURKAPI_API_KEY.
Parameters
Add them to the URL after a ?, as in ?id=2763572613808775. Join several with &.
| Parameter | Description |
|---|---|
idstringoptional | The ad's Ad Library id (ad_archive_id), e.g. 2763572613808775. Required unless you pass url.
|
urlstringoptional | An Ad Library link containing ?id=…, e.g. https://www.facebook.com/ads/library/?id=2763572613808775. |
trimbooleanoptional | true drops image and video URLs (inside snapshot.cards too). All text is kept.
|
Example response
A real response, captured from the live API. Lists are trimmed to a couple of items, and long text such as media links is cut short with ….
Show the example response (6 KB)Hide
{
"success": true,
"credits_remaining": 997,
"credits_charged": 1,
"adArchiveID": "2763572613808775",
"adid": null,
"categories": [
"POLITICAL"
],
"collationCount": null,
"collationID": null,
"containsDigitallyCreatedMedia": false,
"containsSensitiveContent": false,
"currency": "EUR",
"endDate": 1715670000,
"endDateString": null,
"fevInfo": {
"authorized_entity_text": "This information was provided to Meta.",
"submitted_on": "Submitted Nov 27, 2019",
"phone": "+3222834602",
"email": "epfacebook@europarl.europa.eu",
"website": "https://europarl.europa.eu/",
"address": "rue Wiertz 60, Brussels 1047, BE",
"page_admin_additional_info": null,
"free_form_additional_info": {
"treasurer_name": null,
"cnpj_or_cpf": null,
"committee_id": null,
"point_of_contact": null,
"director_name": null,
"corporate_purpose": null,
"promoter_name": null,
"agent_name": null,
"gst_number": null,
"ec_certificates": []
},
"page_name_additional_info": null,
"fec_additional_info": null,
"learn_more_content": {
"help_center_text": "Visit the Help Center to learn about what we require from advertisers to…",
"help_center_url": "https://www.facebook.com/help/180607332665293?ref=infosheet"
}
},
"gatedType": "eligible",
"hasUserReported": false,
"hideDataStatus": "NONE",
"impressionsWithIndex": {
"impressionsText": "45K - 50K",
"impressionsIndex": -1
},
"isAAAEligible": false,
"isActive": false,
"menuItems": [],
"pageID": "178362315106",
"pageIsDeleted": false,
"pageName": "European Parliament",
"publisherPlatform": [
"instagram"
],
"reachEstimate": "500K - 1M",
"regionalRegulationData": {
"finserv": {
"is_deemed_finserv": false,
"is_limited_delivery": false
},
"tw_anti_scam": {
"is_limited_delivery": false
}
},
"reportCount": null,
"snapshot": {
"additional_info": null,
"branded_content": null,
"brazil_tax_id": "",
"byline": "European Parliament",
"caption": "europarl.europa.eu",
"cards": [
{
"body": "🗳️ Do you know which day you’ll be voting? Check this map to see when y…",
"caption": "",
"cta_text": "Learn More",
"cta_type": "LEARN_MORE",
"image_crops": [],
"link_description": null,
"link_url": "https://www.europarl.europa.eu/topics/en/article/20240429STO20939/voting…",
"original_image_url": null,
"resized_image_url": null,
"title": null,
"video_hd_url": "https://video.ffcm1-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQP5D0FnYXk6fnmJ18R4…",
"video_preview_image_url": "https://scontent.ffcm1-1.fna.fbcdn.net/v/t39.35426-6/440941922_819247863…",
"video_sd_url": "https://video.ffcm1-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQNM-RLmoUEb6Au9EgSG…",
"watermarked_resized_image_url": "",
"watermarked_video_hd_url": "",
"watermarked_video_sd_url": ""
},
{
"body": "🗳️ Do you know which day you’ll be voting? Check this map to see when y…",
"caption": "",
"cta_text": "Learn More",
"cta_type": "LEARN_MORE",
"image_crops": [],
"link_description": null,
"link_url": "https://www.europarl.europa.eu/topics/en/article/20240429STO20939/voting…",
"original_image_url": "https://scontent.ffcm1-1.fna.fbcdn.net/v/t39.35426-6/438242108_969883858…",
"resized_image_url": "https://scontent.ffcm1-2.fna.fbcdn.net/v/t39.35426-6/438196942_117125097…",
"title": null,
"video_hd_url": "https://video.ffcm1-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQP5D0FnYXk6fnmJ18R4…",
"video_preview_image_url": "https://scontent.ffcm1-1.fna.fbcdn.net/v/t39.35426-6/440941922_819247863…",
"video_sd_url": "https://video.ffcm1-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQNM-RLmoUEb6Au9EgSG…",
"watermarked_resized_image_url": "",
"watermarked_video_hd_url": "",
"watermarked_video_sd_url": ""
}
],
"country_iso_code": "BE",
"cta_text": "Learn more",
"cta_type": "LEARN_MORE",
"disclaimer_label": "PAID_FOR_BY",
"display_format": "DCO",
"ec_certificates": [],
"event": null,
"extra_images": [],
"extra_links": [],
"extra_texts": [],
"extra_videos": [],
"images": [],
"is_reshared": false,
"link_description": null,
"link_url": "https://www.europarl.europa.eu/topics/en/article/20240429STO20939/voting…",
"page_categories": [
"Organization"
],
"page_id": "178362315106",
"page_is_deleted": false,
"page_like_count": 2804395,
"page_name": "European Parliament",
"page_profile_picture_url": "https://scontent.ffcm1-1.fna.fbcdn.net/v/t39.35426-6/438222673_409122951…",
"page_profile_uri": "https://facebook.com/europeanparliament",
"root_reshared_post": null,
"title": null,
"videos": [],
"body": "🗳️ Do you know which day you’ll be voting? Check this map to see when y…",
"final_url": null
},
"spend": "<€100",
"startDate": 1715324400,
"startDateString": null,
"stateMediaRunLabel": null,
"totalActiveTime": null,
"url": null,
"aaa_info": null
}Response fields
Every field in a successful response, and what it means. A name with [] belongs to each item of a list: snapshot.images[].original_image_url is the original_image_url of every item in snapshot.images. null means there's no value.
Show all 101 fieldsHide
| Field | Type | Description |
|---|---|---|
| success | true | Always true here; errors have success: false. |
| credits_remaining | number | Your balance after this call. On anonymous playground calls: free tries left today. |
| credits_charged | number | Credits this call cost; 0 on free endpoints. On anonymous playground calls: tries used (1). |
| adArchiveID | stringcan be null | The Ad Library id. Pass it as id to the ad details endpoint. |
| adid | stringcan be null | Meta's internal ad id. null in every response we've seen; use the Ad Library id. |
| pageID | stringcan be null | The advertiser's Facebook page id. Pass it as pageId to the company ads endpoint. |
| pageName | stringcan be null | The advertiser's page name. |
| isActive | boolean | Whether the ad is running now. |
| startDate | numbercan be null | The day the ad started running, as Unix seconds at midnight Pacific time. |
| endDate | numbercan be null | The day the ad stopped, or its latest delivery day if still running, as Unix seconds at midnight Pacific time. |
| startDateString | stringcan be null | The same date as ISO 8601 text when Facebook includes it; null in every response we've seen, so use the Unix seconds field. |
| endDateString | stringcan be null | The same date as ISO 8601 text when Facebook includes it; null in every response we've seen, so use the Unix seconds field. |
| totalActiveTime | numbercan be null | Seconds the ad has run, when Facebook reports it; usually null. |
| publisherPlatform | string[] | Where it ran, lowercase: facebook, instagram, audience_network, messenger, threads. |
| collationCount | numbercan be null | How many near-identical versions are grouped under this ad. A high count means the advertiser is scaling it; null for a single version. |
| collationID | stringcan be null | Id shared by the grouped versions of this ad; null for a single version. |
| snapshot | object | The creative: copy, media, landing page and advertiser details (snake_case inside). |
| snapshot.page_id | stringcan be null | The advertiser's page id (same as the ad's). |
| snapshot.page_name | stringcan be null | The advertiser's page name as shown on the ad. |
| snapshot.page_profile_uri | stringcan be null | The advertiser's Facebook page URL. |
| snapshot.page_profile_picture_url | stringcan be null | The advertiser's profile picture, 60×60 px. |
| snapshot.page_like_count | numbercan be null | Followers of the advertiser's page. |
| snapshot.page_categories | string[] | Page categories, e.g. ["Outdoor & Sporting Goods Company"]. |
| snapshot.page_is_deleted | boolean | Whether the advertiser's page has been deleted. |
| snapshot.title | stringcan be null | Headline under the creative; null when there is none. |
| snapshot.caption | stringcan be null | Display link under the creative, usually the domain (e.g. HOKA.com). |
| snapshot.link_url | stringcan be null | Landing page the ad sends people to, UTM tags included. |
| snapshot.link_description | stringcan be null | Text under the headline; null when there is none. |
| snapshot.cta_text | stringcan be null | Button label, e.g. "Shop now". |
| snapshot.cta_type | stringcan be null | Button type, e.g. SHOP_NOW, LEARN_MORE, NO_BUTTON. |
| snapshot.display_format | stringcan be null | Creative format, e.g. IMAGE, VIDEO, DCO (dynamic creative), DPA (dynamic product ad) or MULTI_IMAGES. For DCO and DPA ads the creative is in cards. |
| snapshot.images | object[] | Image creatives. Empty for video and card-based ads, and with trim=true. |
| snapshot.images[].original_image_url | stringcan be null | Full-size image URL. Facebook's links expire after a few days, so download what you need. |
| snapshot.images[].resized_image_url | stringcan be null | 600 px image URL, good for thumbnails. |
| snapshot.images[].watermarked_resized_image_url | string | Watermarked copy; empty or null in every response we've seen. |
| snapshot.images[].image_crops | any[] | Facebook internal field; an empty array in every response we've seen. Crop hints for the image. |
| snapshot.videos | object[] | Video creatives. Empty for image and card-based ads, and with trim=true. |
| snapshot.videos[].video_hd_url | stringcan be null | HD video file URL; null if Facebook has no HD version. |
| snapshot.videos[].video_sd_url | stringcan be null | SD video file URL. |
| snapshot.videos[].video_preview_image_url | stringcan be null | Poster frame (still image) URL. |
| snapshot.videos[].watermarked_video_hd_url | stringcan be null | Watermarked copy; empty or null in every response we've seen. |
| snapshot.videos[].watermarked_video_sd_url | stringcan be null | Watermarked copy; empty or null in every response we've seen. |
| snapshot.cards | object[] | Carousel, dynamic-creative and product versions, each with its own copy and media. With trim=true the text stays and media URLs are null. |
| snapshot.cards[].title | stringcan be null | This version's headline. |
| snapshot.cards[].body | stringcan be null | This version's primary text. |
| snapshot.cards[].caption | stringcan be null | This version's display link, usually the domain. |
| snapshot.cards[].cta_text | stringcan be null | Button label, e.g. "Shop now". |
| snapshot.cards[].cta_type | stringcan be null | Button type, e.g. SHOP_NOW, LEARN_MORE. |
| snapshot.cards[].link_url | stringcan be null | This version's landing page URL. |
| snapshot.cards[].link_description | stringcan be null | Text under the headline; null when there is none. |
| snapshot.cards[].original_image_url | stringcan be null | Full-size image URL; null for video versions. |
| snapshot.cards[].resized_image_url | stringcan be null | 600 px image URL; null for video versions. |
| snapshot.cards[].watermarked_resized_image_url | string | Watermarked copy; empty or null in every response we've seen. |
| snapshot.cards[].image_crops | any[] | Facebook internal field; an empty array in every response we've seen. Crop hints for the image. |
| snapshot.cards[].video_hd_url | stringcan be null | HD video file URL; null for image versions. |
| snapshot.cards[].video_sd_url | stringcan be null | SD video file URL; null for image versions. |
| snapshot.cards[].video_preview_image_url | stringcan be null | Poster frame URL; null for image versions. |
| snapshot.cards[].watermarked_video_hd_url | stringcan be null | Watermarked copy; empty or null in every response we've seen. |
| snapshot.cards[].watermarked_video_sd_url | stringcan be null | Watermarked copy; empty or null in every response we've seen. |
| snapshot.byline | stringcan be null | Who paid for the ad (the "Paid for by" name) on political and issue ads; null on most others. |
| snapshot.disclaimer_label | stringcan be null | The disclaimer kind on political and issue ads, e.g. PAID_FOR_BY (the name is in byline); null otherwise. |
| snapshot.country_iso_code | stringcan be null | Facebook internal field; null in every response we've seen. A country code on some ads. |
| snapshot.is_reshared | boolean | Whether the ad reshares an existing post (see root_reshared_post). |
| snapshot.root_reshared_post | any | The original post when is_reshared is true; null otherwise. |
| snapshot.branded_content | any | Paid partnership details when the ad is branded content; null otherwise. |
| snapshot.additional_info | any | Facebook internal field; null in every response we've seen. |
| snapshot.brazil_tax_id | any | Advertiser tax id shown on ads under Brazil's rules; null otherwise. |
| snapshot.ec_certificates | any[] | Facebook internal field; an empty array in every response we've seen. |
| snapshot.event | any | Event details on event ads; null otherwise. |
| snapshot.extra_images | any[] | More images from dynamic creative; usually empty. |
| snapshot.extra_links | any[] | More landing page URLs from dynamic creative; usually empty. |
| snapshot.extra_texts | any[] | More text variations from dynamic creative; usually empty. |
| snapshot.extra_videos | any[] | More videos from dynamic creative; usually empty. |
| snapshot.body | stringcan be null | The ad's primary text as a plain string; null when the copy is only in cards. |
| snapshot.final_url | stringcan be null | Final landing URL after redirects, when Facebook reports it; usually null. |
| categories | string[] | Special ad categories Facebook assigned. ["UNKNOWN"] for ordinary ads. |
| currency | string | Currency code of spend (e.g. USD) when spend is reported; an empty string otherwise. |
| spend | any | Spend range as Facebook shows it, e.g. "$600K - $700K" or ">$1M" (see currency). Political and issue ads only; null otherwise. |
| impressionsWithIndex | object | Impressions range, reported for political and issue ads only. |
| impressionsWithIndex.impressionsText | stringcan be null | The range as Facebook shows it; null when not reported. |
| impressionsWithIndex.impressionsIndex | number | Facebook's bucket number for the range; -1 when not reported. Ad details usually omit it. |
| reachEstimate | any | Estimated reach as Facebook shows it, e.g. ">1M", on political and issue ads and some EU-delivered ads; null otherwise. |
| url | stringcan be null | Ad Library link for this ad. null in every response we've seen; build it as https://www.facebook.com/ads/library/?id=<Ad Library id>. |
| containsDigitallyCreatedMedia | boolean | Whether Facebook labels the creative as AI-generated or digitally altered. |
| containsSensitiveContent | boolean | Facebook internal field; false in every response we've seen. Facebook's sensitive-content flag. |
| gatedType | string | Facebook internal field; ELIGIBLE (lowercase in ad details) in every response we've seen. |
| hasUserReported | boolean | Whether the viewer reported the ad. Always false, since we read the Ad Library logged out. |
| hideDataStatus | string | Facebook internal field; NONE in every response we've seen. |
| isAAAEligible | boolean | Facebook flag, true on a few ads; meaning undocumented. Ad details' aaa_info holds the related data when present. |
| menuItems | any[] | Facebook internal field; an empty array in every response we've seen. |
| pageIsDeleted | boolean | Whether the advertiser's page has been deleted. |
| regionalRegulationData | object | Delivery limits under regional ad rules. |
| regionalRegulationData.finserv | object | Financial-services ad rules. |
| regionalRegulationData.finserv.is_deemed_finserv | boolean | Facebook treats the ad as a financial-services ad. |
| regionalRegulationData.finserv.is_limited_delivery | boolean | Delivery is limited under financial-services rules. |
| regionalRegulationData.tw_anti_scam | object | Taiwan's anti-scam ad rules. |
| regionalRegulationData.tw_anti_scam.is_limited_delivery | boolean | Delivery is limited under Taiwan's anti-scam rules. |
| reportCount | numbercan be null | Times the ad was reported, when disclosed; null in every response we've seen. |
| fevInfo | any | Facebook internal field; null in every response we've seen. |
| stateMediaRunLabel | any | Label for ads run by state-controlled media; null otherwise. |
| aaa_info | any | Extra transparency data on some ads (see isAAAEligible); null in every response we've seen. |
Cost and freshness
Each call costs 1 credit. Calls turned away before they reach the platform (invalid parameters, a key or balance problem, a rate limit) are free, and so are 5xx errors. A not_found is charged, since the platform was checked. How charging works.
Answers are cached for up to 6 hours. A repeat call within that time can return the same data, and costs the same.
Errors
A failed call returns success: false, an error message, a code your program can check, and a docs link. These are the errors this endpoint can return. All error codes.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key | No API key was sent. Put it in the x-api-key header. |
| 401 | invalid_api_key | This key doesn't exist or was revoked. |
| 402 | insufficient_credits | Not enough credits for this call. Buy a pack, or wait for tomorrow's free top-up. |
| 402 | over_max_credits | This call would cost more than the max_credits you set. The error says what it needs; raise max_credits to allow it. Not charged. |
| 403 | account_suspended | This account is suspended. Email support@lurkapi.com. Not charged. |
| 404 | not_found | What you asked for (a post, an ad, a subreddit) doesn't exist, or there's no endpoint at that path. Charged if the platform was checked. |
| 405 | method_not_allowed | Endpoints only take GET, with parameters in the URL. |
| 429 | rate_limited | Too many calls in a short time. Wait as many seconds as the retry-after header says, then retry. |
| 500 | internal_error | Something broke on our side. Retry. Not charged. |
| 502 | upstream_error | The platform didn't send a usable answer. Retry. Not charged. |
| 503 | upstream_busy | All our connections to the platform are busy. Retry in a few seconds. Not charged. |
| 504 | upstream_timeout | The platform took over 30 seconds to answer (90 for web pages). Retry. Not charged. |
Use it in Claude
Connect LurkAPI to Claude and ask in plain English. This endpoint is the facebook_ad tool. For example:
Use LurkAPI's facebook_ad tool with id "2763572613808775", then summarize what you find.Claude calls facebook_ad with arguments like these, and each call costs 1 credit:
{"id": "2763572613808775"}To save tokens, tool results leave out empty fields, keep one URL per image or video, and give transcripts as plain text unless you pass timestamps=true. A result over 45,000 characters comes in parts. Its _note gives the offset for the next part, and later parts are free for 15 minutes. Pass trim=false for the full response.
From the blog
All posts
· 5 min read
Best Reddit scrapers in 2026: price and speed compared
LurkAPI is the best Reddit scraper: 1,000 subreddit posts for $0.04, a feed in 0.92 s, and search and comment threads on the same key.

· 5 min read
Instagram MCP server for Claude and ChatGPT
Claude can’t read Instagram on its own. Add one MCP URL and it reads any public account’s posts, reels and comments, 1 credit a call, no Meta app.

· 5 min read
Best Threads scrapers in 2026: what 1,000 posts cost
LurkAPI is the best Threads scraper: 1,000 posts for $0.06, a page in about 1.2 s, and pages back to an account's first post.