diff --git a/web/content/marketing/library/keyword-ranking-report.mdx b/web/content/marketing/library/keyword-ranking-report.mdx new file mode 100644 index 000000000..b26017303 --- /dev/null +++ b/web/content/marketing/library/keyword-ranking-report.mdx @@ -0,0 +1,137 @@ +--- +title: "The keyword ranking report your CEO will read" +description: "A table of positions is not a report. Lead with the number the business tracks, group the movement into four buckets, explain the three rows that caused it, and say what happens next. One page, every week." +--- + +## What should a keyword ranking report show? + +The number the business already cares about, first, then rankings as the explanation for it. Most ranking reports do this backwards. They open with a table of keywords and positions, sometimes with green and red arrows, and leave the reader to work out whether anything in it matters. The reader is usually someone who does not do SEO, has ten minutes, and needs to decide whether to keep paying for this. A table does not help them decide. + +Grant Simmons, who ran search at Homes.com and now works with Fiat Growth, drew the line when we were talking about a well-liked Search Console tool: + +> it's still not reporting. It's insights. + +![A quote card reading: It's still not reporting. It's insights. Attributed to Grant Simmons of Fiat Growth, formerly Homes.com, from The Unscripted SEO Interview Podcast.](/library/rank-tracking/quote-not-reporting-insights.png "Grant Simmons on reporting versus insights") + +Insights are what a tool gives you. A report is what you give someone who has to make a decision with it. His full conversation on measurement is [on the Unscripted SEO podcast](https://unscriptedseo.com/grant-simmons-on-entity-seo-llms-and-the-future-of-search-visibility/). The structure below is built to be the second thing. + +## The structure + +### 1. One number, and what it did + +Open with the metric the business tracks, for the pages the tracker covers, against the previous period. Clicks from Search Console if that is what you have; leads or revenue on those pages if analytics is connected. One sentence: "Organic clicks on the twelve service pages we track were 1,240 this month, up from 980." That sentence is the report. Everything after it is support. + +![OpenSEO Search Console report for seoarcade.com, last 28 days against the previous period: clicks, impressions, click-through rate and average position with the change, then clicks, impressions, CTR and position by page](/library/rank-tracking/search-console-clicks-by-page-openseo.png "Search Console clicks by page in OpenSEO") + +Do not open with average position across all tracked keywords. It is a mean of unrelated things, it moves when you add a keyword, and nobody outside SEO knows whether 11.4 is good. + +### 2. Movement in four buckets + +Rankings come next, as counts rather than rows. Of the keywords tracked this period: + +- how many entered the top three +- how many entered page one +- how many dropped off page one +- how many did not move + +Label the comparison period next to these counts. The OpenSEO MCP currently compares ranks against a seven-day baseline, falling back to the earliest available snapshot when history is sparse. In a monthly report, present this as the latest weekly movement alongside the monthly click totals. It is not a month-over-month ranking comparison. + +### 3. The three rows that explain the number + +Now the table, cut to three rows: the biggest gain, the biggest loss, and the row where the position held but the clicks changed. Show keyword, position, previous position, ranking URL, and what is on the page. + +The third row is the one that earns the report its credibility. When a term holds position 4 and loses half its clicks, the reason is almost always something Google put above it: an AI overview, a local pack, a video block. A tracker that records SERP features shows it in a column, and a report that names it turns "clicks fell, not sure why" into "clicks fell because Google added an answer box, and here is what we do about it". + +### 4. Brand and non-brand, separately + +Split the tracked list and the clicks into brand and non-brand before you draw any conclusion. Brand terms rise when the company does marketing of any kind, and a report that counts them as SEO wins will get found out at the first hard question. Charlie Birch, founder of Humanize Collective, on what the number is supposed to be for: + +> the result that you ultimately want is not search volume, it's conversion and retention + +The non-brand line is the one SEO can claim. Report it on its own, even when it is the smaller number. Especially then. Expect the query rows to add up to less than the page total: Search Console withholds a share of queries for privacy, so some clicks on every page belong to queries you will never see. Say so in a footnote rather than letting the reader find the gap. [The Dark Query Problem](/blogs/dark-queries) covers how large that share is and how to read around it. Her episode is [here](https://unscriptedseo.com/charlie-birch-on-where-brand-strategy-meets-seo-what-every-founder-needs-to-know/). + +### 5. AI answers, with the caveat attached + +If you report visibility in AI assistants, say what the number is. Brittany Trafis, founder of Soarion Digital, on the tools that monitor it: + +> It is a monitoring tool. It is directional. do not think this data is Google Search Console level data. It is nowhere close + +Put the row in, label it directional, and do not let it sit next to the click count as if it were the same kind of fact. Her episode on treating AI search as its own channel is [on the podcast](https://unscriptedseo.com/brittany-trafis-ai-search-is-not-seo-2-0-unscripted-seo-podcast/). + +### 6. What we did, what we will do + +Two short lists. The work that shipped this period, and the work planned for next, each tied to a row in section 3 or a bucket in section 2. This is the section that gets the budget renewed, because it connects the numbers to decisions someone made. + +## What to leave out + +The full keyword table goes in an attachment, not the body. Anyone who wants it can open it; putting it in front of the reader costs the attention you need for section 1. + +Year-on-year comparisons need a warning label now. Google's results pages changed enough between 2024 and 2026 that a query's clicks last year and this year are often not the same kind of number, because the page they came from had a different shape. Compare periods, name what changed on the SERP in between, and resist the chart that makes it look continuous. + +And leave out any score you cannot explain in a sentence. A "visibility index" invites a conversation about how it is calculated instead of a conversation about what to do. + +## Run this with the OpenSEO MCP + +Connect Search Console to the project and the [OpenSEO MCP](/docs/mcp) to your assistant. The tracker read and the Search Console read use no credits, so the report itself is free to produce once the tracker has run. + +```text +Using the OpenSEO MCP on project [name], write this month's ranking +report for [company]. + +1. Call get_rank_tracker to list trackers, then read the relevant + ones with trackerId. Pull every tracked keyword with position, + previous position, and ranking URL. Label this section "Latest + weekly ranking movement": the comparison uses a seven-day + baseline, with an earliest-available fallback for sparse history. + Do not present it as monthly movement or assume a full week of + history when that is not established. + +2. Pull Search Console performance for the last 28 days and the 28 + days before, by page, filtered to the URLs from step 1. Give me + total clicks for both periods. That is the first sentence. + +3. Bucket the keywords: entered top 3, entered page 1, left page 1, + unchanged. Four counts. + +4. Show me three rows: the largest position gain, the largest loss, + and the keyword where position moved by 2 or less but Search + Console clicks on its URL changed by more than 30%. Include SERP + features for that third row. + +5. Pull Search Console query data for the same two 28-day periods, + filtered to the same URLs from step 1. Split reported queries into + brand (containing "[brand]") and non-brand. Report non-brand clicks + separately. Note that query totals can omit anonymized queries + and differ from page totals. + +6. Do not compute an average position across keywords. Do not + invent a score. Put the full table in a separate block at the end. +``` + +Step 4 is the only step that needs judgement, and the prompt hands it a rule instead. Adjust the thresholds to your site, but keep the rule written down so the report is consistent month to month. + +## A report is a request for a decision + +Every ranking report is asking the reader to do something: keep funding this, approve that fix, accept that a term is lost. Write it so the request is visible in the first sentence and supported by the fourth section, and the tracker underneath it becomes the most useful thing in your stack rather than the most ignored. If you have not built the tracker yet, [which keywords to track](/library/rank-tracking/which-keywords-to-track) is where to start. + +## Ranking report FAQ + +### What should an SEO ranking report include? + +The business metric for the tracked pages against the prior period, ranking movement as four counts (entered top 3, entered page 1, left page 1, unchanged), the three rows that explain the movement with SERP features noted, a brand and non-brand split, and a short list of work done and planned. The full keyword table goes in an attachment. + +### How often should I send a ranking report? + +Monthly for most businesses, weekly if a launch or migration is in progress. The tracker can check daily; the report should not, because week-to-week ranking noise is real and reporting it trains the reader to ignore the report. + +### Should a ranking report show average position? + +Not across all keywords. It is a mean of unrelated queries, changes whenever the list changes, and means nothing to a non-SEO reader. Report positions per keyword for the few rows that matter and movement counts for the rest. + +### How do I explain a ranking drop to a client or manager? + +Name the keyword, the previous and current position, the URL, and what is now on the results page, then say what you are doing about it. If the position held and clicks fell, show the SERP feature that took the click. A drop with a cause and a plan is a normal report; a drop with neither is a problem. + +### Can OpenSEO generate a ranking report? + +It provides the pieces: tracked positions with previous positions and SERP features, Search Console clicks by page and query at no credit cost, and an MCP so an assistant can assemble the report from a prompt like the one above. It does not produce a composite score, on purpose. diff --git a/web/content/marketing/library/local-rank-tracking.mdx b/web/content/marketing/library/local-rank-tracking.mdx new file mode 100644 index 000000000..7c26a4885 --- /dev/null +++ b/web/content/marketing/library/local-rank-tracking.mdx @@ -0,0 +1,112 @@ +--- +title: "Local rank tracking: your position depends on where the searcher is standing" +description: "For a local business there is no single rank, only a rank at a point on the map. A nine-point grid around one Tennessee service company showed four different businesses holding number one, and the company itself nowhere in the set." +--- + +## Why a local business cannot have one rank + +Ask Google for a plumber from the north side of a town and from the south side, three kilometres apart, and you get two different map packs. Neither is wrong. Google weights proximity heavily for local queries, so the answer moves with the searcher, and a rank tracker that checks from one fixed point reports one of the many answers as if it were the answer. + +Melissa Popp, content strategy director at Rickety Roo, states the rule most local SEOs work from: + +> we know proximity and relevance are two of the most important factors for how you show up in map pack as a local business + +![A quote card reading: Proximity and relevance are two of the most important factors for how you show up in the map pack as a local business. Attributed to Melissa Popp, content strategy director at Rickety Roo, from The Unscripted SEO Interview Podcast.](/library/rank-tracking/quote-proximity-and-relevance.png "Melissa Popp on proximity and relevance") + +Her episode on fixing your Google Business Profile before anything else is [on the Unscripted SEO podcast](https://unscriptedseo.com/melissa-popp-go-fix-your-google-business-profile/). The implication for tracking is that a local business needs its position measured from several places, not one, or the number will describe the tracker's location rather than the customer's. + +## What a nine-point grid showed + +Here is the shape of the problem on a real business: an HVAC and plumbing company in a mid-sized Tennessee town. I ran a three-by-three grid, nine points three kilometres apart, centred on the town, on mobile, for two queries. + +For `plumber cookeville tn`, the number one result was a different business depending on the point. One plumber held the top spot across the north and west, a larger plumbing, heating and cooling company held the centre and the south, a third business took the east and south-east, and a fourth appeared at a single north-east point. Every point returned 18 to 20 results. + +![OpenSEO local rank grid for plumber cookeville tn: nine points three kilometres apart, with a different business at number one across the north-west, the centre and south, the east, and the north-east, and the target business matched at none of them](/library/rank-tracking/local-rank-grid-cookeville-openseo.png "Local rank grid in OpenSEO") + +For `hvac repair cookeville`, the split was cleaner: one company held number one at seven of nine points and a second company took two of the three points along the southern edge. + +So over a six-kilometre square, a searcher could be shown five or six different businesses at number one depending on which query they typed and where they typed it. A single-point tracker reporting "position 1" or "position 4" for this town would be reporting one of those cells and calling it the market. + +The company I was checking on appeared at none of the eighteen points. The grid's name-match returned nothing, and a listings search within 15 kilometres found no business under the name the company uses on its website. That is a finding in its own right, and it comes before any question of rank: the first thing a local grid tells you is whether you are in the set at all. If the profile exists under another name, that name needs to go into the tracker; if it does not exist, no amount of rank tracking will find it. + +## Storefront or service area + +Set up your Business Profile to match how you serve customers. [Google allows eligible hybrid businesses](https://support.google.com/business/answer/9157481) to show a storefront address and a service area when they both receive customers and travel or deliver to them. If customers do not visit your address, hide it and list the area you serve. + +Melissa describes the difficulty of competing across locations: + +> it can be a lot easier to rank an actual brick and mortar location than it can be a service area page because you open up the competitiveness + +Choose the profile settings that describe your business accurately. You can also build useful location pages for the neighbourhoods and towns you serve to reach customers through organic website results. [Map positioning to real demand](/library/keyword-research/positioning-to-demand) covers finding which of those towns search for it. + +## Which points to track + +Run a Maps grid to survey your visibility, then repeat it with the same settings to compare positions over time. Choose points where your customers search. Melissa's method for finding them: + +> there's also testing that needs to be involved there to figure out, know, like what areas, you know, looking in GA4 and other analytics tools, you know, where are people searching from down to at that city level + +That gives you two to five named places. Separately, OpenSEO rank trackers measure your website's organic rankings from a chosen location. They do not track your Business Profile's Maps position. A business serving three towns can use three small trackers to follow its organic results. Twenty keywords in three locations is 60 checks a week, which on the queued schedule is about $0.60 a month. + +The grid itself is priced per point: a 3x3 grid is nine Maps searches, a 5x5 is twenty-five, and the app shows the cost before it runs. Use the 3x3 for a quarterly survey and the 5x5 when you are deciding whether to open or list a second location. + +## What to do with a bad grid + +If you rank well at the centre and fall off two kilometres out, that is proximity doing what proximity does, and the fixes are the ones Melissa puts first: a complete, accurate profile in the right category, reviews you answer, and hours and contact details that are correct. If you rank badly everywhere, check the category and the name before anything else. And if you are absent, as the company above was, the tracker has done its job on the first run by telling you the problem is upstream of rankings. + +This is also a budget conversation. Melissa on the size of budget she can work with: + +> I can work with $500. It doesn't mean that we are going to have all the results we want in six months or a year. But I can move the needle with $500 + +A local grid and three small trackers are a few dollars of that, and they are what stops the other $490 being spent on the wrong town. + +## Run this with the OpenSEO MCP + +Connect the [OpenSEO MCP](/docs/mcp): use the grid for Maps positions and a rank tracker for organic website rankings. The [local-seo](/docs/skills/local-seo) skill packages this workflow. + +```text +Using the OpenSEO MCP on project [name] for [business name] at +[address or coordinates]: + +1. Search local businesses within 10 km for "[business name]" and + confirm the listing exists. Give me its exact title, its cid, and + its primary category. If nothing matches, stop and tell me. + +2. Run a 3x3 local rank grid, 2 km spacing, on mobile, for + "[main service] [town]", using the cid from step 1 as the target. + Show me the rank at each point and the business holding #1 at + each point. + +3. Repeat for "[second service] [town]". + +4. Summarise: at how many of the 9 points am I in the top 3, how + many in the top 10, and which competitors hold #1 where I do not. + +5. For organic website rankings, separately create one rank tracker + per location I name, weekly, mobile, depth 20. Estimate the cost + with 20 keywords each before adding anything. Explain that these + trackers do not measure Maps positions. +``` + +Step 1 is the one that would have saved the company above a quarter of guessing. Match on the cid, not the name, once you have it. + +## Local rank tracking FAQ + +### Why do my local rankings look different on my phone and my colleague's? + +Because you are standing in different places, or Google thinks you are. Proximity is one of the strongest signals for local queries, so two searchers a few kilometres apart routinely see different map packs. A tracker that checks from one point reports one of those views. + +### What is a local rank grid? + +A set of searches run from points on a grid around a location, usually 3x3 or 5x5, reporting where a business ranks at each point. It shows how far a business's visibility reaches and where competitors take over, which a single rank check cannot. + +### How many points should a local rank grid have? + +Nine, spaced to cover the area you serve, is enough to see the shape. Use twenty-five when you are making a location decision. Beyond that you are paying for resolution you will not act on. + +### Should a service-area business set up a service area or an address on Google? + +Use the settings that match how you serve customers. An eligible storefront that also visits or delivers to customers can show both an address and a service area. If customers do not visit your address, hide it and list your service area. + +### Does OpenSEO do local rank tracking? + +Yes. The MCP local rank grid measures Business Profile positions in Maps. Scheduled rank trackers measure organic website rankings from a chosen location. Both use credits. On the hosted app, scheduled and live rank-tracker checks require the $10/month plan, which includes $10 of credits. diff --git a/web/content/marketing/library/search-console-vs-rank-tracker.mdx b/web/content/marketing/library/search-console-vs-rank-tracker.mdx new file mode 100644 index 000000000..9ad4ab74d --- /dev/null +++ b/web/content/marketing/library/search-console-vs-rank-tracker.mdx @@ -0,0 +1,125 @@ +--- +title: "Is Search Console a rank tracker? Where the free data stops" +description: "Search Console tells you an average position across searchers, devices, and countries, for the queries it chooses to show you. That is enough for a lot of sites. Here is how to tell whether yours is one of them." +--- + +## Can you check keyword rankings for free? + +Yes, for your own site, in Google Search Console. It costs nothing, it is Google's own record rather than a third party's guess, and it reports the metric a rank tracker cannot: clicks. For a site that wants to know whether SEO is working, Search Console is the first answer and sometimes the only one needed. + +What it does not give you is a rank. It gives you an average position, and the difference matters more than the name suggests. + +## What Search Console's position is + +Every time your site appears in a result set, Google records the position of your highest result. The number in the report is the mean of those positions across every impression in the date range, every device, every country, and every query that rolls up into the row you are looking at. + +So a query at "position 8.4" might be position 3 on desktop in one state and position 14 on mobile everywhere else. A page at "position 12" might be at 5 for one query and 30 for another. The average is true and it hides the shape. + +Three other properties of the data shape what you can do with it: + +- **Recent data can be preliminary.** The [24-hour view](https://developers.google.com/search/blog/2024/12/recent-data-search-console) shows data within hours. Finalized reports arrive later, and recent figures can change. +- **It keeps 16 months.** Grant Simmons, who ran search at Homes.com and now works with Fiat Growth, points out that this is the part people waste: "generally you get a lot more information when you look at the 16 months of Google search console data to have an understanding of if the article has been around for that long all the queries that have mapped not just the most recent queries I think that's often forgotten." His episode on measurement is [on the Unscripted SEO podcast](https://unscriptedseo.com/grant-simmons-on-entity-seo-llms-and-the-future-of-search-visibility/). + ![A quote card reading: You get a lot more information when you look at the 16 months of Google Search Console data. Attributed to Grant Simmons of Fiat Growth, formerly Homes.com, from The Unscripted SEO Interview Podcast.](/library/rank-tracking/quote-sixteen-months-search-console.png "Grant Simmons on 16 months of Search Console data") + +- **It does not show every query.** A share of your clicks arrive on queries Search Console never reports, for privacy reasons. I have a post that earns about a hundred clicks a month, and the query report explains twelve of them. [The Dark Query Problem](/blogs/dark-queries) is about what to do with the rest. + +There is one more thing worth knowing if you compare Search Console across time. In the autumn of 2025, Google stopped serving 100 results on a single page. Rank trackers had been fetching that page for years to record deep positions, and every fetch counted as an impression for every site on it. When the page went away, desktop impressions fell across the industry at once. If your impressions chart has a cliff that month, that is the cliff, and it was never people. + +## What a rank tracker adds + +A tracker asks Google a specific question on a schedule you set: for this keyword, from this location, on this device, where does this domain appear, and what else is on the page? It records the answer as a number, not an average. + +A tracker puts these measurements together for each scheduled check: + +1. **A single position** for one keyword on one device in one place, comparable week to week. +2. **The ranking URL for that check.** Search Console also reports URLs when you group by query and page; a tracker records which URL appeared in the specific search it ran. +3. **The previous position**, so movement is a number in a column rather than a chart you squint at. +4. **SERP features** on the page: an AI overview, a local pack, a video carousel, a shopping block. When your position holds and your clicks fall, this column is usually the reason. +5. **Competitors' positions**, for terms you do not rank for at all. Search Console has nothing to say about a query you never appeared for. + +Here is what the difference looks like on a real tracker. The 25 keywords below were chosen because Search Console reported seoarcade.com at average positions between 2 and 20 for them. After one mobile check from the US, three had a position; the other 22 were not in the top 40 at all. The averages were true. The positions were not there. + +![OpenSEO rank tracker results for seoarcade.com after one mobile check: 25 tracked keywords sorted by position, three with a position, ranking URL and SERP features, the rest showing no position inside the top 40](/library/rank-tracking/rank-tracker-positions-openseo.png "Rank tracker results in OpenSEO") + +The cost is that every one of those answers is a paid request. In OpenSEO, scheduled checks run through a queue that prices out at a fraction of a cent per keyword per device, so a 25-keyword weekly tracker costs about $0.25 a month; a one-off live check costs around three and a half times more per keyword. On the hosted app, rank checks need the $10 plan, which includes $10 of credits each month. Search Console reads in OpenSEO cost nothing at all. + +## When Search Console alone is enough + +More often than tool vendors admit. Search Console is enough when all of the following are true: + +- You have one location and one language, so the average is not blending markets. +- You can read direction from a chart and do not need a position to the decimal. +- Your questions are about your own pages, not a competitor's. +- The pages you care about are ones Google already shows, so the query rows exist. + +A ten-page consultancy site, a regional service business with one office, a founder checking whether the launch post is gaining: Search Console, filtered to the right page and device, answers the question. Adding a tracker adds a bill and a second dashboard without adding a decision. + +## When you need the tracker + +Four situations, and one of them is usually what tips it. + +- **You are local.** Position depends on where the searcher is standing, and Search Console's country filter is far too coarse. [Local rank tracking](/library/rank-tracking/local-rank-tracking) covers this. +- **You report upward.** "Average position improved from 14.2 to 13.1" is a sentence nobody can act on. "Nine of the twenty terms tied to the pricing page moved onto page one" is. Search Console cannot produce the second sentence without a lot of manual work each week. +- **You need to know why clicks fell while position held.** That is the SERP features column. +- **You want a competitor in the same table.** Search Console is a first-party tool and will never report someone else's site. + +## Use both, in the right order + +The workable pattern is Search Console first and the tracker second. Search Console picks the keywords, because it knows which queries already show your site and how far down; [which keywords to track](/library/rank-tracking/which-keywords-to-track) is that process. The tracker then follows those terms with the precision Search Console lacks. Then Search Console comes back at the end, because clicks are the number the tracker cannot produce, and a report that shows positions moving without showing what the clicks did is half a report. + +## Run this with the OpenSEO MCP + +Connect Search Console to the project, then connect the [OpenSEO MCP](/docs/mcp). The first three steps use no credits. + +```text +Using the OpenSEO MCP on project [name]: + +1. Pull Search Console performance for the last 28 days by query and + device. Show me the 20 queries with the most impressions, with + position on mobile and on desktop side by side. Flag any query + where the two positions differ by more than 5. + +2. Pull the same 20 queries by query and page. For any query that + returns two of my URLs, list both with their impressions. + +3. Pull the last 16 months by date for the whole property and tell + me whether impressions dropped by more than 30% in any single + week, with the week. + +4. Call get_rank_tracker for this project and list the tracked + keywords with their latest mobile position and previous position. + For each one that is also in the Search Console list, put the two + positions in one row: tracker position, Search Console average. + +5. Tell me which keywords I am tracking that Search Console shows no + impressions for, and which Search Console queries with more than + 500 impressions I am not tracking. +``` + +Step 4 is where the difference shows. The tracker position and the Search Console average for the same term will not match, and the gap between them is the blending this page is about. Step 5 keeps the two lists honest with each other over time. + +## The free data is the ground truth for clicks + +Everything a tracker reports is a description of a results page. Search Console is a record of what people did on it. When the two disagree about whether things are going well, the clicks are right and the positions are the explanation. Set them up in that relationship from the start and you will read both correctly. + +## Search Console and rank tracking FAQ + +### Is Google Search Console a rank tracker? + +Not in the usual sense. It reports an average position per query or page, blended across devices, countries, and dates, for your own verified site only. Its 24-hour view shows recent preliminary data; finalized reports arrive later. A rank tracker records one position per keyword, device, and location on a schedule, and can include sites you do not own. + +### Why does my Search Console position not match the rank tracker? + +Because they measure different things. Search Console averages your best position across every impression in the period, so a term that is 3 in one state and 15 elsewhere reports as something in between. A tracker reports the position from one place on one device on one day. A gap of several positions between them is normal. + +### How accurate is Search Console average position? + +It is Google's own count of where your result appeared, so the impressions and clicks are as accurate as any data you will get. The position is accurate as an average; it is not a rank. Filter by device and country and the average becomes much closer to what a searcher sees. + +### How do I check keyword rankings for free? + +Open Search Console, go to Performance, filter to the page or query you care about, and switch on the average position metric. Filter to a single device and country to make the number meaningful. That covers your own site; for a competitor's rankings you need a tool that fetches results, which costs money wherever you do it. + +### Does OpenSEO use credits to read Search Console? + +No. Search Console and URL inspection reads are free in OpenSEO, in the app and through the MCP. Rank tracking checks use credits because they fetch live results, and on the hosted app they require the $10/month plan, which includes $10 of credits. diff --git a/web/content/marketing/library/which-keywords-to-track.mdx b/web/content/marketing/library/which-keywords-to-track.mdx new file mode 100644 index 000000000..992cffadd --- /dev/null +++ b/web/content/marketing/library/which-keywords-to-track.mdx @@ -0,0 +1,123 @@ +--- +title: "Which keywords to track, and how many" +description: "A tracker with 500 keywords in it gets ignored by the second month. Pick 20 to 50 from Search Console, tie each one to a page that earns money, and price the list before you add it." +--- + +## How many keywords should you track? + +Fewer than you want to. A rank tracker is a report you have to read every week, and the number of rows you will read is the number you should track. For most sites that is 20 to 50 keywords. An agency running many clients might track a few hundred per site. Nobody reads 500. + +The instinct to track everything comes from a reasonable place: if the tool can do it, why not? Two reasons. Tracking costs money on every check, so 500 keywords on a daily schedule is a real line item. And a list that long hides the ten terms that matter inside 490 that do not, which is how a tracker ends up as a dashboard nobody opens. + +Charlie Birch, founder of Humanize Collective, makes the underlying point about metrics in general: + +> you can always find a big number to point at, you know, out of context or in a sliver, and be like, yay, big numbers, but like big numbers of what + +![A quote card reading: You can always find a big number to point at, out of context or in a sliver, and be like, yay, big numbers. But big numbers of what? Attributed to Charlie Birch, founder of Humanize Collective, from The Unscripted SEO Interview Podcast.](/library/rank-tracking/quote-big-numbers-of-what.png "Charlie Birch on big numbers") + +A 500-row tracker is a big number of what. Her conversation on where brand strategy meets SEO is on [the Unscripted SEO podcast](https://unscriptedseo.com/charlie-birch-on-where-brand-strategy-meets-seo-what-every-founder-needs-to-know/). + +## Where the list comes from + +Not from a brainstorm, and not from the keyword research tool's volume column. The list comes from Search Console, because Search Console already knows which queries Google shows your site for and how far down. Those are the only terms where a rank change can be read against a click change, which is the point of tracking. + +Here is what that looked like on a real site. Three months of Search Console data for seoarcade.com, a small SEO consultancy site, came back with 76 clicks across the top 400 queries, 52 of them on the brand name. That is not much, and the impressions told a different story: + +- `seo for home services`: 4,558 impressions, average position 17.3, two clicks +- `keyword forecasting`: 3,694 impressions, position 20.3, one click +- `white label keyword research`: 3,131 impressions, position 1.8, one click +- `local seo for home services`: 2,861 impressions, position 14.3, two clicks +- `seo sops`: 2,503 impressions, position 11.9, two clicks +- `authority link building`: 1,984 impressions, position 13.1, no clicks +- `international seo keyword research`: 1,917 impressions, position 10.1, one click + +Those are the terms worth tracking. Each one has demand Google is already willing to show the site for, an average position within reach of page one, and a page that exists. A position change on any of them will show up in clicks within weeks, which makes the tracker readable: this moved, and here is what it did. + +One caution before you trust the averages. When these 25 terms went into a tracker and it ran a single mobile check from the US, only three of them came back inside the top 40. The Search Console averages were real, but they were blended across desktop, states, and weeks, and the position a phone in one place sees is a different number. That gap is the subject of [Is Search Console a rank tracker?](/library/rank-tracking/search-console-vs-rank-tracker), and it is the reason to track these terms rather than assume them. + +The third row is the odd one. Position 1.8 on 3,131 impressions and one click means the page is ranking for a query it does not satisfy, or the SERP has a feature above it that takes the click. Search Console cannot tell you which. A tracker that records SERP features per keyword can, and that is a reason to track a term you already rank for. + +## The four buckets + +Build the list in four groups and cap each one. + +**Money pages.** Five to fifteen terms, one or two per page that produces leads or revenue. These are the rows the business cares about, whether or not they move this month. + +**Striking distance.** Ten to twenty terms from Search Console at positions 4 to 20 with meaningful impressions. This is where movement happens and where the tracker earns its keep. Re-pull this bucket quarterly, because the terms change. + +**Brand, as a control.** One or two brand terms. They should sit at position 1. If they move, something is wrong with the site or the listing, and you want to know before anything else. + +**A competitor's term.** One or two terms you do not rank for yet but a named competitor does. They cost the same to track and they turn the report from "how are we doing" into "how are we doing against them". + +That is 20 to 40 rows, and every one of them means something to the person reading the report. + +## Clean the list before it goes in + +Search Console's query list carries junk, and it is worth knowing what it looks like so you do not track it. The same seoarcade.com pull included queries like `12371: multiple keyword serps report` and several that were the full text of an AI agent's prompt, complete with markdown headers. Those are tools and agents hitting Google, not people. They will show impressions and never a click. Leave them out. + +Also decide, per keyword, whether case matters. `OpenSEO` and `openseo` return the same results; `Apple` and `apple` may not. OpenSEO lowercases tracked keywords by default and tracks a cased keyword separately if you ask, which is right for brand names and wrong for almost everything else. + +## Price the list before you add it + +OpenSEO estimates cost before any check runs. On the seoarcade.com tracker, 25 keywords, mobile only, checking the top 40 results, came back as 225 credits for a one-off live check and 63 credits per scheduled weekly check, which is 252 credits or about $0.25 a month. The $10 plan includes $10 of credits, so a list of this size uses under 3% of them. Doubling the depth to 100 results, adding desktop, or moving to daily checks each raise the cost in proportion, and the estimate updates before you commit. + +![OpenSEO rank tracker configuration for seoarcade.com: United States, national targeting, mobile only, weekly, top 40 results, with the cost shown before saving as about $0.0025 per keyword per check and about $0.50 a month for 50 keywords](/library/rank-tracking/rank-tracker-cost-estimate-openseo.png "Rank tracker cost estimate in OpenSEO") + +## Run this with the OpenSEO MCP + +Connect Search Console to the project first, because the whole workflow reads from it, and connect the [OpenSEO MCP](/docs/mcp) so an agent can do the pull, the filter, and the add in one pass. + +```text +Using the OpenSEO MCP on project [name]: + +1. Pull Search Console performance for the last 3 months by query, + all rows. Exclude queries containing the brand name, queries + longer than 80 characters, and anything that looks like a tool + ID or a prompt rather than a search. + +2. Keep queries with average position between 4 and 20 and at least + 100 impressions. Sort by impressions. Show me the top 30 with + position, impressions, and clicks. + +3. Pull the same period by query and page, and for each of the 30 + tell me which URL Google is showing. Flag any query where two + URLs both appear. + +4. Call get_rank_tracker for this project. If a tracker exists for + the site's domain, use it; otherwise create one with schedule + weekly, devices mobile, depth 40. + +5. Call estimate_rank_tracker_cost with additionalKeywordCount set + to the number of terms I approve, and show me the per-check and + monthly numbers before adding anything. +``` + +Step 3 is the one people skip. If two of your URLs both show for a query, the tracker will report whichever one Google picked that day, and the row will look volatile when the site is competing with itself. [Cluster keywords into topical hubs](/library/keyword-research/cluster-topical-hubs) covers fixing that. + +## The list is a decision, not a setting + +Every keyword in the tracker is a claim that someone will act on its movement. If you cannot say what you would do differently when a row goes from 14 to 8, or from 8 to 14, it does not belong in the list. That test cuts most first drafts in half and makes what is left worth reading. + +Once the list is in, the next question is what to do with the numbers each week. [The keyword ranking report your CEO will read](/library/rank-tracking/keyword-ranking-report) covers that. + +## Tracking keywords FAQ + +### How many keywords should I track for SEO? + +Twenty to fifty for a typical small or mid-size site: the terms tied to pages that earn money, the striking-distance queries from Search Console, one or two brand terms as a control, and a couple of competitor terms. Track more only if someone will read the extra rows. + +### Which keywords should I track first? + +The ones Search Console already shows your site for at positions 4 to 20 with real impressions. They have demand, they are within reach of page one, and a position change will show up in clicks quickly enough to learn from. + +### Should I track keywords I already rank number one for? + +A few. Brand terms belong in the tracker as an early-warning control, and a term where you rank near the top with a low click-through rate is worth tracking for its SERP features, since a feature above your result explains the missing clicks better than the position does. + +### Does it cost more to track more keywords? + +Yes, in proportion. In OpenSEO the cost of a check is the number of keywords times the number of devices times the depth of results inspected, and scheduled checks run through a cheaper queue than one-off live checks. The app shows an estimate before you add keywords or start a run. On the hosted app, rank checks need the $10/month plan, which includes $10 of credits. + +### How often should I re-pick the keywords? + +Quarterly for the striking-distance bucket, since those queries shift as pages move. The money-page bucket changes only when the business does. diff --git a/web/public/library/rank-tracking/local-rank-grid-cookeville-openseo.png b/web/public/library/rank-tracking/local-rank-grid-cookeville-openseo.png new file mode 100644 index 000000000..ee17ed53f Binary files /dev/null and b/web/public/library/rank-tracking/local-rank-grid-cookeville-openseo.png differ diff --git a/web/public/library/rank-tracking/quote-big-numbers-of-what.png b/web/public/library/rank-tracking/quote-big-numbers-of-what.png new file mode 100644 index 000000000..f61fa65cd Binary files /dev/null and b/web/public/library/rank-tracking/quote-big-numbers-of-what.png differ diff --git a/web/public/library/rank-tracking/quote-not-reporting-insights.png b/web/public/library/rank-tracking/quote-not-reporting-insights.png new file mode 100644 index 000000000..ca8c442fb Binary files /dev/null and b/web/public/library/rank-tracking/quote-not-reporting-insights.png differ diff --git a/web/public/library/rank-tracking/quote-proximity-and-relevance.png b/web/public/library/rank-tracking/quote-proximity-and-relevance.png new file mode 100644 index 000000000..695cc1c56 Binary files /dev/null and b/web/public/library/rank-tracking/quote-proximity-and-relevance.png differ diff --git a/web/public/library/rank-tracking/quote-sixteen-months-search-console.png b/web/public/library/rank-tracking/quote-sixteen-months-search-console.png new file mode 100644 index 000000000..d8fa27ac9 Binary files /dev/null and b/web/public/library/rank-tracking/quote-sixteen-months-search-console.png differ diff --git a/web/public/library/rank-tracking/rank-tracker-cost-estimate-openseo.png b/web/public/library/rank-tracking/rank-tracker-cost-estimate-openseo.png new file mode 100644 index 000000000..1aabe1daf Binary files /dev/null and b/web/public/library/rank-tracking/rank-tracker-cost-estimate-openseo.png differ diff --git a/web/public/library/rank-tracking/rank-tracker-positions-openseo.png b/web/public/library/rank-tracking/rank-tracker-positions-openseo.png new file mode 100644 index 000000000..e85cddeb9 Binary files /dev/null and b/web/public/library/rank-tracking/rank-tracker-positions-openseo.png differ diff --git a/web/public/library/rank-tracking/search-console-clicks-by-page-openseo.png b/web/public/library/rank-tracking/search-console-clicks-by-page-openseo.png new file mode 100644 index 000000000..5fcec78bb Binary files /dev/null and b/web/public/library/rank-tracking/search-console-clicks-by-page-openseo.png differ diff --git a/web/src/lib/feature-pages.ts b/web/src/lib/feature-pages.ts index ad752586a..1fde83c23 100644 --- a/web/src/lib/feature-pages.ts +++ b/web/src/lib/feature-pages.ts @@ -535,6 +535,40 @@ export const featurePages = { "Start with keywords tied to important pages, active content work, and competitor opportunities discovered in keyword research.", }, ], + guides: { + title: "The Rank Tracking Strategy Library", + description: + "Practitioner strategies for tracking what matters and reporting it so it gets read. Each guide includes a full walkthrough and a copy-paste MCP prompt.", + items: [ + { + label: "Which keywords to track, and how many", + description: + "Twenty to fifty terms from Search Console, priced before they go in.", + href: "/library/rank-tracking/which-keywords-to-track", + }, + { + label: "Is Search Console a rank tracker?", + description: + "What the free average position hides, and when it is enough.", + href: "/library/rank-tracking/search-console-vs-rank-tracker", + }, + { + label: "Local rank tracking", + description: "Why a local business needs a grid before a tracker.", + href: "/library/rank-tracking/local-rank-tracking", + }, + { + label: "The keyword ranking report your CEO will read", + description: + "Lead with the business number and use rankings to explain it.", + href: "/library/rank-tracking/keyword-ranking-report", + }, + ], + cta: { + label: "Browse all rank tracking strategies", + href: "/library/rank-tracking", + }, + }, }, savedKeywords: { slug: FEATURE_PAGE_SLUGS.savedKeywords, diff --git a/web/src/lib/strategy-libraries.ts b/web/src/lib/strategy-libraries.ts index f02eab337..7dc4ac47f 100644 --- a/web/src/lib/strategy-libraries.ts +++ b/web/src/lib/strategy-libraries.ts @@ -112,6 +112,38 @@ export const siteAuditStrategies: StrategyLibraryItem[] = [ }, ]; +export const RANK_TRACKING_LIBRARY = { + name: "Rank Tracking", + path: "/library/rank-tracking", +}; + +export const rankTrackingStrategies: StrategyLibraryItem[] = [ + { + title: "Which keywords to track, and how many", + description: + "Twenty to fifty terms from Search Console, tied to pages that earn money, priced before they go in. A 500-row tracker is a report nobody reads.", + href: "/library/rank-tracking/which-keywords-to-track", + }, + { + title: "Is Search Console a rank tracker? Where the free data stops", + description: + "Search Console gives an average across searchers and devices, with preliminary recent data. Enough for many sites. How to tell whether yours needs more.", + href: "/library/rank-tracking/search-console-vs-rank-tracker", + }, + { + title: "Local rank tracking: position depends on where the searcher stands", + description: + "Nine points three kilometres apart, four different businesses at number one. Why a local business needs a grid before a tracker.", + href: "/library/rank-tracking/local-rank-tracking", + }, + { + title: "The keyword ranking report your CEO will read", + description: + "Lead with the business number, group movement into four counts, explain three rows, say what happens next. One page, every month.", + href: "/library/rank-tracking/keyword-ranking-report", + }, +]; + export const AI_AGENT_SEO_LIBRARY = { name: "AI-Agent SEO", path: "/library/ai-agent-seo", diff --git a/web/src/routeTree.gen.ts b/web/src/routeTree.gen.ts index 122dde43f..ebea88824 100644 --- a/web/src/routeTree.gen.ts +++ b/web/src/routeTree.gen.ts @@ -42,12 +42,17 @@ import { Route as MarketingFeaturesBacklinkCheckerRouteImport } from './routes/_ import { Route as MarketingFeaturesAiSearchPromptsRouteImport } from './routes/_marketing/features/ai-search-prompts' import { Route as MarketingFeaturesAiBrandVisibilityRouteImport } from './routes/_marketing/features/ai-brand-visibility' import { Route as MarketingLibrarySiteAuditIndexRouteImport } from './routes/_marketing/library/site-audit/index' +import { Route as MarketingLibraryRankTrackingIndexRouteImport } from './routes/_marketing/library/rank-tracking/index' import { Route as MarketingLibraryKeywordResearchIndexRouteImport } from './routes/_marketing/library/keyword-research/index' import { Route as MarketingLibraryCompetitiveAnalysisIndexRouteImport } from './routes/_marketing/library/competitive-analysis/index' import { Route as MarketingLibraryAiAgentSeoIndexRouteImport } from './routes/_marketing/library/ai-agent-seo/index' import { Route as MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRouteImport } from './routes/_marketing/library/site-audit/technical-seo-audit-checklist' import { Route as MarketingLibrarySiteAuditSeoAuditReportTemplateRouteImport } from './routes/_marketing/library/site-audit/seo-audit-report-template' import { Route as MarketingLibrarySiteAuditIndexBloatRouteImport } from './routes/_marketing/library/site-audit/index-bloat' +import { Route as MarketingLibraryRankTrackingWhichKeywordsToTrackRouteImport } from './routes/_marketing/library/rank-tracking/which-keywords-to-track' +import { Route as MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRouteImport } from './routes/_marketing/library/rank-tracking/search-console-vs-rank-tracker' +import { Route as MarketingLibraryRankTrackingLocalRankTrackingRouteImport } from './routes/_marketing/library/rank-tracking/local-rank-tracking' +import { Route as MarketingLibraryRankTrackingKeywordRankingReportRouteImport } from './routes/_marketing/library/rank-tracking/keyword-ranking-report' import { Route as MarketingLibraryKeywordResearchSeedFromConversationRouteImport } from './routes/_marketing/library/keyword-research/seed-from-conversation' import { Route as MarketingLibraryKeywordResearchSearchIntentMappingRouteImport } from './routes/_marketing/library/keyword-research/search-intent-mapping' import { Route as MarketingLibraryKeywordResearchPositioningToDemandRouteImport } from './routes/_marketing/library/keyword-research/positioning-to-demand' @@ -241,6 +246,12 @@ const MarketingLibrarySiteAuditIndexRoute = path: '/library/site-audit/', getParentRoute: () => MarketingRoute, } as any) +const MarketingLibraryRankTrackingIndexRoute = + MarketingLibraryRankTrackingIndexRouteImport.update({ + id: '/library/rank-tracking/', + path: '/library/rank-tracking/', + getParentRoute: () => MarketingRoute, + } as any) const MarketingLibraryKeywordResearchIndexRoute = MarketingLibraryKeywordResearchIndexRouteImport.update({ id: '/library/keyword-research/', @@ -277,6 +288,30 @@ const MarketingLibrarySiteAuditIndexBloatRoute = path: '/library/site-audit/index-bloat', getParentRoute: () => MarketingRoute, } as any) +const MarketingLibraryRankTrackingWhichKeywordsToTrackRoute = + MarketingLibraryRankTrackingWhichKeywordsToTrackRouteImport.update({ + id: '/library/rank-tracking/which-keywords-to-track', + path: '/library/rank-tracking/which-keywords-to-track', + getParentRoute: () => MarketingRoute, + } as any) +const MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute = + MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRouteImport.update({ + id: '/library/rank-tracking/search-console-vs-rank-tracker', + path: '/library/rank-tracking/search-console-vs-rank-tracker', + getParentRoute: () => MarketingRoute, + } as any) +const MarketingLibraryRankTrackingLocalRankTrackingRoute = + MarketingLibraryRankTrackingLocalRankTrackingRouteImport.update({ + id: '/library/rank-tracking/local-rank-tracking', + path: '/library/rank-tracking/local-rank-tracking', + getParentRoute: () => MarketingRoute, + } as any) +const MarketingLibraryRankTrackingKeywordRankingReportRoute = + MarketingLibraryRankTrackingKeywordRankingReportRouteImport.update({ + id: '/library/rank-tracking/keyword-ranking-report', + path: '/library/rank-tracking/keyword-ranking-report', + getParentRoute: () => MarketingRoute, + } as any) const MarketingLibraryKeywordResearchSeedFromConversationRoute = MarketingLibraryKeywordResearchSeedFromConversationRouteImport.update({ id: '/library/keyword-research/seed-from-conversation', @@ -426,12 +461,17 @@ export interface FileRoutesByFullPath { '/library/keyword-research/positioning-to-demand': typeof MarketingLibraryKeywordResearchPositioningToDemandRoute '/library/keyword-research/search-intent-mapping': typeof MarketingLibraryKeywordResearchSearchIntentMappingRoute '/library/keyword-research/seed-from-conversation': typeof MarketingLibraryKeywordResearchSeedFromConversationRoute + '/library/rank-tracking/keyword-ranking-report': typeof MarketingLibraryRankTrackingKeywordRankingReportRoute + '/library/rank-tracking/local-rank-tracking': typeof MarketingLibraryRankTrackingLocalRankTrackingRoute + '/library/rank-tracking/search-console-vs-rank-tracker': typeof MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute + '/library/rank-tracking/which-keywords-to-track': typeof MarketingLibraryRankTrackingWhichKeywordsToTrackRoute '/library/site-audit/index-bloat': typeof MarketingLibrarySiteAuditIndexBloatRoute '/library/site-audit/seo-audit-report-template': typeof MarketingLibrarySiteAuditSeoAuditReportTemplateRoute '/library/site-audit/technical-seo-audit-checklist': typeof MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRoute '/library/ai-agent-seo/': typeof MarketingLibraryAiAgentSeoIndexRoute '/library/competitive-analysis/': typeof MarketingLibraryCompetitiveAnalysisIndexRoute '/library/keyword-research/': typeof MarketingLibraryKeywordResearchIndexRoute + '/library/rank-tracking/': typeof MarketingLibraryRankTrackingIndexRoute '/library/site-audit/': typeof MarketingLibrarySiteAuditIndexRoute } export interface FileRoutesByTo { @@ -482,12 +522,17 @@ export interface FileRoutesByTo { '/library/keyword-research/positioning-to-demand': typeof MarketingLibraryKeywordResearchPositioningToDemandRoute '/library/keyword-research/search-intent-mapping': typeof MarketingLibraryKeywordResearchSearchIntentMappingRoute '/library/keyword-research/seed-from-conversation': typeof MarketingLibraryKeywordResearchSeedFromConversationRoute + '/library/rank-tracking/keyword-ranking-report': typeof MarketingLibraryRankTrackingKeywordRankingReportRoute + '/library/rank-tracking/local-rank-tracking': typeof MarketingLibraryRankTrackingLocalRankTrackingRoute + '/library/rank-tracking/search-console-vs-rank-tracker': typeof MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute + '/library/rank-tracking/which-keywords-to-track': typeof MarketingLibraryRankTrackingWhichKeywordsToTrackRoute '/library/site-audit/index-bloat': typeof MarketingLibrarySiteAuditIndexBloatRoute '/library/site-audit/seo-audit-report-template': typeof MarketingLibrarySiteAuditSeoAuditReportTemplateRoute '/library/site-audit/technical-seo-audit-checklist': typeof MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRoute '/library/ai-agent-seo': typeof MarketingLibraryAiAgentSeoIndexRoute '/library/competitive-analysis': typeof MarketingLibraryCompetitiveAnalysisIndexRoute '/library/keyword-research': typeof MarketingLibraryKeywordResearchIndexRoute + '/library/rank-tracking': typeof MarketingLibraryRankTrackingIndexRoute '/library/site-audit': typeof MarketingLibrarySiteAuditIndexRoute } export interface FileRoutesById { @@ -540,12 +585,17 @@ export interface FileRoutesById { '/_marketing/library/keyword-research/positioning-to-demand': typeof MarketingLibraryKeywordResearchPositioningToDemandRoute '/_marketing/library/keyword-research/search-intent-mapping': typeof MarketingLibraryKeywordResearchSearchIntentMappingRoute '/_marketing/library/keyword-research/seed-from-conversation': typeof MarketingLibraryKeywordResearchSeedFromConversationRoute + '/_marketing/library/rank-tracking/keyword-ranking-report': typeof MarketingLibraryRankTrackingKeywordRankingReportRoute + '/_marketing/library/rank-tracking/local-rank-tracking': typeof MarketingLibraryRankTrackingLocalRankTrackingRoute + '/_marketing/library/rank-tracking/search-console-vs-rank-tracker': typeof MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute + '/_marketing/library/rank-tracking/which-keywords-to-track': typeof MarketingLibraryRankTrackingWhichKeywordsToTrackRoute '/_marketing/library/site-audit/index-bloat': typeof MarketingLibrarySiteAuditIndexBloatRoute '/_marketing/library/site-audit/seo-audit-report-template': typeof MarketingLibrarySiteAuditSeoAuditReportTemplateRoute '/_marketing/library/site-audit/technical-seo-audit-checklist': typeof MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRoute '/_marketing/library/ai-agent-seo/': typeof MarketingLibraryAiAgentSeoIndexRoute '/_marketing/library/competitive-analysis/': typeof MarketingLibraryCompetitiveAnalysisIndexRoute '/_marketing/library/keyword-research/': typeof MarketingLibraryKeywordResearchIndexRoute + '/_marketing/library/rank-tracking/': typeof MarketingLibraryRankTrackingIndexRoute '/_marketing/library/site-audit/': typeof MarketingLibrarySiteAuditIndexRoute } export interface FileRouteTypes { @@ -598,12 +648,17 @@ export interface FileRouteTypes { | '/library/keyword-research/positioning-to-demand' | '/library/keyword-research/search-intent-mapping' | '/library/keyword-research/seed-from-conversation' + | '/library/rank-tracking/keyword-ranking-report' + | '/library/rank-tracking/local-rank-tracking' + | '/library/rank-tracking/search-console-vs-rank-tracker' + | '/library/rank-tracking/which-keywords-to-track' | '/library/site-audit/index-bloat' | '/library/site-audit/seo-audit-report-template' | '/library/site-audit/technical-seo-audit-checklist' | '/library/ai-agent-seo/' | '/library/competitive-analysis/' | '/library/keyword-research/' + | '/library/rank-tracking/' | '/library/site-audit/' fileRoutesByTo: FileRoutesByTo to: @@ -654,12 +709,17 @@ export interface FileRouteTypes { | '/library/keyword-research/positioning-to-demand' | '/library/keyword-research/search-intent-mapping' | '/library/keyword-research/seed-from-conversation' + | '/library/rank-tracking/keyword-ranking-report' + | '/library/rank-tracking/local-rank-tracking' + | '/library/rank-tracking/search-console-vs-rank-tracker' + | '/library/rank-tracking/which-keywords-to-track' | '/library/site-audit/index-bloat' | '/library/site-audit/seo-audit-report-template' | '/library/site-audit/technical-seo-audit-checklist' | '/library/ai-agent-seo' | '/library/competitive-analysis' | '/library/keyword-research' + | '/library/rank-tracking' | '/library/site-audit' id: | '__root__' @@ -711,12 +771,17 @@ export interface FileRouteTypes { | '/_marketing/library/keyword-research/positioning-to-demand' | '/_marketing/library/keyword-research/search-intent-mapping' | '/_marketing/library/keyword-research/seed-from-conversation' + | '/_marketing/library/rank-tracking/keyword-ranking-report' + | '/_marketing/library/rank-tracking/local-rank-tracking' + | '/_marketing/library/rank-tracking/search-console-vs-rank-tracker' + | '/_marketing/library/rank-tracking/which-keywords-to-track' | '/_marketing/library/site-audit/index-bloat' | '/_marketing/library/site-audit/seo-audit-report-template' | '/_marketing/library/site-audit/technical-seo-audit-checklist' | '/_marketing/library/ai-agent-seo/' | '/_marketing/library/competitive-analysis/' | '/_marketing/library/keyword-research/' + | '/_marketing/library/rank-tracking/' | '/_marketing/library/site-audit/' fileRoutesById: FileRoutesById } @@ -969,6 +1034,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MarketingLibrarySiteAuditIndexRouteImport parentRoute: typeof MarketingRoute } + '/_marketing/library/rank-tracking/': { + id: '/_marketing/library/rank-tracking/' + path: '/library/rank-tracking' + fullPath: '/library/rank-tracking/' + preLoaderRoute: typeof MarketingLibraryRankTrackingIndexRouteImport + parentRoute: typeof MarketingRoute + } '/_marketing/library/keyword-research/': { id: '/_marketing/library/keyword-research/' path: '/library/keyword-research' @@ -1011,6 +1083,34 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MarketingLibrarySiteAuditIndexBloatRouteImport parentRoute: typeof MarketingRoute } + '/_marketing/library/rank-tracking/which-keywords-to-track': { + id: '/_marketing/library/rank-tracking/which-keywords-to-track' + path: '/library/rank-tracking/which-keywords-to-track' + fullPath: '/library/rank-tracking/which-keywords-to-track' + preLoaderRoute: typeof MarketingLibraryRankTrackingWhichKeywordsToTrackRouteImport + parentRoute: typeof MarketingRoute + } + '/_marketing/library/rank-tracking/search-console-vs-rank-tracker': { + id: '/_marketing/library/rank-tracking/search-console-vs-rank-tracker' + path: '/library/rank-tracking/search-console-vs-rank-tracker' + fullPath: '/library/rank-tracking/search-console-vs-rank-tracker' + preLoaderRoute: typeof MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRouteImport + parentRoute: typeof MarketingRoute + } + '/_marketing/library/rank-tracking/local-rank-tracking': { + id: '/_marketing/library/rank-tracking/local-rank-tracking' + path: '/library/rank-tracking/local-rank-tracking' + fullPath: '/library/rank-tracking/local-rank-tracking' + preLoaderRoute: typeof MarketingLibraryRankTrackingLocalRankTrackingRouteImport + parentRoute: typeof MarketingRoute + } + '/_marketing/library/rank-tracking/keyword-ranking-report': { + id: '/_marketing/library/rank-tracking/keyword-ranking-report' + path: '/library/rank-tracking/keyword-ranking-report' + fullPath: '/library/rank-tracking/keyword-ranking-report' + preLoaderRoute: typeof MarketingLibraryRankTrackingKeywordRankingReportRouteImport + parentRoute: typeof MarketingRoute + } '/_marketing/library/keyword-research/seed-from-conversation': { id: '/_marketing/library/keyword-research/seed-from-conversation' path: '/library/keyword-research/seed-from-conversation' @@ -1162,12 +1262,17 @@ interface MarketingRouteChildren { MarketingLibraryKeywordResearchPositioningToDemandRoute: typeof MarketingLibraryKeywordResearchPositioningToDemandRoute MarketingLibraryKeywordResearchSearchIntentMappingRoute: typeof MarketingLibraryKeywordResearchSearchIntentMappingRoute MarketingLibraryKeywordResearchSeedFromConversationRoute: typeof MarketingLibraryKeywordResearchSeedFromConversationRoute + MarketingLibraryRankTrackingKeywordRankingReportRoute: typeof MarketingLibraryRankTrackingKeywordRankingReportRoute + MarketingLibraryRankTrackingLocalRankTrackingRoute: typeof MarketingLibraryRankTrackingLocalRankTrackingRoute + MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute: typeof MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute + MarketingLibraryRankTrackingWhichKeywordsToTrackRoute: typeof MarketingLibraryRankTrackingWhichKeywordsToTrackRoute MarketingLibrarySiteAuditIndexBloatRoute: typeof MarketingLibrarySiteAuditIndexBloatRoute MarketingLibrarySiteAuditSeoAuditReportTemplateRoute: typeof MarketingLibrarySiteAuditSeoAuditReportTemplateRoute MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRoute: typeof MarketingLibrarySiteAuditTechnicalSeoAuditChecklistRoute MarketingLibraryAiAgentSeoIndexRoute: typeof MarketingLibraryAiAgentSeoIndexRoute MarketingLibraryCompetitiveAnalysisIndexRoute: typeof MarketingLibraryCompetitiveAnalysisIndexRoute MarketingLibraryKeywordResearchIndexRoute: typeof MarketingLibraryKeywordResearchIndexRoute + MarketingLibraryRankTrackingIndexRoute: typeof MarketingLibraryRankTrackingIndexRoute MarketingLibrarySiteAuditIndexRoute: typeof MarketingLibrarySiteAuditIndexRoute } @@ -1224,6 +1329,14 @@ const MarketingRouteChildren: MarketingRouteChildren = { MarketingLibraryKeywordResearchSearchIntentMappingRoute, MarketingLibraryKeywordResearchSeedFromConversationRoute: MarketingLibraryKeywordResearchSeedFromConversationRoute, + MarketingLibraryRankTrackingKeywordRankingReportRoute: + MarketingLibraryRankTrackingKeywordRankingReportRoute, + MarketingLibraryRankTrackingLocalRankTrackingRoute: + MarketingLibraryRankTrackingLocalRankTrackingRoute, + MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute: + MarketingLibraryRankTrackingSearchConsoleVsRankTrackerRoute, + MarketingLibraryRankTrackingWhichKeywordsToTrackRoute: + MarketingLibraryRankTrackingWhichKeywordsToTrackRoute, MarketingLibrarySiteAuditIndexBloatRoute: MarketingLibrarySiteAuditIndexBloatRoute, MarketingLibrarySiteAuditSeoAuditReportTemplateRoute: @@ -1235,6 +1348,8 @@ const MarketingRouteChildren: MarketingRouteChildren = { MarketingLibraryCompetitiveAnalysisIndexRoute, MarketingLibraryKeywordResearchIndexRoute: MarketingLibraryKeywordResearchIndexRoute, + MarketingLibraryRankTrackingIndexRoute: + MarketingLibraryRankTrackingIndexRoute, MarketingLibrarySiteAuditIndexRoute: MarketingLibrarySiteAuditIndexRoute, } diff --git a/web/src/routes/_marketing/library/index.tsx b/web/src/routes/_marketing/library/index.tsx index eeb94c398..f92f1f0a3 100644 --- a/web/src/routes/_marketing/library/index.tsx +++ b/web/src/routes/_marketing/library/index.tsx @@ -4,16 +4,18 @@ import { aiAgentSeoStrategies, competitiveAnalysisStrategies, keywordResearchStrategies, + rankTrackingStrategies, siteAuditStrategies, } from "@/lib/strategy-libraries"; const PATH = "/library"; const description = - "Browse practical SEO strategies for finding search demand, sizing up competitors, auditing a site, running SEO through an AI agent, mapping intent, and planning pages."; + "Browse practical SEO strategies for finding search demand, sizing up competitors, auditing a site, tracking rankings, running SEO through an AI agent, mapping intent, and planning pages."; const featuredStrategies = [ ...keywordResearchStrategies.slice(0, 2), ...competitiveAnalysisStrategies.slice(0, 1), ...siteAuditStrategies.slice(0, 1), + ...rankTrackingStrategies.slice(0, 1), ...aiAgentSeoStrategies.slice(0, 1), ]; @@ -99,6 +101,23 @@ function StrategyLibraryIndexPage() {

+ +

+ Rank Tracking +

+

+ Pick the keywords worth watching, know where Search Console stops, + track local positions from where customers stand, and write the + ranking report that gets read. +

+

+ View all {rankTrackingStrategies.length} strategies{" "} + +

+
({ + "@type": "Question", + name: faq.question, + acceptedAnswer: { "@type": "Answer", text: faq.answer }, + })), +}; + +const breadcrumbLd = buildBreadcrumbJsonLd([ + { name: "Strategy Library", path: "/library" }, + { name: "Rank Tracking", path: PATH }, +]); + +export const Route = createFileRoute("/_marketing/library/rank-tracking/")({ + head: () => + buildPageSeo({ + title: "Rank Tracking: The Strategy Library", + description: + "Four rank tracking strategies for people who report SEO upward without an SEO budget: pick the keywords, know where Search Console stops, track local positions properly, and write the report that gets read. Each includes a workflow and an OpenSEO MCP prompt.", + path: PATH, + titleSuffix: "OpenSEO", + }), + component: RankTrackingLibraryPage, +}); + +function RankTrackingLibraryPage() { + return ( +
+
+ +

+ The Rank Tracking Strategy Library +

+

+ Four strategies for the person who has to show someone a number every + month: which keywords to track and how many, where Search + Console’s free data stops, how to track a local business from + where its customers stand, and how to write the ranking report that + gets read. Each one includes a workflow and a copy-paste OpenSEO MCP + prompt. +

+
+ +
+

+ How do you track keyword rankings without wasting the budget? +

+

+ Start from what Search Console already shows, track only what someone + will read, measure local positions from where customers stand, and + report movement as an explanation for the business number rather than + a table. +

+
+ {rankTrackingStrategies.map((strategy, index) => { + const number = String(index + 1).padStart(2, "0"); + return ( + + + {number} + +

+ {strategy.title} + +

+

+ {strategy.description} +

+
+ ); + })} +
+
+ +
+

+ Why rank tracking gets bought and then ignored +

+

+ A rank tracker is the easiest SEO tool to justify and the easiest to + stop reading. It gets bought because positions are the number everyone + understands. It gets ignored because a table of 300 keywords with + green and red arrows does not tell the person paying for it whether + anything happened that matters, and after a couple of months they stop + opening it. +

+

+ The fix is upstream of the tool. Track a short list chosen from Search + Console, so every row is a query Google already shows your site for + and a page that earns something. Then report positions as the + explanation for what clicks did, not as the headline. The four + strategies above are that sequence: choose, measure, measure locally + if you are local, report. +

+
+ +
+

+ What OpenSEO rank tracking checks +

+

+ A tracker is a domain, a location, and a language. You choose the + devices (mobile, desktop, or both; mobile by default), how deep to + look (the top 10 to 100 results, 40 by default), and a schedule + (manual, daily, weekly, or monthly). Each check records, per keyword + and device, the position, the previous position, the URL that ranked, + and the SERP features on the page. A tracker holds up to 1,000 + keywords, and a project can hold up to 500 trackers, which is how you + track several locations for one business. +

+

+ Creating a tracker and adding keywords cost nothing. Checks cost + credits, and the app shows an estimate before every scheduled add and + every live run. Scheduled checks go through a queue that is much + cheaper per keyword than a one-off live check. The keyword count, + devices, depth, and schedule multiply, so 100 keywords on mobile at + depth 40, weekly, is about a dollar a month; both devices doubles it + and daily checks multiply it by about seven. +

+

+ On the hosted app, rank checks need the $10/month plan, which includes + $10 of credits each month; a free account can build a tracker but its + checks will not run. Self-hosted deployments are not gated and pay + their data provider directly. Search Console reads, which the + strategies here lean on, use no credits on either. +

+

+ You can run all of it from the{" "} + + rank tracking + {" "} + page, or through the{" "} + + OpenSEO MCP + + , which exposes the tracker to an AI assistant: create it, estimate + the cost, add and remove keywords, run a check, and read the latest + positions, alongside Search Console and the local rank grid in the + same conversation. +

+
+ +
+

+ Where rank tracking ends +

+

+ Three things a tracker cannot tell you, and where to get them. +

+ +
+ +
+

+ Rank tracking FAQ +

+
+ {faqs.map((faq) => ( +
+

+ {faq.question} +

+

+ {faq.answer} +

+
+ ))} +
+
+ +
+
+

+ Track rankings with your own agent +

+

+ Each strategy ends with a copy-paste MCP prompt. OpenSEO is open + source, free to start, and does not require a credit card. +

+
+ + Start with OpenSEO + + +
+ +