InsightSocial API
Recipes

Ad library aggregation

Pull the ads a brand is running on Meta, LinkedIn and TikTok in one parallel fan-out and flatten them into one table. 150 to 270 credits per brand.

You will build a competitive-intelligence view of every ad a company is running on Meta (Facebook and Instagram), LinkedIn and TikTok, flattened into one table. Useful for sales-call prep, market research and creative benchmarking.

Cost per run

150 to 270 credits per brand: Meta ad search (50), LinkedIn ad search (50) and TikTok ad search (metered, 50–170). Auditing a 25-company competitive set costs 3,750 to 6,750 credits. Repeating the same search is charged again, so store the results.

How do you list every ad one advertiser has live?

Query each network's public ad library in parallel with the brand name. All three have a search endpoint that takes a name, and the three calls finish together.

Every major ad network publishes a transparency library, but each has its own interface, its own query model and its own response shape, and none of the official interfaces export data.

What you need

EndpointNetworkParametersCredits
GET /v1/facebook/adlibrary/search/adsMeta Ad Libraryquery, optional country, status, media_type, start_date, end_date50
GET /v1/linkedin/ads/searchLinkedIn Ad Librarycompany, keyword or companyId, optional countries, startDate, endDate50
GET /v1/tiktok/adlibrary/searchTikTok Ad Libraryquery or advertiser_name50–170

Three networks, three parameter names

Meta takes query. LinkedIn takes company (or keyword, or companyId) and spells its dates startDate and endDate. TikTok takes query or advertiser_name. A parameter an endpoint does not know is either ignored or rejected with a 400, which is not charged. Check each endpoint's parameters in GET /v1/endpoints before you generalise the call.

The code

TikTok's ad search returns ads as posts in the standard shape (data.items[].post). The Meta and LinkedIn ad searches are beta endpoints: data.items is a list of each network's own ad records, with our conventions applied (snake_case keys, string ids). The script reads the few fields an audit needs from each and flattens them into one row type: {network, id, advertiser, text, url}.

Python
# ad_audit.py
# Pulls the ads a brand is running on three networks in parallel.
# Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python ad_audit.py
import os
from collections import Counter
from concurrent.futures import ThreadPoolExecutor

import requests

KEY = os.environ["INSIGHTSOCIAL_API_KEY"]
BASE = "https://api.insightsocial.app/v1"
BRAND = "Duolingo"

def get(path, **params):
    res = requests.get(f"{BASE}/{path}", params=params,
                       headers={"x-api-key": KEY}, timeout=120)
    return res.json()

LEGS = {
    "meta":     ("facebook/adlibrary/search/ads", {"query": BRAND}),
    "linkedin": ("linkedin/ads/search",           {"company": BRAND}),
    "tiktok":   ("tiktok/adlibrary/search",       {"advertiser_name": BRAND}),
}

with ThreadPoolExecutor(max_workers=3) as pool:
    futures = {net: pool.submit(get, path, **params) for net, (path, params) in LEGS.items()}
    results = {net: f.result() for net, f in futures.items()}

def flatten(network, item):
    if network == "meta":  # beta: Meta Ad Library records
        snap = item.get("snapshot") or {}
        return {"id": item.get("ad_archive_id"), "advertiser": item.get("page_name"),
                "text": (snap.get("body") or {}).get("text"), "url": item.get("url")}
    if network == "linkedin":  # beta: LinkedIn Ad Library records
        return {"id": item.get("id"), "advertiser": item.get("advertiser"),
                "text": item.get("description"), "url": item.get("url")}
    post = item["post"]  # TikTok: the standard post shape
    return {"id": post["id"], "advertiser": post["author"]["display_name"],
            "text": post["content"]["text"], "url": post["url"]}

ads, spent = [], 0
for network, body in results.items():
    spent += body.get("credits_used", 0)
    if not body["success"]:
        print(f"{network}: {body['error']['type']} (not charged)")
        continue
    for item in body["data"].get("items", []):
        ads.append({"network": network, **flatten(network, item)})

print(f'{len(ads)} ads found for "{BRAND}"')
print(dict(Counter(row["network"] for row in ads)))
for row in ads[:5]:
    print(f'[{row["network"]}] {row["advertiser"]}: {(row["text"] or "")[:80]}')
print(f"credits used: {spent}")

What you get back

Example output
66 ads found for "Duolingo"
{'meta': 30, 'linkedin': 24, 'tiktok': 12}
[meta] Duolingo: Learn a language for free with fun lessons designed to keep you motivated!
[meta] Duolingo: ...
...
credits used: 150

The figures are illustrative. The flattened row keeps only what the three networks share. The full records carry more: Meta ads the creative, images, running status (is_active) and the platforms they run on; LinkedIn ads the targeting, run dates and impression range; TikTok ads the creative, advertiser and first-shown date. On LinkedIn, advertiser is who paid for the ad; poster can be a person, when the brand promotes an employee's or a creator's post. Beta endpoints keep each network's structure and may change shape, so read their fields defensively, as the code does with .get().

What to change

  • Widen the Meta leg. GET /v1/facebook/adlibrary/search/companies (50) resolves a brand name to its advertiser pages and their pageId. GET /v1/facebook/adlibrary/company/ads (50–350) then returns one page's full run, which is more precise than a keyword search. GET /v1/facebook/adlibrary/ad/transcript (100) returns what a video ad says out loud.
  • Read one ad in full. GET /v1/facebook/adlibrary/ad, GET /v1/linkedin/ad and GET /v1/tiktok/adlibrary/ad are 50 credits each.
  • Richer TikTok rows. include=ad on the TikTok search adds the brand name, landing page and advertiser profile link to every row, under post.ext.ad.
  • What actually performs. GET /v1/tiktok/ads/top (100–1000) returns TikTok's leaderboard of top ads for a market and time window, with click-through rate and rank. Filter it with keyword to see a category's best creative.
  • Scope by market and date. Add country and start_date / end_date on Meta, and countries and startDate / endDate on LinkedIn.
  • Page deeper. All three return pagination.next_cursor. Send it back as cursor, with the same parameters, to get the next page. See Pagination.

On this page