URL: /code/data/keywords · Id: `page:/code/data/keywords`

# Keywords

> A Keyword is “a search phrase or AI prompt that pages target”: find what buyers type, measure it, and see who ranks.

A [Keyword](https://startupbuilder.dev/business/nouns/keyword) is “a search phrase or AI prompt that [pages](https://startupbuilder.dev/business/nouns/page) target”: find what buyers type, measure it, and see who ranks.

- **Noun:** [Keyword](https://startupbuilder.dev/business/nouns/keyword)
- **Calls:** 5
- **Emits:** `Keyword.found`, `Keyword.measured`, `Keyword.forecasted`, `Keyword.searched`, `Keyword.asked`

A Data Verb reads the market and returns a reading. A Software Verb changes your records and emits an Event. Every call here is a Data Verb: it reads, and it changes a record only where its Effects say so.

## Properties
| Field | Type | What `null` means |
| --- | --- | --- |
| `terms` | `Keyword[]` | Never null: empty when nothing passed `where`, at $0. |
| `terms[].term` | `string` | Never null. |
| `terms[].searches` | `number \| null`, in searches a month | Unknown: no reading for a long-tail term. Never zero demand. |
| `terms[].cpc` | `number \| null`, in USD a click | No advertiser bids on it, or no reading. |
| `terms[].difficulty` | `number \| null`, in 0 to 100 | Unknown. |
| `terms[].intent` | `'buy' \| 'compare' \| 'learn' \| 'navigate' \| null` | Unknown. |
| `terms[].trend` | `'rising' \| 'flat' \| 'falling' \| null`, in 12 months | Under 12 months of readings. |
| `terms[].position` | `number \| null` | Absent without `rival`; `null` past position 100. |
| `terms[].rivalUrl` | `string \| null` | Absent without `rival`. |
| `terms[].monthlyAdCost` | `number \| null`, in USD a month | Absent unless `mode: rival-paid`. |
| `terms[].posts` | `number \| null`, in posts this week | Absent unless `mode: trending`. |
| `terms[]` | [`Observed`](/code/data/types#observed) | Each row also carries `observedAt`, `method`, `venue` and `locale`. |
| `found` | `number`, in terms | Never null: before `where` ran. |
| `kept` | `number`, in terms | Never null: after `where` ran; you pay for these. |
| `peopleAlsoAsk` | `string[]` | Never null: empty when the results page shows none. |
| `ignored` | `string[]` | Never null: empty when every filter you sent was applied. |
| `cost` | [`Cost`](/code/data/types#cost), in USD | Never null. In try mode `usd` is 0 and `maxUsd` is what the call would have cost. |
| `measurements` | `Keyword[]` | Never null: one row per term sent, in order. |
| `measurements[].term` | `string` | Never null. |
| `measurements[].searches` | `number \| null`, in searches a month, 12-month average | Unknown: no reading. Never zero demand, and never billed. |
| `measurements[].cpc` | `number \| null`, in USD a click | No advertiser bids on it, or no reading. |
| `measurements[].competition` | `number \| null`, in 0 to 1, advertisers bidding | No reading. |
| `measurements[].difficulty` | `number \| null`, in 0 to 100 | Unknown, or not in `measures`. |
| `measurements[].intent` | `'buy' \| 'compare' \| 'learn' \| 'navigate' \| null` | Unknown, or not in `measures`. |
| `measurements[].trend` | `'rising' \| 'flat' \| 'falling' \| null`, in 12 months | Under 12 months of readings. |
| `measurements[].history` | `{ month: string; searches: number }[]`, in searches a month | Absent without `trend`; a month with no reading is left out. |
| `measurements[].aiSearches` | `number \| null`, in asks a month | Absent without `aiSearches`; `null` is no reading. |
| `measurements[]` | [`Observed`](/code/data/types#observed) | Each row also carries `observedAt`, `method`, `venue` and `locale`. |
| `clicks` | `number \| null`, in clicks in `period` | Too little volume to forecast, never zero clicks. |
| `impressions` | `number \| null`, in impressions in `period` | Too little volume to forecast. |
| `ctr` | `number \| null`, in share, 0.042 is 4.2% | Too little volume to forecast. |
| `averageCpc` | `number \| null`, in USD a click | Too little volume to forecast. |
| `spend` | `number \| null`, in USD in `period` | Too little volume to forecast. |
| `perKeyword` | `{ term: string; clicks: number \| null; spend: number \| null }[]` | Never null: one row per term; `null` where a term alone is too thin to forecast. |
| `keywords` | `string[]` | Never null: the set forecast. |
| `bid` | `number`, in USD | Never null. |
| `match` | `'exact' \| 'phrase' \| 'broad'` | Never null. |
| `period` | `string` | Never null. |
| `(row)` | [`Observed`](/code/data/types#observed) | The forecast carries `observedAt`, `method`, `venue` and `locale`; `method` is `reported` from your own ad account. |
| `keyword` | `string` | Never null. |
| `results` | `Result[]` | Never null: empty when the site showed nothing, at $0. |
| `results[].position` | `number` | Never null: as the site shows it, ads and blocks counted. |
| `results[].url` | `string` | Never null. |
| `results[].title` | `string` | Never null. |
| `results[].snippet` | `string \| null` | The site shows no snippet. |
| `results[].host` | [`Hostname`](/code/data/types#hostname) | Never 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 } \| null` | Absent on web results; `null` on a deleted account. |
| `results[].publishedAt` | `string \| null`, in ISO 8601 | The site shows no date. |
| `results[].engagement` | `{ score?: number; comments?: number; views?: number } \| null` | Absent on web results. |
| `blocks.aiOverview` | `{ text: string; cited: { url: string; host: Hostname }[] } \| null` | The results page shows no AI answer above the results for this term today. |
| `blocks.peopleAlsoAsk` | `string[]` | Never null: empty when the page shows none. |
| `blocks.localPack` | `Result[]` | 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)` | [`Observed`](/code/data/types#observed) | The page carries `observedAt`, `method`, `venue` and `locale`. |
| `answers` | `Answer[]` | Never null: one per engine per run. |
| `answers[].venue` | [`VenueId`](/code/data/types#venueid) | Never null. |
| `answers[].run` | `number`, in 1 to 3 | Never null. |
| `answers[].text` | `string` | Never null: the answer as shown. |
| `answers[].named` | `{ brand: string; position: number; host: Hostname \| null }[]` | Never null: brands in order of first mention; empty when none is named. |
| `answers[].cited` | `{ position: number; url: string; host: Hostname; title: string \| null }[]` | Never null: empty when the engine cites nothing. |
| `answers[].queries` | `string[]` | Never null: the searches the engine ran, empty when it does not show them. |
| `answers[].mentioned` | `{ brand: string; named: boolean; cited: boolean }[]` | Never null: one row per brand in `brands`. |
| `answers[]` | [`Observed`](/code/data/types#observed) | Each answer also carries `observedAt`, `method` and `locale`. |

## Object
```json
{
  "searches": 1900,
  "cpc": 2.4,
  "difficulty": 18,
  "intent": "learn",
  "trend": "rising",
  "venue": "google-search",
  "locale": "en-US"
}
```

## Verbs
| Verb | The question it answers | Price | Success rate |
| --- | --- | --- | --- |
| [`Keyword.find`](/code/data/keywords/find) | Which search terms should my pages target? | 5¢ per 100 ideas | 100% of 16,219 calls |
| [`Keyword.measure`](/code/data/keywords/measure) | How many people search this term, what does a click cost, and how hard is page one? | 18¢ per 100 terms | 99.94% of 7,106 calls |
| [`Keyword.forecast`](/code/data/keywords/forecast) | What would a month of ads on these terms cost, and how many clicks would it buy? | 9¢ a forecast | 100% of 97 calls |
| [`Keyword.search`](/code/data/keywords/search) | Who shows up when my buyer searches this, and where am I? | 3¢ per 100 searches | 99.1% of 1,710,120 calls |
| [`Keyword.ask`](/code/data/keywords/ask) | Do AI answers name me, and which pages do they cite? | 0.4¢ an answer | 99.96% of 131,093 calls |

## The order you call them in
1. [`Keyword.find`](/code/data/keywords/find): Which search terms should my pages target?
2. [`Keyword.measure`](/code/data/keywords/measure): How many people search this term, what does a click cost, and how hard is page one?
3. [`Keyword.search`](/code/data/keywords/search): Who shows up when my buyer searches this, and where am I?
4. [`Keyword.ask`](/code/data/keywords/ask): Do AI answers name me, and which pages do they cite?
5. [`Keyword.forecast`](/code/data/keywords/forecast): What would a month of ads on these terms cost, and how many clicks would it buy?

## Monitors on this Noun
| Verb | What it watches |
| --- | --- |
| [`Keyword.search`](/code/data/keywords/search) | Your rank for a term, daily; a move of 3 places or more records an Event. |
| [`Keyword.ask`](/code/data/keywords/ask) | Whether each answer engine names or cites you for a buying question, weekly, 3 runs each. |
| [`Keyword.search`](/code/data/keywords/search) | New Reddit, X and Hacker News posts asking about your problem, daily. |

## Works with
- **Workflows:** [Competitor gap](/capabilities/size-up-competitors/competitor-gap) · [Keyword opportunity map](/capabilities/research-demand/keyword-opportunity-map) · [Pick who to sell to first](/capabilities/find-customers/pick-who-to-sell-to) · [Ad test setup](/capabilities/run-ads/ad-test-setup) · [Search readout](/capabilities/measure/search-readout) · [Post-engager harvest](/capabilities/find-customers/post-engagers) · [AI answer audit](/capabilities/check-ai-answers/ai-answer-audit)
- **Agent tasks:** [Monthly search content](/capabilities/make-content/monthly-search-content) · [Answer page per question](/capabilities/make-content/answer-pages) · [Answer threads](/capabilities/publish-and-post/answer-threads) · [Launch day](/capabilities/earn-first-links/launch-day) · [Account brief](/capabilities/reach-out/account-brief) · [Message test](/capabilities/run-ads/message-test)
- **Services:** [Ranked page](/capabilities/make-content/ranked-page)