Skip to content

Keyword.search

Run one term on Google, Bing, Reddit, X or LinkedIn and see the ranked results, the AI answer above them and your position.

Price
3¢ per 100 searches
Success rate
99.1% of 1,710,120 calls
Freshness
Fresh for 1 day
Emits
Keyword.searched
Capability
Market data
POST https://api.sb/{your-domain}/keywords/search

Options and prices

You getInput that selects itPriceSuccess rate
Google results and your rank{"venue":"google-search"}About 3¢ per 100 searches99.1% of 1,710,120 calls
Bing results{"venue":"bing"}About 3¢ per 100 searches90.7% of 5,169 calls
Local results around a place{"venue":"google-maps","place":"Tacoma, WA"}About 10¢ per 100 searches99.5% of 223,694 calls
News results{"venue":"google-news"}About 10¢ per 100 searches99.95% of 301,427 calls
Posts on Reddit{"venue":"reddit"}About 19¢ per 100 searches99.4% of 142,199 calls
Posts on X{"venue":"x"}About 8¢ per 100 searches85.6% of 162,439 calls
Posts on Hacker News{"venue":"hacker-news"}About 2¢ per 100 searches100% of 1,200 calls
Posts on LinkedIn{"venue":"linkedin"}About 20¢ per 100 searches99.97% of 154,260 calls
Posts on YouTube{"venue":"youtube"}About 9¢ per 100 searches100% of 9,400 calls
Posts on TikTok{"venue":"tiktok"}About 12¢ per 100 searches99.99% of 64,000 calls
Posts on Stack Overflow{"venue":"stack-overflow"}About 1.5¢ per 100 searches100% of 9 calls
Issues on one GitHub repository that use the term{"venue":"github","repo":"rentrival/ledger"}About 1.5¢ per 100 searches100% of 82 calls

Inputs

NameTypeDefaultMeaning
keywordstringRequiredA phrase or a saved Keyword id. Your brand name as the keyword, with your domain in exclude, finds mentions.
venue'google-search' | 'bing' | 'google-maps' | 'google-news' | 'reddit' | 'x' | 'linkedin' | 'hacker-news' | 'youtube' | 'tiktok' | 'stack-overflow' | 'github''google-search'Where to search, which also picks the output shape. Each site is its own job with its own price.
repostringNone.With github: the repository whose issues to read, as owner/name. Without it, a GitHub search is refused with input-missing.
localestringyour home MarketThe market and language, such as en-US.
placestringNone.With google-maps: the place to search around, such as Tacoma, WA. Without it, a maps search is refused with input-missing.
depthnumber10Results to read, at most 100. One price covers any depth.
withinstring'30d'Posts and news only: how far back to look.
trackHostname[]your DomainsReport these hosts’ positions in tracked.
excludeHostname[]None.Drop results on these hosts: pass your own to find mentions you did not write.
maxAgestring'1d'A stored reading younger than this returns at $0 instead of reading again.
maxCostnumber0.05The most this call may spend, in USD. Over it, the call is refused before it spends, or stops early with what it has.

Outputs

FieldTypeWhat null means
keywordstringNever null.
resultsResult[]Never null: empty when the site showed nothing, at $0.
results[].positionnumberNever null: as the site shows it, ads and blocks counted.
results[].urlstringNever null.
results[].titlestringNever null.
results[].snippetstring | nullThe site shows no snippet.
results[].hostHostnameNever null.
results[].kind'organic' | 'ad' | 'local' | 'news' | 'video' | 'post' | 'comment' | 'question' | 'issue'Never null: which kinds come back depends on venue (Output shapes).
results[].author{ handle: string; url: string | null } | nullAbsent on web results; null on a deleted account.
results[].publishedAtstring | null, in ISO 8601The site shows no date.
results[].engagement{ score?: number; comments?: number; views?: number } | nullAbsent on web results.
blocks.aiOverview{ text: string; cited: { url: string; host: Hostname }[] } | nullThe results page shows no AI answer above the results for this term today.
blocks.peopleAlsoAskstring[]Never null: empty when the page shows none.
blocks.localPackResult[]Never null: empty when the page shows no map pack.
tracked{ host: Hostname; position: number | null; url: string | null }[]Never null. position: null means not in the depth read, never position zero.
(page)ObservedThe page carries observedAt, method, venue and locale.
costCost, in USDNever null. In try mode usd is 0 and maxUsd is what the call would have cost.

Output shapes

venue picks the shape, and venue says which one came back, so your code switches on it.

When venue is google-search, bing. Ranked web results, the blocks above them, and where your hosts sit.

FieldTypeWhat null means
results[].kind'organic' | 'ad' | 'video' | 'news'Never null.
blocks.aiOverview{ text: string; cited: { url: string; host: Hostname }[] } | nullNo AI answer above the results for this term today. Bing returns null here every time.
blocks.peopleAlsoAskstring[]Never null: empty when the page shows none.
tracked[].positionnumber | nullNot in the depth read, never position zero.

Local results around a place

When venue is google-maps. The map pack for place: each business, its rating and its distance order. place is required.

FieldTypeWhat null means
results[].kind'local'Never null.
blocks.localPackResult[]Never null: the same rows as results, in map order.
blocks.aiOverviewnullAlways null on a maps search.

News results

When venue is google-news. Articles that name the term, newest first within within.

FieldTypeWhat null means
results[].kind'news'Never null.
results[].publishedAtstring | nullThe outlet shows no date.

Posts

When venue is reddit, x, linkedin, hacker-news, youtube, tiktok, stack-overflow, github. Posts and comments that use the term, with who wrote them and how far they spread. There is no results page, so blocks is empty and tracked lists any post on your own hosts.

FieldTypeWhat null means
results[].kind'post' | 'comment' | 'video' | 'question' | 'issue'Never null: video on YouTube and TikTok, question on Stack Overflow, issue on GitHub.
results[].author{ handle: string; url: string | null } | nullnull when the account was deleted.
results[].publishedAtstring | nullThe site shows no date.
results[].engagement{ score?: number; comments?: number; views?: number }Each count is absent where the site does not show it: X shows no score, Hacker News no views.
One row of each shape, as JSON

Loading the samples.

Read the result

FieldValueWhat it meansWhat to do
tracked.position8 to 30 at week 6The page is indexed and in range, but trusted less than the pages above it.Rewrite it from the query’s wording, and link it from your 3 strongest pages. (the week-6 rule in the search playbook)
tracked.positionnull with impressions after week 6The page is shown for the term but never reaches the top 100: it targets the wrong term.Re-pick a term at or under your difficulty ceiling. (the week-6 rule in the search playbook)
blocks.aiOverviewPresentPosition-one click rates on such terms fell 58% to 61% in two studies.Answer the question in your first 50 words; the cited pages are the ones to beat. (two click-rate studies of terms with an AI answer above the results)
results[].host3 or more of the top 10 hold your referring domains or fewerThe term is winnable with the links you can reach in 8 weeks.Read each one’s referring domains with Domain.benchmark, then approve the term. (the winnability rule in the search playbook)
results[].kindpost with 10 or more comments this monthBuyers are asking about the problem right now, in public.Answer the thread with a sourced reply, not a pitch. (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. A results page is shared across startups for the same term, site and locale for a day; tracked is yours.

Emits

Keyword.searched with { keyword, venue, results, blocks, tracked }

Performed by

Analyst

Price

About 3¢ per 100 searches. Free: a page read younger than maxAge. maxCost defaults to 5¢. 30 results pages for a first keyword map: 30 × 0.03¢ a search is about 0.9¢.

Reliability

The first route succeeds 99.1% of 1,710,120 calls. If it fails, a second route answers at 99.9% of 148,816 calls, and you pay for one.

Freshness

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

Batch and pages

Up to 1 term a call, up to 100 results deep. It answers in one response.

Rate

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

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

Text
Who shows up on Google for “rent tracking app for landlords”, and where
is leaseline.app? Spend up to 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
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: 'Keyword.search',
  input: { keyword: 'rent tracking app for landlords' },
  change: 'rank-change',
  threshold: 3,
  every: '1d',
})

Used by