# Ad library aggregation (/docs/recipes/ads-library-aggregation)



Ad library aggregation [#ad-library-aggregation]

Build a competitive-intelligence dashboard that shows every ad a target company is running across Meta, Google, LinkedIn, and Reddit — for sales-call prep, market-research reports, or buying-team intake.

How do you find all the ads a company is running? [#how-do-you-find-all-the-ads-a-company-is-running]

Query the four public ad libraries — Meta, Google Ad Transparency, LinkedIn, and Reddit — in parallel through one API key. Each network has a search endpoint that takes the brand name (or domain, for Google), and the four 5-credit calls complete in one `Promise.all`, returning every active and recent ad for 20 credits total.

The problem [#the-problem]

Every major ad network publishes a transparency library, but each lives behind a different UI, a different query model, and a different response shape. Manually checking four libraries per competitor doesn't scale past your first sales call, and none of the official UIs export data.

The solution [#the-solution]

Four ad-library endpoints, all advanced tier (5 credits each):

* `GET /v1/facebook/adlibrary/search/ads` — Meta Ad Library keyword search (param: `query`)
* `GET /v1/google/company/ads` — Google Ad Transparency Library by advertiser (param: `domain` or `advertiser_id`)
* `GET /v1/linkedin/ads/search` — LinkedIn Ad Library keyword search (param: `query`)
* `GET /v1/reddit/ads/search` — Reddit Ad Library keyword search (param: `query`)

The four ad-library responses have different upstream shapes; this recipe flattens them into a unified `{ network, ad }` row so you can render them in one table.

```typescript
// recipe-ad-audit.ts
// Pulls every ad a brand is running across 4 networks in parallel.
// Run with: SOCIALCRAWL_KEY=sc_... npx tsx recipe-ad-audit.ts

const KEY = process.env.SOCIALCRAWL_KEY;
if (!KEY) throw new Error("Set SOCIALCRAWL_KEY");

const BASE = "https://www.socialcrawl.dev/v1";
const brand = "stripe";

async function get(path: string, params: Record<string, string>) {
  const url = new URL(`${BASE}/${path}`);
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
  const res = await fetch(url, { headers: { "x-api-key": KEY! } });
  if (!res.ok) return { success: false, error: await res.text() };
  return (await res.json()) as {
    success: boolean;
    data?: { items?: unknown[] };
    credits_remaining: number;
  };
}

type AdRow = { network: string; ad: unknown };

// Google ad-library searches by domain or advertiser_id — not free-text.
// Brand → domain is the most common shape; map your input accordingly.
const brandDomain = "stripe.com";

const [meta, google, linkedin, reddit] = await Promise.all([
  get("facebook/adlibrary/search/ads", { query: brand }),
  get("google/company/ads", { domain: brandDomain }),
  get("linkedin/ads/search", { query: brand }),
  get("reddit/ads/search", { query: brand }),
]);

const ads: AdRow[] = [
  ...(meta.data?.items ?? []).map((ad) => ({ network: "meta", ad })),
  ...(google.data?.items ?? []).map((ad) => ({ network: "google", ad })),
  ...(linkedin.data?.items ?? []).map((ad) => ({ network: "linkedin", ad })),
  ...(reddit.data?.items ?? []).map((ad) => ({ network: "reddit", ad })),
];

console.log(`${ads.length} ads found for "${brand}"`);
const counts = ads.reduce<Record<string, number>>(
  (acc, row) => ({ ...acc, [row.network]: (acc[row.network] ?? 0) + 1 }),
  {},
);
console.table(counts);

console.log(`credits left: ${reddit.credits_remaining}`);
```

What you get back [#what-you-get-back]

```jsonc
// Final aggregated shape after the flattening loop:
[
  {
    "network": "meta",
    "ad": {
      "id": "ad_1234567890",
      "page_name": "Stripe",
      "ad_creative_body": "Accept payments online in minutes...",
      "first_active": "2026-05-01", // <-- normalised from raw upstream
      "active": true,
    },
  },
  {
    "network": "linkedin",
    "ad": {
      "id": "ln_987654321",
      "advertiser_name": "Stripe",
      "headline": "Built for fast-growing teams",
      "creative_url": "https://www.linkedin.com/ads/...",
    },
  },
  // ... google + reddit rows mixed in
]
```

Credits cost [#credits-cost]

> **Cost per run:** 20 credits per brand audit (4 networks × 5 credits). Auditing a 25-company competitive set costs 500 credits.

Take it further [#take-it-further]

* See [Endpoint pricing](/docs/endpoint-pricing) for why ad-library endpoints are advanced tier (they hit slower upstream APIs that return larger payloads).
* Swap `brand = "stripe"` for any company name — the Meta, LinkedIn, and Reddit endpoints all accept free-text queries.
* Next: [Creator engagement scoring](/docs/recipes/creator-engagement-scoring) shows the same parallel-fan-out pattern applied to profile data.
* Tracking the competitor's organic side too? See [Competitor tracking](/docs/recipes/competitor-tracking). Platform references: [Facebook API](/platforms/facebook), [LinkedIn API](/platforms/linkedin).
