URL: /capabilities/make-content/ranked-page · Id: `page:/capabilities/make-content/ranked-page`

# Ranked page

> Pages on terms you can win, billed only when each reaches the top 20 on Google within 120 days; a miss is rewritten free.

[Pages](https://startupbuilder.dev/business/nouns/page) on terms you can win, billed only when each reaches the top 20 on Google within 120 days; a miss is rewritten free.

```text title="Run it · Ask"
Rank 10 pages for property manager searches within 120 days, $25 each, and show me the terms first.
```

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

const result = await $.try.Page.rank({ goal: 'Rank 10 pages for property manager searches within 120 days, $25 each, and show me the terms first.', budget: { amount: 250, currency: 'USD' } })
```

```json title="Run it · Response"
{
  "mode": "try",
  "stored": false,
  "estimate": "About $2.58 per 10 pages",
  "bills": "$25 a ranked page",
  "sample": {
    "term": "rent ledger template",
    "page": "leaseline.app/rent-ledger-template",
    "rank at day 120": "14",
    "state": "ranked"
  }
}
```

- **Kind:** Service
- **Price:** $25 per ranked page
- **Your time:** 35 minutes
- **Approval:** Goes out on its own
- **Deepens:** [`capability:demand-generation`](https://startupbuilder.dev/systems/integrations/upstreams#demand-generation)

## What you get
- **10 pages** · pages on terms you can win, billed only when each reaches the top 20 on Google within 120 days; a miss is rewritten free. · [`noun:page`](https://startupbuilder.dev/business/nouns/page)
- `pages[]` · Each page, its term, its rank each week, and its state: `ranked`, `rewritten` or `credited`.
- `bill` · Ranked pages times the price; a page that never ranks is never billed.

**Connect once:** [Google Search Console](/code/integrations/google-search-console), [Bing Webmaster](/code/integrations/bing-webmaster). **How long:** 120 days from each page going live, and 120 more for a rewrite.

## Trigger
It runs when you call it, or on any Trigger you set.

```ts
await $.Workflow.create({ verb: 'Page.rank', trigger: { schedule: 'weekly' } })
```

## 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. Pick winnable terms | [`Keyword.map`](/capabilities/research-demand/keyword-opportunity-map) | Ten terms at or under your difficulty ceiling, with 3 of the top 10 inside the links you can earn | About 87¢ | Runs on its own |
| 2. Write each page | [`Page.answer`](/capabilities/make-content/answer-pages) | A page that settles its term in the first 60 words, every claim sourced | About $1.18 | Goes out on its own |
| 3. Get each page indexed | [`Site.index`](/capabilities/get-indexed/index-the-site) | Each page held by Google and Bing, or the engine’s reason it is not | Free | Runs on its own |
| 4. Read each page’s rank every week | [`Keyword.search`](/code/data/keywords/search) | Each page’s position for its term, weekly for 17 weeks | About 5.1¢ | Runs on its own |
| 5. Rewrite each page outside the top 20 at day 120 | [`Page.answer`](/capabilities/make-content/answer-pages) | The page rewritten against the pages that rank above it, at no charge | About 47¢ | Goes out on its own |
| 6. Read each rewrite’s rank for 120 more days | [`Keyword.search`](/code/data/keywords/search) | Each rewrite’s position weekly; a second miss is credited | About 2¢ | Runs on its own |

Calls: `Keyword.map`, then `Page.answer`, then `Site.index`, then `Keyword.search`, then `Page.answer`, then `Keyword.search`.

About $2.58 per 10 pages, each time it runs, capped at $12.00.

## Example: give it a goal
> Rank 10 pages for property manager searches within 120 days, $25 each, and show me the terms first.

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

## When something fails

| When | Then |
| --- | --- |
| A page is not indexed by day 30 | Index the site names the engine’s reason, the fix runs, and the 120 days start from the day it is held. |
| Google changes the results page for a term | The weekly read records it; a term that turns into a results page with no organic slots in the top 20 is swapped for the next winnable term, at no charge. |
| You edit a page yourself | The warranty holds unless the edit removes the answer in its first 60 words; the read notes the edit date. |

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-page-rank-1-<step>-<item>`.

## What it promises
| Term | This Service |
| --- | --- |
| Done | The page ranks in the top 20 on Google Search for its term, in its locale, within 120 days of going live. |
| Counted by | `Page.ranked` |
| Billed | $25 a ranked page, on acceptance |
| Target | Each page in the order, one winnable term each. |
| Accepted when | Two weekly reads in a row in the top 20 accept the page. |
| Credited | A page that misses the top 20 after its free rewrite. |
| Remedy | A page outside the top 20 at day 120 is rewritten once at no charge and read for 120 more days; a second miss is credited. |
| Evaluator | Code reads the term’s results page on Google Search every week. |
| Window | 120 days from going live, then 120 more after a rewrite. |
| Assurance | `measured`, the weakest check on the way to done |
| Oversight | `none`: agents do every step |

**Leaseline’s first order: 10 pages live on September 2, read at day 120 and again at day 240.**

| Count | What happened |
| --- | --- |
| 10 | Ordered |
| 10 | Delivered |
| 6 | In the top 20 by day 120, and billed. |
| 4 | Outside it at day 120, so rewritten at no charge. |
| 2 | Rewrites in the top 20 by day 240, and billed. |
| 2 | Missed again: credited, never billed. |
| **8** | **Accepted, billed 8 × $25 = $200** |

## Change it
| Ask | Compiles to |
| --- | --- |
| Only terms at difficulty 20 or under. | [`Keyword.map`](/capabilities/research-demand/keyword-opportunity-map) |
| Read the rank on Bing too. | [`Keyword.search`](/code/data/keywords/search) |
| Ten more pages next quarter, on the terms the first ten opened up. | [`Page.rank`](/capabilities/make-content/ranked-page) |

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

export default Service({
  name: 'page-rank',
  verb: 'Page.rank',
  input: { site: 'string', seeds: 'string[]', pages: 'integer', locale: 'string' },
  maxCost: 12,
  builtFrom: ['Keyword.map', 'Page.answer', 'Site.index'],
  oversight: { posture: 'none' },
  contract: {
    done: 'Page.ranked', accept: { by: 'metric:rank', top: 20, reads: 2 }, assurance: 'measured',
    bills: { usd: 25, per: 'ranked page' }, window: '120d', remedy: 'rewrite-once-then-credit',
  },
  execute: async (ctx, { site, seeds, pages = 10, locale = 'en-US' }) => {
    const map = await ctx.step('terms', () => $.Keyword.map({ seeds, site, winnable: true, limit: pages, maxCost: 1 }))
    for (const term of map.terms) await ctx.step(`write:${term.text}`, () => $.Page.answer({ question: term.text, site, maxCost: 0.3 }))
    await ctx.step('index', () => $.Site.index({ site }))
    await ctx.every('1w', { for: '120d' }, ($) => $.Keyword.search({ keywords: map.terms.map((t) => t.text), locale, maxCost: 0.3 }))
  },
})
```

Extend it with `extends: 'Page.rank'` and `base()`, or [compose your own](/code/compose) from these steps.

## Works with
**Plays that order it:** [Answer engine optimization](/plays/aeo), [Category hubs](/plays/category-hubs), [Editorial SEO](/plays/editorial-seo), [Programmatic SEO](/plays/programmatic-seo), [Switch kit](/plays/switch-kit).

| Built from | Kind | Its own price |
| --- | --- | --- |
| [Keyword opportunity map](/capabilities/research-demand/keyword-opportunity-map) | Workflow | About 87¢ per 300 terms |
| [Answer page per question](/capabilities/make-content/answer-pages) | Agent task | About 12¢ a page |
| [Index the site](/capabilities/get-indexed/index-the-site) | Workflow | Free |

It serves [Make content](/capabilities/make-content). It deepens [`process:content-marketing`](https://startupbuilder.dev/workflows/processes/content-marketing) and [`capability:demand-generation`](https://startupbuilder.dev/systems/integrations/upstreams#demand-generation).

## For agents

| Fact | Value |
| --- | --- |
| Guide | `Page.rank` |
| Guide approval | `limits` |
| Goal | Rank 10 pages for property manager searches within 120 days, $25 each, and show me the terms 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. |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | `leaseline-page-rank-1` |
| Lookups | A `find:` input asks unless exactly one record fits |

| Step | Action class | Seat rule that would hold it |
| --- | --- | --- |
| 1. Pick winnable terms | None | None. |
| 2. Write each page | None | None. |
| 3. Get each page indexed | None | None. |
| 4. Read each page’s rank every week | None | None. |
| 5. Rewrite each page outside the top 20 at day 120 | None | None. |
| 6. Read each rewrite’s rank for 120 more days | None | None. |

The amounts live once, on the hub ([approvals](https://startupbuilder.dev/agents/approvals)).

| Step | Function | Doer | Checker | Assurance | Inputs |
| --- | --- | --- | --- | --- | --- |
| 1. Pick winnable terms | `code` | Code | Code keeps a term only when its difficulty and the top 10’s authority pass the declared winnable rule | `measured` | `$0.seeds, $0.site` |
| 2. Write each page | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | A second model checks every claim against its cited source before the page publishes | `graded` | `$1.terms` |
| 3. Get each page indexed | `code` | Code | Index state read daily from Google Search Console and Bing Webmaster until each page is held | `measured` | `$2.pages` |
| 4. Read each page’s rank every week | `code` | Code | The results page for the term, read on Google Search in your locale; two reads in a row in the top 20 settle it | `measured` | `$1.terms, $0.locale` |
| 5. Rewrite each page outside the top 20 at day 120 | `agentic` | The [Content lead](/capabilities/agents/content-lead) agent | A second model reads the rewrite beside the top 3 results and names what it adds | `graded` | `$4.missed` |
| 6. Read each rewrite’s rank for 120 more days | `code` | Code | The same weekly results page read, for the rewrite’s 120 days | `measured` | `$5.pages` |

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

## Under the hood

| Kind | What it touches |
| --- | --- |
| Systems | None. |
| Nouns | [`noun:keyword`](https://startupbuilder.dev/business/nouns/keyword), [`noun:page`](https://startupbuilder.dev/business/nouns/page), [`noun:site`](https://startupbuilder.dev/business/nouns/site) |
| Events | None. |

## Reference

| Name | Type | Default | Meaning |
| --- | --- | --- | --- |
| `site` | `Hostname` | Required | Your domain, connected once. |
| `seeds` | `string[]` | Required | Three seed phrases in your buyers’ words; the map picks the winnable terms from them. |
| `pages` | `integer` | `10` | Pages in the order, one term each. |
| `locale` | `string` | `'en-US'` | The results page each rank is read on. |

Emits `Page.ranked`. Refuses with [`max-cost`](/code/refusals#max-cost) before it spends, and with the Data calls and Accounts groups ([refusals](/code/refusals)).