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
- Deepens
- Content marketing
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' } })
{
"mode": "try",
"stored": false,
"estimate": "About 98¢ per 50 terms",
"sample": {
"term": "rent ledger template",
"position": "14",
"change": "−9",
"clicks": "31",
"named by": "perplexity"
}
}
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, ornullwhen 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.
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.
| Step | Verb | Returns | Price | Approval |
|---|---|---|---|---|
| 1. Read the results page for each saved term | Keyword.search | Your position in the top 100, and who holds the slots above you | About 1.5¢ | Runs on its own |
| 2. Ask each engine each question three times | Keyword.ask | Who each answer names and cites | About 96¢ | Runs on its own |
| 3. Read the week’s clicks from Search Console | Integration.call | Clicks and impressions per term for the last 7 days | Free | Runs on its own |
| 4. Read organic visits and answer share | Metric.query | organic-visits and answer-share this week, beside last week | Free | Runs on its own |
| 5. Write the readout row per term | Collection.query | Position, change, clicks and cited or not, one row per term | Free | Runs 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
| When | Then |
|---|---|
| A term is not in the top 100 | It reads position:, never 0, and its change is blank that week. |
| Search Console has a gap that week | clicks is empty for the week with a note; the ranks and answers still land. |
| You call it twice in one ISO week | The second call returns the stored readout at $0: it is idempotent per site and week. |
| Goal mode is asked for a one-off look back | It 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
| Ask | Compiles 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 |
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: 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
| Fact | Value |
|---|---|
| Guide | Keyword.track |
| Guide approval | limits |
| Goal | Tell 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. |
| Trigger | every('mon 07: |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-keyword-track-1 |
| Lookups | A 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
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. Read the results page for each saved term | code | Code | Code checks each result against its declared type | measured | $0.site, |
| 2. Ask each engine each question three times | code | Code | Code checks each result against its declared type | measured | $0.questions, |
| 3. Read the week’s clicks from Search Console | code | Code | Code checks each result against its declared type | measured | $0.site |
| 4. Read organic visits and answer share | code | Code | Code checks each result against its declared type | measured | $0.site |
| 5. Write the readout row per term | code | Code | Code checks each result against its declared type | measured | $1, |
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
| Kind | What it touches |
|---|---|
| Systems | Analytics and experiments |
| Nouns | Keyword, Integration, Metric, Collection |
| Events | Integration.called, Metric.queried, Collection.queried |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
site | Hostname | Required | Your site. |
terms | string[] | 'saved' | 'saved' | The terms to rank, or every term your Keyword opportunity map marked earn. |
questions | string[] | 'saved' | 'saved' | The buyer questions to ask the answer engines, or the ones your AI answer audit saved. |
engines | VenueId[] | ['chatgpt', | The answer engines asked. |
runs | integer | 3 | Asks per question per engine; share is read across all of them. |
maxCost | number | 1.50 | The 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).