Skip to content

Search readout

Each week: where each saved term ranks and how it moved, which engines name you, and the clicks Search Console counted.

Kind
Workflow
Price
98¢ per 50 terms
Your time
5 minutes
Approval
Runs on its own
Keyword.track
You, to your agent

Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.

curl -X POST "https://api.sb/leaseline.app/keywords/track?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"goal":"Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
npx startup-builder do Keyword.track --try --data '{"goal":"Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Keyword.track({ goal: 'Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })"
  }
}
import { $ } from 'startup-builder'

const result = await $.try.Keyword.track({ goal: 'Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })
Response
{
  "mode": "try",
  "stored": false,
  "estimate": "About 98¢ per 50 terms",
  "sample": {
    "term": "rent ledger template",
    "position": "14",
    "change": "−9",
    "clicks": "31",
    "named by": "perplexity"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

What you get

  • 50 terms · Each week: where each saved term ranks and how it moved, which engines name you, and the clicks Search Console counted. · Keyword
  • terms[].position · Rank in the top 100, or null when it is not there; never 0.
  • terms[].change · Places moved since last week: −9 is nine places up.
  • terms[].clicks · Clicks Search Console counted for the term this week; it lags 2 to 3 days.
  • answerShare · Answers naming you, over all answers this week. Read it as a four-week average: one week moves with the engines, not with you.
  • organicVisits · Visits from search this week, from Analytics.

Connect once: Google Search Console, Google Analytics. How long: About 3 minutes.

Trigger

It runs weekly at 07:00 on the first weekday, as a Monitor over the saved terms and questions. A move past a threshold emits Keyword.measured with change, and Triggers fire on it.

TypeScript
every('mon 07:00', ($) => $.Keyword.track({ site: 'leaseline.app', terms: 'saved', questions: 'saved' }))

Steps

Your agent does these steps, one row per step; each step’s check is part of its row and its price.

StepVerbReturnsPriceApproval
1. Read the results page for each saved termKeyword.searchYour position in the top 100, and who holds the slots above youAbout 1.5¢Runs on its own
2. Ask each engine each question three timesKeyword.askWho each answer names and citesAbout 96¢Runs on its own
3. Read the week’s clicks from Search ConsoleIntegration.callClicks and impressions per term for the last 7 daysFreeRuns on its own
4. Read organic visits and answer shareMetric.queryorganic-visits and answer-share this week, beside last weekFreeRuns on its own
5. Write the readout row per termCollection.queryPosition, change, clicks and cited or not, one row per termFreeRuns on its own

Calls: Keyword.search, then Keyword.ask, then Integration.call, then Metric.query, then Collection.query.

About 98¢ per 50 terms, each time it runs, capped at $1.50.

Example: give it a goal

Tell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first.

The plan it shows runs 5 steps, priced before it spends anything; Run it above shows the result it returns.

When something fails

Each failure, and what happens next
WhenThen
A term is not in the top 100It reads position: null, never 0, and its change is blank that week.
Search Console has a gap that weekclicks is empty for the week with a note; the ranks and answers still land.
You call it twice in one ISO weekThe second call returns the stored readout at $0: it is idempotent per site and week.
Goal mode is asked for a one-off look backIt reads the stored weekly Events first at $0 and pays only for the weeks missing; repeating the readout stays the Monitor’s job.

Each step retries three times, then falls through to the next route; one item's failure lands in skipped with its reason, never failing the call. Before a step would cross maxCost it stops and returns what it has, with a resume link. Each step's idempotency key is the call's key plus the step and the item: leaseline-keyword-track-1-<step>-<item>.

Change it

AskCompiles to
Tell me the day a term drops off page one.Monitor.create
Ask once per engine instead of three times to halve the cost.Keyword.track
Refresh any page that slipped five places in a month.Site.grow
TypeScript
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'keyword-track',
  verb: 'Keyword.track',
  input: { site: 'string', terms: "string[] | 'saved'", questions: "string[] | 'saved'" },
  trigger: { schedule: 'mon 07:00' },
  maxCost: 1.5,
  execute: async (ctx, { site, terms = 'saved', questions = 'saved', engines = ['chatgpt', 'perplexity', 'gemini', 'google-ai-mode'], runs = 3 }) => {
    const ranks = await ctx.step('ranks', () => $.Keyword.search({ keywords: terms, venue: 'google-search', site, maxCost: 0.05 }))
    const answers = await ctx.step('answers', () => $.Keyword.ask({ questions, venues: engines, runs, maxCost: 1.1 }))
    const console = await ctx.step('console', () => $.Integration.call({ integration: 'google-search-console', op: 'performance', days: 7 }))
    const m = await ctx.step('metrics', () => $.Metric.query({ metrics: ['organic-visits', 'answer-share'], period: 'week' }))
    return readout(ranks, answers, console, m)
  },
})

Extend it with extends: 'Keyword.track' and base(), or compose your own from these steps.

Works with

Plays that order it: Answer engine optimization, Editorial SEO, Programmatic SEO.

It serves Measure, and runs in Launch and search. It deepens Content marketing.

For agents

For agents: the guide, its approvals, retries and lookups
FactValue
GuideKeyword.track
Guide approvallimits
GoalTell me how my rankings and AI answers moved since September, spend up to $1, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval.
Triggerevery('mon 07:00', ($) => $.Keyword.track({ site: 'leaseline.app', terms: 'saved', questions: 'saved' }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-keyword-track-1
LookupsA find: input asks unless exactly one record fits
Step
1. Read the results page for each saved term
2. Ask each engine each question three times
3. Read the week’s clicks from Search Console
4. Read organic visits and answer share
5. Write the readout row per term

The amounts live once, on the hub (approvals).

Who does each step, and how it is checked
StepFunctionDoerCheckerAssuranceInputs
1. Read the results page for each saved termcodeCodeCode checks each result against its declared typemeasured$0.site, $0.terms
2. Ask each engine each question three timescodeCodeCode checks each result against its declared typemeasured$0.questions, $0.engines, $0.runs
3. Read the week’s clicks from Search ConsolecodeCodeCode checks each result against its declared typemeasured$0.site
4. Read organic visits and answer sharecodeCodeCode checks each result against its declared typemeasured$0.site
5. Write the readout row per termcodeCodeCode checks each result against its declared typemeasured$1, $2, $3, $4

The doer is never the checker, and the weakest assurance on the path is the whole call’s.

Under the hood

Under the hood: the Systems, Nouns and Events it touches
KindWhat it touches
SystemsAnalytics and experiments
NounsKeyword, Integration, Metric, Collection
EventsIntegration.called, Metric.queried, Collection.queried

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
siteHostnameRequiredYour site.
termsstring[] | 'saved''saved'The terms to rank, or every term your Keyword opportunity map marked earn.
questionsstring[] | 'saved''saved'The buyer questions to ask the answer engines, or the ones your AI answer audit saved.
enginesVenueId[]['chatgpt', 'perplexity', 'gemini', 'google-ai-mode']The answer engines asked.
runsinteger3Asks per question per engine; share is read across all of them.
maxCostnumber1.50The most this call spends each time it runs, in USD. It stops before a step would cross it and returns what it has, with a resume link.

Emits Keyword.tracked. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents