URL: /code/data/domains/find-competitors · Id: `page:/code/data/domains/find-competitors`

# Domain.findCompetitors

> Find the domains that rank or buy ads for most of your terms, with how many of their terms you have no page for.

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

Find the domains that rank or buy ads for most of your terms, with how many of their terms you have no page for.

- **Price:** 5¢ per 100 rows
- **Success rate:** 100% of 2,639 calls
- **Freshness:** Fresh for 30 days
- **Emits:** `Domain.competitorsFound`
- **Capability:** [Market data](https://startupbuilder.dev/systems/marketing/market-data)

```text title="Run it · Ask"
Who is leaseline.app up against in search? Spend up to 1¢.
```

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

const result = await $.try.Domain.findCompetitors({ hostname: 'leaseline.app', maxCost: 0.01 })
```

```json title="Run it · Response"
{
  "competitors": [
    {
      "hostname": "rentrival.example",
      "sharedKeywords": 31,
      "theirKeywords": 2210,
      "missingForYou": 2179,
      "visibility": 0.41,
      "method": "sampled",
      "locale": "en-US",
      "observedAt": "2026-10-27T14:02:11Z"
    },
    {
      "hostname": "ledgerly.example",
      "sharedKeywords": 22,
      "theirKeywords": 840,
      "missingForYou": 818,
      "visibility": 0.18,
      "method": "sampled",
      "locale": "en-US",
      "observedAt": "2026-10-27T14:02:11Z"
    }
  ],
  "basis": "targets",
  "cost": {
    "usd": 0,
    "maxUsd": 0.005,
    "units": 10,
    "misses": 0
  }
}
```

## Options and prices
| You get | Input that selects it | Price | Success rate |
| --- | --- | --- | --- |
| Who ranks for your terms | `{"kind":"organic"}` | About 5¢ per 100 rows | 100% of 2,639 calls |
| Who buys ads on your terms | `{"kind":"paid"}` | About 2¢ per 100 rows | 91.9% of 5,547 calls |

## Inputs
| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `hostname` | [`Hostname`](/code/data/types#hostname) | Required | Your domain, or a rival’s to see who it competes with. |
| `kind` | `'organic' \| 'paid'` | `'organic'` | Rivals in the results, or rivals in the ads. |
| `locale` | `string` | `your home Market` | The market and language, such as `en-US`. |
| `limit` | `number` | `10` | Rivals to return, at most 100, best first. |
| `maxCost` | `number` | `0.10` | 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 | Unit | What `null` means |
| --- | --- | --- | --- |
| `competitors` | `Domain[]` | None. | Never null: empty when no domain shares a term, at $0. |
| `competitors[].hostname` | [`Hostname`](/code/data/types#hostname) | None. | Never null. |
| `competitors[].sharedKeywords` | `number` | terms | Never null: terms you both rank for, or both target. |
| `competitors[].theirKeywords` | `number \| null` | terms in the top 100 | Unknown. |
| `competitors[].missingForYou` | `number \| null` | terms | Unknown: terms they rank for and you do not. |
| `competitors[].visibility` | `number \| null` | 0 to 1, share of clicks on the shared terms | Unknown. |
| `competitors[]` | [`Observed`](/code/data/types#observed) | None. | Each row also carries `observedAt`, `method` and `locale`. |
| `basis` | `'ranked' \| 'targets'` | None. | Never null. `targets` means your domain ranks for too few terms, so rivals were read from the results of your target Keywords instead. |
| `cost` | [`Cost`](/code/data/types#cost) | 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 |
| --- | --- | --- | --- |
| `basis` | `targets` | A new domain ranks for too few terms to have organic rivals; these are the domains on your target terms’ results pages. | Treat them as the rivals for now; run it again once you rank for 50 terms. (the new-domain rule) |
| `missingForYou` | In the hundreds | Most of a rival’s terms are ones you have no page for. | Pass the rival to `Keyword.find` with `exclude` set to your domain, and keep the terms under your ceiling. (the content-gap rule in the search playbook) |
| `visibility` | Over 0.3 on your terms | This rival takes about a third of the clicks on the terms you share. | Read their ads with `Ad.find` and their links with `Domain.findLinks` first. (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**

Records each rival as an observed relationship, never as a Domain you hold. Lists are shared across startups for 30 days.

**Emits**

`Domain.competitorsFound` with `{ competitors, hostname, kind, basis }`

**Performed by**

[Analyst](https://startupbuilder.dev/agents/marketing/analyst)

**Price**

About 5¢ per 100 rows. Free: a list younger than 30 days. `maxCost` defaults to 10¢. The top 10 rivals, organic and paid: 20 × 0.05¢ a row is about 1¢.

**Reliability**

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

**Freshness**

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

**Batch and pages**

Up to 100 rivals a call. 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
This call has no refusals of its own. 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), [`venue-unsupported`](/code/refusals#venue-unsupported).

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

## Example

**Ask**

```text
Who is leaseline.app up against in search? Spend up to 1¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/domains/find-competitors?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"leaseline.app","maxCost":0.01}'
```

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

**CLI**

```bash
npx startup-builder do Domain.findCompetitors --try --data '{"hostname":"leaseline.app","maxCost":0.01}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Domain.findCompetitors({ hostname: 'leaseline.app', maxCost: 0.01 })"
  }
}
```

**SDK**

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

const result = await $.try.Domain.findCompetitors({
  hostname: 'leaseline.app',
  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. |

## Used by
- **Workflows:** [Competitor gap](/capabilities/size-up-competitors/competitor-gap)
- **Jobs:** [Size up competitors](/capabilities/size-up-competitors)

## Related
[`Domain.benchmark`](/code/data/domains/benchmark) · [`Domain.findLinks`](/code/data/domains/find-links) · [`Domain.verify`](/code/data/domains/verify)