URL: /code/data/social/find-engagers · Id: `page:/code/data/social/find-engagers`

# Post.findEngagers

> List who reacted to or commented on one post, with what they said and where they work, priced per post, not per person.

POST https://api.sb/&#123;your-domain&#125;/posts/find-engagers

List who reacted to or commented on one post, with what they said and where they work, priced per post, not per person.

- **Price:** 0.3¢ a post
- **Success rate:** 99.95% of 44,205 calls
- **Freshness:** Fresh for 6 hours
- **Emits:** `Post.engagersFound`

```text title="Run it · Ask"
Who commented on RentRival’s LinkedIn post about rent-roll chaos? Spend up to 1¢.
```

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

const result = await $.try.Post.findEngagers({ post: 'https://www.linkedin.com/posts/rentrival_rent-roll-chaos-7134', as: 'comments', maxCost: 0.01 })
```

```json title="Run it · Response"
{
  "post": {
    "url": "https://www.linkedin.com/posts/rentrival_rent-roll-chaos-7134",
    "venue": "linkedin",
    "engagement": {
      "reactions": 212,
      "comments": 46
    }
  },
  "engagers": [
    {
      "handle": "maya-chen-cedar-hollow",
      "name": "Maya Chen",
      "headline": "Owner, Cedar Hollow Rentals",
      "profileUrl": "https://www.linkedin.com/in/maya-chen-cedar-hollow",
      "as": "comment",
      "reaction": null,
      "text": "We still reconcile rent by hand every month.",
      "at": "2026-10-24T13:10:00Z",
      "organization": {
        "name": "Cedar Hollow Rentals",
        "hostname": "cedar-hollow-rentals.example"
      },
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled",
      "venue": "linkedin"
    }
  ],
  "after": "eng_c_46",
  "cost": {
    "usd": 0,
    "maxUsd": 0.003,
    "units": 1,
    "misses": 0
  }
}
```

## Inputs
| Name | Type | Meaning | Price |
| --- | --- | --- | --- |
| `post` | `string` | The post’s link, as `Post.find` returns it or as you copy it from the Venue. Required. | Included |
| `venue` | `'linkedin' \| 'reddit' \| 'x' \| 'youtube' \| 'tiktok' \| 'instagram' \| 'facebook'` | Where the post lives. Read from the link; send it only to override. Default `read from `post``. | Included |
| `venue: 'linkedin'` | Option | Engagers on LinkedIn | About 0.3¢ a post; works 99.95% of 44,205 calls |
| `venue: 'reddit'` | Option | Engagers on Reddit | About 0.19¢ a post; works 99.7% of 115,835 calls |
| `venue: 'x'` | Option | Engagers on X | About 0.05¢ a post; works 99.98% of 52,058 calls |
| `venue: 'youtube'` | Option | Engagers on YouTube | About 0.09¢ a post; works 100% of 12,899 calls |
| `venue: 'tiktok'` | Option | Engagers on TikTok | About 0.1¢ a post; works 99.98% of 25,958 calls |
| `venue: 'instagram'` | Option | Engagers on Instagram | About 0.1¢ a post; works 94.4% of 10,480 calls |
| `venue: 'facebook'` | Option | Engagers on Facebook | About 0.19¢ a post; works 99.96% of 16,093 calls |
| `as` | `'both' \| 'comments' \| 'reactions'` | Which engagers to read. Where a Venue shows only one kind, the other comes back empty, never refused. Default `'both'`. | Included |
| `after` | [`Cursor`](/code/data/types#cursor) | The cursor from the last page. | Included |
| `save` | `number` | Save the first n engagers as Prospects, each with the post as where you found them; free, and it follows the call’s Mode. Default `0`. | Included |
| `maxCost` | `number` | The most this call may spend, in USD. Over it, the call is refused before it spends, or stops early with what it has. Default `0.05`. | Included |

## Outputs
| Field | Type | What `null` means | What it means |
| --- | --- | --- | --- |
| `post` | `{ url: string; venue: VenueId; engagement: { reactions?: number; comments?: number } }` | Never null: the post read, with its totals. | None. |
| `engagers` | `Engager[]` | Never null: empty when no one engaged yet. | `comment` that names the problem: The warmest row on the list: the person said it in public. Write to them first, quoting their own words back. *Planning estimate.*<br />Fewer than 1 in 5 rows match your buyer’s titles: The post’s audience is the author’s peers, not your buyers. Score the rows with `ICP.score` before any send, and pick posts aimed at buyers next time. *Source: reviews of the LinkedIn engagement read, 2026.* |
| `engagers[].handle` | `string` | Never null: the account’s handle on the Venue. | None. |
| `engagers[].name` | `string \| null` | The Venue shows a handle only: Reddit, X and TikTok often do. | None. |
| `engagers[].headline` | `string \| null` | The Venue has no headline; LinkedIn is the one that does. | None. |
| `engagers[].profileUrl` | `string \| null` | The account is hidden from people who are signed out, as some X and Instagram accounts are. | None. |
| `engagers[].as` | `'reaction' \| 'comment'` | Never null. | None. |
| `engagers[].reaction` | `string \| null` | A comment, or a Venue with one kind of reaction. | None. |
| `engagers[].text` | `string \| null` | A reaction: only a comment has text. | None. |
| `engagers[].at` | `string \| null`, in ISO 8601 | The Venue shows no time for a reaction. | None. |
| `engagers[].organization` | `{ name: string; hostname: Hostname \| null } \| null` | No employer could be read from the profile. `Contact.enrich` fills it later. | None. |
| `engagers[]` | [`Observed`](/code/data/types#observed) | Each row also carries `observedAt`, `method` and `venue`. | None. |
| `after` | [`Cursor`](/code/data/types#cursor) | The last page: there are no more rows. | None. |
| `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. | None. |

### On each Venue
| Venue | What comes back | Limits | Price |
| --- | --- | --- | --- |
| LinkedIn | Comments and reactions in one read: name, headline and profile; `reaction` is the type (like, insightful, support). | The first 100 reactions a page; a post with more pages through `after`. | About 0.3¢ a post |
| Reddit | Commenters only, with their text; Reddit shows no voters. | Every comment in the thread, replies included, 100 a page. | About 0.19¢ a post |
| X | Repliers, with their text. Likers come back only for a post on your own connected X account. | Likers are billed by X on your own account; replies are not. | About 0.05¢ a post |
| YouTube | Commenters, with their text and likes. | Top-level comments; replies on request through `as: comments` and `after`. | About 0.09¢ a post |
| TikTok | Commenters, with their text. | Comments only; TikTok shows no likers. | About 0.1¢ a post |
| Instagram | Commenters, with their text. | Comments only, and the least reliable read here. | About 0.1¢ a post |
| Facebook | Commenters on a public post; reactions only on a post from your own page. | Public posts only. | About 0.19¢ a post |
| Hacker News | Nothing: no route reads Hacker News commenters yet. | Refuses with `venue-unsupported`; read the thread’s authors with `Post.find` and `kinds: ['comment']`. | None. |

## Effects
**Action class**

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

**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**

Rows are returned, not saved, unless you pass `save`. Readings about people are never shared across startups.

**Emits**

`Post.engagersFound` with `{ post, engagers }`

**Performed by**

[Prospector](https://startupbuilder.dev/agents/sales/prospector)

**Price**

About 0.3¢ a post. At most 0.8¢ a post. Free: a post that is gone or private, saving rows as Prospects. `maxCost` defaults to 5¢. The engagers on 20 posts about your problem: 20 × 0.3¢ a post is about 6¢.

**Reliability**

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

**Freshness**

A reading younger than 6 hours returns at $0. Readings about people are never shared across startups.

**Batch and pages**

Up to 100 comments and 100 reactions a page. Pages of 25 rows by default, 100 at most; pass `after` for the next page. 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.

**Capability**

[Enrichment](https://startupbuilder.dev/systems/sales/enrichment)

## 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
A refusal: Hacker News has no engager read yet, so the call refuses before it spends and `next` names the read that does.

**Ask**

```text
Who commented on this Hacker News thread about rent tracking?
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/posts/find-engagers?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "post": "https://news.ycombinator.com/item?id=41022817"
}'
```

**CLI**

```bash
npx startup-builder do Post.findEngagers --data '{
  "post": "https://news.ycombinator.com/item?id=41022817"
}'
```

The CLI runs in try mode unless you pass `--do`.

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Post.findEngagers({ post: 'https://news.ycombinator.com/item?id=41022817' })"
  }
}
```

**SDK**

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

const result = await $.try.Post.findEngagers({
  post: 'https://news.ycombinator.com/item?id=41022817',
})
```


```json title="Response, in try mode"
{
  "type": "https://startupbuilder.dev/errors/venue-unsupported",
  "title": "The call does not read that search engine or network",
  "status": 422,
  "detail": "Post.findEngagers does not read Hacker News.",
  "fix": "Read the thread’s comments with Post.find and kinds: ['comment'], then take their authors.",
  "next": {
    "verb": "Post.find",
    "input": {
      "term": "rent tracking landlord",
      "venue": "hacker-news",
      "kinds": [
        "comment"
      ]
    }
  },
  "instance": "https://api.sb/leaseline.app/actions/action_Hn4vQ8"
}
```

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/checks#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `https://www.linkedin.com/posts/test-engagers` | `{"engagers":5,"comments":2,"reactions":3}` | Returns the same five engagers, two comments and three reactions, so a harvest Workflow’s dedupe and save steps run. |
| `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: 'Post.findEngagers',
  input: { post: 'https://www.linkedin.com/posts/rentrival_rent-roll-chaos-7134' },
  change: 'new-engager',
  every: '6h',
})
```

## Used by
- **Workflows:** [Post-engager harvest](/capabilities/find-customers/post-engagers)
- **Jobs:** [Find your first customers](/capabilities/find-customers)

## Related
[`Post.find`](/code/data/social/find-posts) · [`Handle.find`](/code/data/social/find-handles) · [`Handle.measure`](/code/data/social/measure-handles)