Raw Meta ads
Live ads from the public Meta Ad Library, normalized into one schema. Virlo reads the Ad Library when you call, and the answer comes back in the same request.
- What it does
- Finds ads and advertisers in the Meta Ad Library as they are right now.
- You send
- A keyword, an ad ID, an advertiser name or an advertiser's Facebook page ID, plus optional filters.
- You get back
- Ads with their text, images, videos, dates and platforms, or advertisers with their page ID and follower counts.
- Cost
- $0.01 (1 credit) per call, including a call that finds nothing. Errors are free.
- Synchronous. No job to poll. Each call returns one page. Most calls answer in 2 to 8 seconds, and a slow one can take about 25, so set your client timeout to at least 30 seconds.
- Media links expire. Image and video URLs are Facebook CDN links that stop working after a few days. Download what you need to keep.
- Spend and reach are ranges, and mostly
null. The Ad Library shows reach for ads shown in the EU or UK, and spend, reach and impressions for political and issue ads. Every other ad hasnullthere. - Unprocessed. No AI fields or history. To collect Meta ads next to TikTok, YouTube and Instagram research, turn on
meta_ads_enabledon an agent. For live TikTok, YouTube and Instagram data, see Raw data.
Which one do I need?
- Ads that mention a product, topic or brand: search ads
- One ad you already have the ID of: one ad
- The page ID of an advertiser: search advertisers
- Everything one advertiser is running: an advertiser's ads, by page ID or by name
Filters
Search ads and an advertiser's ads take three optional filters. Search advertisers takes country only.
- Name
country- Type
- string
- Description
Country where the ads were shown: a 2-letter ISO code such as
US,GBorDE, orALL. DefaultALL.UKis read asGB.
- Name
status- Type
- string
- Description
active(running now),inactive(stopped) orall. Defaultactive.
- Name
media_type- Type
- string
- Description
Ads that contain this media:
image,video,meme,none(text only) orall. Defaultall.
The ad shape
A single ad comes back as { "data": { ... } }. A list comes back as { "data": [ ... ], "pagination": { "next_cursor": "...", "has_more": true } }. Timestamps are ISO 8601 in UTC. Counts are numbers or null, never strings.
Every ad, from any route, has these fields:
- Name
id- Type
- string
- Description
The Ad Library ID of the ad. Pass it to one ad.
- Name
url- Type
- string
- Description
The ad in the public Ad Library.
- Name
advertiser- Type
- object
- Description
page_id,name,avatar_urlandurl(the advertiser's Facebook page).nameandavatar_urlcan benull.
- Name
is_active- Type
- boolean | null
- Description
truewhile the ad is running.
- Name
started_at- Type
- string | null
- Description
First day the ad ran.
In a range, a null bound is open. { "lower": 1000000, "upper": null } means more than 1 million.
Search ads
Ads whose text or advertiser matches a keyword, in the Ad Library's own order. The first page has about 30 ads and each later page about 10.
Query parameters
Request
curl -G "https://api.virlo.ai/v1/raw/meta/ads/search" \
--data-urlencode "q=running shoes" \
--data-urlencode "country=US" \
--data-urlencode "media_type=video" \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"data": [
{
"id": "1184620937415528",
"url": "https://www.facebook.com/ads/library/?id=1184620937415528",
"advertiser": {
"page_id": "15087023444",
"name": "Nike",
"avatar_url": "https://scontent.xx.fbcdn.net/v/avatar.jpg",
"url": "https://www.facebook.com/nike"
},
"is_active": true,
"started_at": "2026-10-20T07:00:00.000Z",
"ended_at": null,
"publisher_platforms": ["facebook", "instagram"],
"media_type": "video",
"creatives": [
{
"body": "Built for the long run. More cushion, less weight.",
"title": "Pegasus. Your everyday trainer.",
"description": null,
"caption": "nike.com",
"cta_text": "Shop now",
"cta_type": "shop_now",
"link_url": "https://www.nike.com/running",
"image_urls": [],
"videos": [
{
"hd_url": "https://video.xx.fbcdn.net/v/example-hd.mp4",
"sd_url": "https://video.xx.fbcdn.net/v/example-sd.mp4",
"preview_image_url": "https://scontent.xx.fbcdn.net/v/poster.jpg"
}
]
}
],
"variant_count": 3,
"page_categories": ["Sportswear", "Product/service"],
"reach": null,
"spend": null,
"impressions": null,
"platform_data": {
"display_format": "video",
"collation_id": "2049316685821054",
"page_like_count": 39494133
}
}
],
"pagination": {
"next_cursor": "eyJ2IjoxLCJjIjoiTTRuNXA2In0",
"has_more": true
}
}
One ad
One ad by its Ad Library ID. An ad shown in the EU or UK also carries its exact reach and, in platform_data, its audience, payer and beneficiary.
Path parameters
- Name
ad_id- Type
- string
- Description
The Ad Library ID, digits only: the
idof an ad from any other route, or theid=in an Ad Library link.
Request
curl "https://api.virlo.ai/v1/raw/meta/ads/1096372158840215" \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"data": {
"id": "1096372158840215",
"url": "https://www.facebook.com/ads/library/?id=1096372158840215",
"advertiser": {
"page_id": "15087023444",
"name": "Nike",
"avatar_url": "https://scontent.xx.fbcdn.net/v/avatar.jpg",
"url": "https://www.facebook.com/nike"
},
"is_active": false,
"started_at": "2026-10-06T07:00:00.000Z",
"ended_at": "2026-10-24T07:00:00.000Z",
"publisher_platforms": ["facebook", "instagram"],
"media_type": "image",
"creatives": [
{
"body": "New colours for the season. Free returns for members.",
"title": "Air Max: new colours",
"description": null,
"caption": "nike.com",
"cta_text": "Shop now",
"cta_type": "shop_now",
"link_url": "https://www.nike.com/gb/air-max",
"image_urls": ["https://scontent.xx.fbcdn.net/v/creative.jpg"],
"videos": []
}
],
"variant_count": null,
"page_categories": ["Sportswear", "Product/service"],
"reach": { "lower": 412890, "upper": 412890 },
"spend": null,
"impressions": null,
"platform_data": {
"display_format": "image",
"page_like_count": 39494133,
"audience": {
"gender": "All",
"age_min": 18,
"age_max": 65,
"locations": ["United Kingdom"]
},
"payer": "NIKE, Inc.",
"beneficiary": "NIKE, Inc."
}
}
}
Search advertisers
Advertisers (Facebook pages) whose name matches, in the Ad Library's own order, usually 5 to 15. Use a result's page_id with an advertiser's ads.
This route returns one list with no pagination. A search that finds nothing returns { "data": [] } and costs $0.01.
Query parameters
- Name
q- Type
- string
- Description
Required. The advertiser's name or part of it, 1 to 200 characters.
- Name
country- Type
- string
- Description
Optional. Changes which pages the Ad Library suggests. See filters.
Advertiser fields
- Name
page_id- Type
- string
- Description
The advertiser's Facebook page ID.
- Name
name, avatar_url- Type
- string | null
- Description
Page name and avatar. The avatar link expires.
- Name
url- Type
- string
- Description
The advertiser's Facebook page.
- Name
verified- Type
- boolean | null
- Description
Whether the page has a verified badge.
- Name
likes- Type
- number | null
- Description
Facebook page likes.
Request
curl -G "https://api.virlo.ai/v1/raw/meta/advertisers/search" \
--data-urlencode "q=nike" \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"data": [
{
"page_id": "15087023444",
"name": "Nike",
"avatar_url": "https://scontent.xx.fbcdn.net/v/avatar.jpg",
"url": "https://www.facebook.com/nike",
"verified": true,
"likes": 39494133,
"category": "Sportswear Store",
"instagram_handle": "nike",
"instagram_followers": 290887065,
"platform_data": {
"page_alias": "nike",
"verification": "blue_verified",
"instagram_verified": true
}
}
]
}
An advertiser's ads
The ads of one advertiser. There are two routes, and each returns the same shape:
GET /v1/raw/meta/advertisers/:page_id/adstakes the advertiser's Facebook page ID. Use it when you know exactly which advertiser you want.GET /v1/raw/meta/advertisers/ads?name=takes a name. Virlo uses the first live advertiser the Ad Library suggests for that name. Theadvertiserobject in the response says which one it used. If it is the wrong one, call search advertisers and use the:page_idroute.
The response adds advertiser next to data and pagination: { "data": [ ... ], "advertiser": { ... }, "pagination": { ... } }. It has the advertiser fields, plus platform_data.ad_count, the number of ads that match your filters as the Ad Library counts them (it stops at 50,001). Called with :page_id, category, instagram_handle and instagram_followers are null.
Path parameters
- Name
page_id- Type
- string
- Description
The advertiser's Facebook page ID, digits only:
page_idfrom advertiser search, oradvertiser.page_idfrom any ad. Only on the:page_idroute.
Query parameters
- Name
name- Type
- string
- Description
Required on
/v1/raw/meta/advertisers/ads, and not accepted on the:page_idroute. The advertiser's name, 1 to 200 characters.
- Name
country, status, media_type- Type
- string
- Description
Optional filters.
- Name
cursor- Type
- string
- Description
next_cursorfrom the previous page. See paging. Withname, every later call reuses the advertiser the first call found.
Request
curl -G "https://api.virlo.ai/v1/raw/meta/advertisers/15087023444/ads" \
--data-urlencode "country=US" \
-H "Authorization: Bearer YOUR_API_KEY"
Response
{
"data": [
{
"id": "1184620937415528",
"url": "https://www.facebook.com/ads/library/?id=1184620937415528",
"advertiser": {
"page_id": "15087023444",
"name": "Nike",
"avatar_url": "https://scontent.xx.fbcdn.net/v/avatar.jpg",
"url": "https://www.facebook.com/nike"
},
"is_active": true,
"started_at": "2026-10-20T07:00:00.000Z",
"ended_at": null,
"publisher_platforms": ["facebook", "instagram"],
"media_type": "video",
"creatives": [
{
"body": "Built for the long run. More cushion, less weight.",
"title": "Pegasus. Your everyday trainer.",
"description": null,
"caption": "nike.com",
"cta_text": "Shop now",
"cta_type": "shop_now",
"link_url": "https://www.nike.com/running",
"image_urls": [],
"videos": [
{
"hd_url": "https://video.xx.fbcdn.net/v/example-hd.mp4",
"sd_url": "https://video.xx.fbcdn.net/v/example-sd.mp4",
"preview_image_url": "https://scontent.xx.fbcdn.net/v/poster.jpg"
}
]
}
],
"variant_count": 3,
"page_categories": ["Sportswear", "Product/service"],
"reach": null,
"spend": null,
"impressions": null,
"platform_data": {
"display_format": "video",
"page_like_count": 39494133
}
}
],
"advertiser": {
"page_id": "15087023444",
"name": "Nike",
"avatar_url": "https://scontent.xx.fbcdn.net/v/avatar.jpg",
"url": "https://www.facebook.com/nike",
"verified": true,
"likes": 39494133,
"category": "Sportswear Store",
"instagram_handle": "nike",
"instagram_followers": 290887065,
"platform_data": {
"page_alias": "nike",
"verification": "blue_verified",
"ad_count": 1240
}
},
"pagination": {
"next_cursor": "eyJ2IjoxLCJjIjoiSzJqM2g0In0",
"has_more": true
}
}
Paging through results
Search ads and an advertiser's ads return one page per call: about 30 ads on the first page, then about 10 on each page after it. Search advertisers does not page.
- Keep going while
has_moreistrue. Send the same request again withcursorset tonext_cursor. On the last pagenext_cursorisnullandhas_moreisfalse. - There is no
limit. The Ad Library decides how many ads are on a page. - Keep the filters the same. A cursor works only with the request that produced it: the same route, input and filters. Send
country=GBwith a cursor from acountry=UScall and you get400withinvalid_cursor. - Cursors are opaque and expire after 6 hours. Don't build or change one. A changed, expired or mismatched cursor returns
400withinvalid_cursor: start again from page 1. - Each page is a separate call and costs $0.01.
Page 2
curl -G "https://api.virlo.ai/v1/raw/meta/ads/search" \
--data-urlencode "q=running shoes" \
--data-urlencode "country=US" \
--data-urlencode "media_type=video" \
--data-urlencode "cursor=eyJ2IjoxLCJjIjoiTTRuNXA2In0" \
-H "Authorization: Bearer YOUR_API_KEY"
Costs and errors
- $0.01 (1 credit) per call on every route. A call that succeeds but finds nothing (an empty page or an empty advertiser list) is charged.
- Errors cost nothing. That includes every
4xxand5xxbelow.X-Costshows the charge in dollars andX-Credits-Usedin credits. - Not saved. Virlo does not keep these results for you. Call again to get fresh data, and download media you want to keep before the links expire.
Errors come back with message, error, statusCode and code (more on errors):
| Error | When it happens |
|---|---|
400 validation_error | A missing or bad parameter: no q, a q or name over 200 characters, an ad_id or page_id that is not all digits, a country that is not ALL or a 2-letter ISO code, a status or media_type outside the list, name on the page ID route, or a parameter the route doesn't take. |
400 invalid_cursor | The cursor was changed, has expired, belongs to a different request, or was sent with different filters. Start again from page 1. |
402 insufficient_credits | Your balance is below $0.01. |
404 not_found | No ad has that ID, no advertiser has that page ID, or no live advertiser matches the name. |
429 rate_limit_exceeded | Your key sent too many requests. Wait retry_after seconds. |
502 upstream_error | Virlo could not get the data from the Ad Library this time. Retry after a few seconds. |
503 service_unavailable | Raw data is briefly unavailable. Retry later. |
504 request_timeout | The Ad Library took too long to answer. Retry after a few seconds. |

