URL: /code/data/ads/find · Id: `page:/code/data/ads/find`

# Ad.find

> Read a rival’s ads, or every ad on your category’s words, on Facebook, Instagram, Google, LinkedIn and TikTok, with how long each ran.

`POST https://api.sb/{your-domain}/ads/find`

Read a rival’s ads, or every ad on your category’s words, on Facebook, Instagram, Google, LinkedIn and TikTok, with how long each ran.

- **Price:** 0.2¢ a search
- **Success rate:** 99.9% of 309,000 calls
- **Freshness:** Fresh for 7 days
- **Emits:** `Ad.found`
- **Capability:** [Market data](https://startupbuilder.dev/systems/marketing/market-data)

```text title="Run it · Ask"
Which ads is rentrival.example running on Facebook and LinkedIn, and which has run longest? Spend up to 1¢.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Ad.find({ advertiser: 'rentrival.example', venues: ['facebook', 'linkedin'], maxCost: 0.01 })
```

```json title="Run it · Response"
{
  "ads": [
    {
      "advertiser": {
        "name": "RentRival",
        "hostname": "rentrival.example"
      },
      "venue": "facebook",
      "format": "image",
      "headline": "Stop chasing rent in spreadsheets",
      "body": "Owners get statements on the 1st. Free for 14 days.",
      "cta": "Sign up",
      "landingUrl": "https://rentrival.example/pm",
      "mediaUrl": null,
      "firstSeen": "2026-06-02",
      "lastSeen": "2026-10-26",
      "daysRunning": 146,
      "transparencyUrl": null,
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    },
    {
      "advertiser": {
        "name": "RentRival",
        "hostname": "rentrival.example"
      },
      "venue": "linkedin",
      "format": "text",
      "headline": "Renewal notices, sent on time, every time",
      "body": "For property managers with 50 to 500 doors.",
      "cta": "Book a demo",
      "landingUrl": "https://rentrival.example/renewals",
      "mediaUrl": null,
      "firstSeen": "2026-10-08",
      "lastSeen": "2026-10-26",
      "daysRunning": 18,
      "transparencyUrl": null,
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    }
  ],
  "total": 37,
  "after": "ad_k2Lw",
  "cost": {
    "usd": 0,
    "maxUsd": 0.0036,
    "units": 2,
    "misses": 0
  }
}
```

## Options and prices
| You get | Input that selects it | Price | Success rate |
| --- | --- | --- | --- |
| A rival’s ads on Facebook and Instagram | `{"advertiser":"rentrival.example","venues":["facebook","instagram"]}` | About 0.2¢ a search | 99.1% of 277,000 calls |
| Every ad on a phrase | `{"keyword":"rent tracking"}` | About 0.2¢ a search | 99.9% of 309,000 calls |
| A rival’s Google ads | `{"advertiser":"rentrival.example","venues":["google-search"]}` | About 0.2¢ a search | 99.95% of 173,000 calls |
| A rival’s LinkedIn ads | `{"advertiser":"rentrival.example","venues":["linkedin"]}` | About 0.16¢ a search | 100% of 15,900 calls |
| TikTok ads on a phrase | `{"keyword":"rent tracking","venues":["tiktok"]}` | About 0.2¢ a search | 99.6% of 7,200 calls |
| How many ads, with no rows | `{"advertiser":"rentrival.example","countOnly":true}` | About 0.5¢ a search | 99.8% of 994 calls |
| A rival’s past search ad copy | `{"advertiser":"rentrival.example","activeOnly":false,"venues":["google-search"]}` | About 0.3¢ a search | 100% of 2,000 calls |

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `advertiser` | `Hostname \| string` | None. | A rival’s domain or page name. Pass this or `keyword`. |
| `keyword` | `string` | None. | Every ad on this exact phrase. Facebook, Instagram and TikTok only. |
| `venues` | `('facebook' \| 'instagram' \| 'google-search' \| 'google-display' \| 'youtube' \| 'linkedin' \| 'tiktok')[]` | `every Venue the input supports` | Where to look. Each site is its own search, priced each. |
| `countries` | `string[]` | `your home Market` | ISO 3166-1 codes where the ads ran. |
| `activeOnly` | `boolean` | `true` | Only ads running now. `false` adds ads that stopped, with their last-seen date. |
| `countOnly` | `boolean` | `false` | Return `total` and no rows. |
| `limit` | `number` | `50` | Ads a page, at most 100. |
| `after` | [`Cursor`](/code/data/types#cursor) | None. | The cursor from the last page. |
| `maxAge` | `string` | `'7d'` | A search younger than this returns at $0. |
| `maxCost` | `number` | `0.05` | The most this call may spend, in USD. Over it, the call is refused before it spends, or stops early with what it has. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `ads` | `Ad[]` | Never null: empty when none ran, at $0; absent with `countOnly`. |
| `ads[].advertiser` | `{ name: string; hostname: Hostname \| null }` | Never null; `hostname: null` when the page names no site. |
| `ads[].venue` | [`VenueId`](/code/data/types#venueid) | Never null. |
| `ads[].format` | `'text' \| 'image' \| 'video' \| 'carousel'` | Never null. |
| `ads[].headline` | `string \| null` | The format has no headline. |
| `ads[].body` | `string \| null` | The format has no body copy. |
| `ads[].cta` | `string \| null` | No button. |
| `ads[].landingUrl` | `string \| null` | The library does not show it. |
| `ads[].mediaUrl` | `string \| null` | A text ad, or the library does not serve the file. |
| `ads[].firstSeen` | `string \| null`, in date | The library does not show a start date. |
| `ads[].lastSeen` | `string \| null`, in date | The library does not show it. |
| `ads[].daysRunning` | `number \| null`, in days | No start date, so unknown; never zero. |
| `ads[].transparencyUrl` | `string \| null` | The site’s library has no page for it. |
| `ads[]` | [`Observed`](/code/data/types#observed) | Each ad also carries `observedAt` and `method`. |
| `total` | `number \| null`, in ads | The library reports no total. |
| `after` | [`Cursor`](/code/data/types#cursor) | The last page: there are no more rows. |
| `cost` | [`Cost`](/code/data/types#cost), in USD | Never null. In try mode `usd` is 0 and `maxUsd` is what the call would have cost. |

## Read the result
| Field | Value | What it means | What to do |
| --- | --- | --- | --- |
| `daysRunning` | The longest-running ad | An advertiser keeps paying only for an ad that works, so the oldest live ad is their proven message. | Read its promise and its offer; test the same promise in your own words, never their copy. (the ad-library rule) |
| `daysRunning` | Under 14 on most ads | The advertiser is still testing; nothing has proven itself yet. | Read again in 4 weeks and keep the ones still running. (our planning number) |
| `total` | 0 on your category’s words | Nobody pays to reach these buyers there: either the money is not there or the field is open. | Price it first: forecast the terms with `Keyword.forecast` before you spend. (the paid playbook) |
| `landingUrl` | A page per audience | The rival splits buyers by size or role, and each page speaks to one. | Audit those pages with `Page.audit` and compare their offers with yours. (our planning number) |

## Effects
**Action class**

None: it reads, so no approval rule names it

**Modes**

- `try`: Runs the read and returns real rows; nothing is saved and nothing is billed.
- `send`: Returns a pending Action at once; its Event carries the rows when the read finishes.
- `do`: Waits for the read and returns the rows, billed per result.

**Idempotency**

The same `Idempotency-Key` with the same input replays the first answer at $0; with other input it returns 409.

**Saved or shared**

Nothing is written to your records: outside ads are read, never saved as your Ads. Searches are shared across startups for 7 days.

**Emits**

`Ad.found` with `{ advertiser, keyword, venues, ads, total }`

**Performed by**

[Paid Media Manager](https://startupbuilder.dev/agents/marketing/paid-media-manager)

**Price**

About 0.2¢ a search. Free: a search younger than `maxAge`. `maxCost` defaults to 5¢. 3 rivals on 5 sites: 15 × 0.2¢ a search is about 3¢.

**Reliability**

The first route succeeds 99.9% of 309,000 calls. If it fails, the call refuses with `sources-busy` and nothing is spent.

**Freshness**

A reading younger than 7 days returns at $0. Readings are shared across startups for the same subject and locale.

**Batch and pages**

Up to 100 ads a page. Pages with an `after` cursor, 25 rows a page by default and 100 at most. It answers in one response.

**Rate**

[The shared rate rules](/code/data#rate): 10 calls a second per key, and an array input counts as one call.

## Refuses when
| Check | Error | Fix | Then call |
| --- | --- | --- | --- |
| A search engine, answer engine or network the call does not read, such as ChatGPT sent to `Keyword.measure` | [`venue-unsupported`](/code/refusals#venue-unsupported) | Pick one the call’s `venue` input lists; AI answers are `Keyword.ask`, posts are `Keyword.search`. | [`Keyword.ask`](/code/data/keywords/ask) |

Every data call can also meet [the shared data-call refusals](/code/refusals#data-calls): [`filter-unsupported`](/code/refusals#filter-unsupported), [`input-missing`](/code/refusals#input-missing), [`limit-exceeded`](/code/refusals#limit-exceeded), [`max-cost`](/code/refusals#max-cost), [`rate-limited`](/code/refusals#rate-limited), [`sources-busy`](/code/refusals#sources-busy).

```json
{
  "type": "https://startupbuilder.dev/errors/venue-unsupported",
  "title": "The call does not read that search engine or network",
  "status": 422,
  "detail": "A search engine, answer engine or network the call does not read, such as ChatGPT sent to `Keyword.measure`.",
  "fix": "Pick one the call’s `venue` input lists; AI answers are `Keyword.ask`, posts are `Keyword.search`.",
  "next": {
    "verb": "Keyword.ask",
    "input": {
      "keyword": "best rent tracking software for a 40-unit property manager",
      "venues": [
        "chatgpt"
      ]
    }
  },
  "instance": "https://api.sb/leaseline.app/actions/action_1"
}
```

## When it waits
Returns in one response; nothing waits.

## Example

**Ask**

```text
Which ads is rentrival.example running on Facebook and LinkedIn, and
which has run longest? Spend up to 1¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/ads/find?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"advertiser":"rentrival.example","venues":["facebook","linkedin"],"maxCost":0.01}'
```

The response is the one in the Run it window above.

**CLI**

```bash
npx startup-builder do Ad.find --try --data '{"advertiser":"rentrival.example","venues":["facebook","linkedin"],"maxCost":0.01}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Ad.find({ advertiser: 'rentrival.example', venues: ['facebook', 'linkedin'], maxCost: 0.01 })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.try.Ad.find({
  advertiser: 'rentrival.example',
  venues: ['facebook', 'linkedin'],
  maxCost: 0.01,
})
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

A `sk_test_` key answers these the same way every time, at $0 ([every test value](/code/data#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `maxCost: 0.0001` | Refuses with [`max-cost`](/code/refusals#max-cost) | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
| `rate-limited.test` | Refuses with [`rate-limited`](/code/refusals#rate-limited) | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
| `sources-busy.test` | Refuses with [`sources-busy`](/code/refusals#sources-busy) | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |

Watch it on a schedule: a Monitor runs this call and records an Event only when the reading changes.

```ts
await $.Monitor.create({
  verb: 'Ad.find',
  input: { advertiser: 'rentrival.example' },
  change: 'new-ad',
  every: '1w',
})
```

## Used by
- **Workflows:** [Competitor gap](/capabilities/size-up-competitors/competitor-gap) · [Pick who to sell to first](/capabilities/find-customers/pick-who-to-sell-to)
- **Jobs:** [Run ads](/capabilities/run-ads) · [Size up competitors](/capabilities/size-up-competitors)

## Related
None.