Skip to content

Handle.measure

Read any public account’s followers, engagement rate and posting cadence from its last 30 posts, before you sponsor it, pitch it or copy it.

Price
19¢ per 100 accounts
Success rate
99.9% of 413,075 calls
Freshness
Fresh for 7 days
Emits
Handle.measured
POST https://api.sb/{your-domain}/handles/measure

Inputs

NameTypeMeaningPrice
handlesstring[]Up to 25 accounts on one Venue, as @handle or a profile link. Required.Included
venue'instagram' | 'tiktok' | 'youtube' | 'x' | 'linkedin' | 'reddit' | 'facebook'Where the accounts are; required when you send bare handles. Default read from a link.Included
venue: 'instagram'OptionAn account on InstagramAbout 19¢ per 100 accounts; works 99.9% of 413,075 calls
venue: 'tiktok'OptionAn account on TikTokAbout 9¢ per 100 accounts; works 100% of 76,265 calls
venue: 'youtube'OptionAn account on YouTubeAbout 19¢ per 100 accounts; works 100% of 49,954 calls
venue: 'x'OptionAn account on XAbout 7¢ per 100 accounts; works 99.5% of 270,000 calls
venue: 'linkedin'OptionAn account on LinkedInAbout 34¢ per 100 accounts; works 99.6% of 35,521 calls
venue: 'reddit'OptionAn account on RedditAbout 10¢ per 100 accounts; works 98.1% of 1,712 calls
venue: 'facebook'OptionAn account on FacebookAbout 19¢ per 100 accounts; works 94.4% of 76,337 calls
postsnumberRecent posts to average over, at most 100. Under 10, engagementRate is null. Default 30.Included
maxAgestringA stored reading younger than this returns at $0 instead of reading again. Default '7d'.Included
maxCostnumberThe 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.10.Included

Outputs

FieldTypeWhat null means
measuresMeasure[]Never null: one row for each account read; a private or gone account is left out, free. What it means lastPostAt over 30 days ago: The account has gone quiet; its follower count is history. Do not pay for a spot; pick an account that posted this week. Planning estimate.
Under half the median of the accounts you compared: The audience is bought or tired. Drop it from a sponsorship shortlist. Planning estimate.
Under 5% of followers on a video Venue: The Venue stopped showing its posts widely. Price a sponsorship from median views, never from followers. Planning estimate.
measures[].handlestringNever null: the account as the Venue names it, @smalllandlordmath.
measures[].followersnumber | null, in followers or subscribersThe Venue counts none: Reddit.
measures[].followingnumber | nullThe Venue shows none: YouTube.
measures[].postsReadnumber, in postsNever null: the posts the averages are over.
measures[].engagementRatenumber | null, in 0 to 1: reactions and comments a post over followers, averagedFewer than 10 posts read, or no follower count.
measures[].medianViewsnumber | null, in views a postThe Venue shows no views: LinkedIn, Reddit.
measures[].cadence{ postsPerWeek: number; lastPostAt: string | null }Never null. lastPostAt is null for an account that never posted.
measures[].topPosts{ url: string; engagement: object }[]Never null: the three posts with the most engagement among those read.
measures[]ObservedEach row also carries observedAt, method and venue.
costCost, in USDNever null. In try mode usd is 0 and maxUsd is what the call would have cost.

On each Venue

VenueWhat comes backLimitsPrice
InstagramFollowers, following, engagement rate and cadence; medianViews from reels.A private account is skipped, free.About 19¢ per 100 accounts
TikTokFollowers, following, engagement rate, cadence and medianViews.None.About 9¢ per 100 accounts
YouTubeSubscribers as followers, engagement rate, cadence and medianViews; following is null.None.About 19¢ per 100 accounts
XFollowers, following, engagement rate, cadence and medianViews where X shows views.Replies are left out of cadence; reposts count.About 7¢ per 100 accounts
LinkedInFollowers, engagement rate and cadence of a member or company page; no views.The dearest Venue to measure, at about 0.34¢ an account.About 34¢ per 100 accounts
RedditCadence and engagement of an account’s posts and comments; followers is null.Engagement is score per post, never a share of followers.About 10¢ per 100 accounts
FacebookA page’s followers and cadence; engagement rate from public posts.Pages only, never personal profiles.About 19¢ per 100 accounts
Hacker NewsNothing: no route reads Hacker News accounts yet.Refuses with venue-unsupported.None.

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. A reading is shared across startups for the same account for 7 days; it reads public accounts only.

Emits

Handle.measured with { venue, measures }

Performed by

Partner Manager

Price

About 19¢ per 100 accounts. At most 34¢ per 100 accounts. Free: an account that is gone or private, a reading younger than maxAge. maxCost defaults to 10¢. Ten shortlisted accounts, measured before you pick two: 10 × 0.19¢ an account is about 1.9¢.

Reliability

The first route succeeds 99.9% of 413,075 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 25 accounts a call. It answers in one response.

Rate

The shared rate rules: 10 calls a second per key, and an array input counts as one call.

Capability

Market data

Refuses when

CheckErrorFixThen call
A search engine, answer engine or network the call does not read, such as ChatGPT sent to Keyword.measurevenue-unsupportedPick one the call’s venue input lists; AI answers are Keyword.ask, posts are Keyword.search.Keyword.ask

Every data call can also meet the shared data-call refusals: filter-unsupported, input-missing, limit-exceeded, max-cost, rate-limited, sources-busy.

The venue-unsupported payload
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 batch: three accounts in one call, one price cap, one cost. The private account is left out and costs nothing.

You, to your agent
Compare @smalllandlordmath, @rentrollreview and @closedaccount on
YouTube; spend up to 2¢.
Response, in try mode
{
  "measures": [
    {
      "venue": "youtube",
      "handle": "@smalllandlordmath",
      "followers": 41200,
      "following": null,
      "postsRead": 30,
      "engagementRate": 0.046,
      "medianViews": 6800,
      "cadence": {
        "postsPerWeek": 1.5,
        "lastPostAt": "2026-10-23T15:00:00Z"
      },
      "topPosts": [
        {
          "url": "https://www.youtube.com/watch?v=rentroll40",
          "engagement": {
            "views": 61000,
            "reactions": 2400,
            "comments": 310
          }
        }
      ],
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    },
    {
      "venue": "youtube",
      "handle": "@rentrollreview",
      "followers": 128000,
      "following": null,
      "postsRead": 30,
      "engagementRate": 0.009,
      "medianViews": 3100,
      "cadence": {
        "postsPerWeek": 0.5,
        "lastPostAt": "2026-09-02T18:00:00Z"
      },
      "topPosts": [
        {
          "url": "https://www.youtube.com/watch?v=rentroll2019",
          "engagement": {
            "views": 22000,
            "reactions": 410,
            "comments": 52
          }
        }
      ],
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    }
  ],
  "cost": {
    "usd": 0,
    "maxUsd": 0.0057,
    "units": 2,
    "misses": 1
  }
}

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

Test values, and watching it

A sk_test_ key answers these the same way every time, at $0 (every test value).

Test valueAlways returnsWhat it is for
@smalllandlordmath{"followers":41200,"engagementRate":0.046,"postsPerWeek":1.5}Measures the same channel every time: 41,200 subscribers, 4.6% engagement, a video and a half a week.
maxCost: 0.0001Refuses with max-costAny priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap.
rate-limited.testRefuses with rate-limitedAny call naming this hostname answers 429 with Retry-After, so your retry logic runs.
sources-busy.testRefuses with sources-busyAny 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.

TypeScript
await $.Monitor.create({
  verb: 'Handle.measure',
  input: { handles: ['@smalllandlordmath'], venue: 'youtube' },
  change: 'engagement-drop',
  threshold: 0.2,
  every: '1w',
})

Used by

None.