SEO Forecasting
Traffic forecasting that survives finance review. Revenue projection from organic search. Building business cases with ranges and confidence intervals. Setting timelines that don't over-promise and don't under-deliver.
The fastest way to lose a CFO’s trust is to forecast a 300% organic traffic lift in twelve months and miss it by 80%. The slowest way is to forecast nothing and let the business decide your budget by gut feel. SEO forecasting in 2026 is the discipline of producing defensible numbers, with explicit assumptions, confidence intervals, and the dignity to say “I do not know” when you do not.
TL;DR
- Forecast in three scenarios — pessimistic, expected, optimistic — with explicit assumptions for each. A single point estimate is not a forecast; it is a guess.
- Bottom-up forecasts beat top-down for SEO. Project per-keyword or per-cluster opportunity using
MSV * CTR_at_target_position * conversion_rate * AOV, sum to total. Top-down (“we’ll capture 5% of category search demand”) is hand-waving. - Account for the 6-12 month ramp. Organic traffic gains compound on a delay. Forecasts that show linear growth from month 1 are wrong; finance will penalize you when reality undershoots in months 1-4 and you have not pre-warned them.
The mental model
SEO forecasting is like weather forecasting, not like tax accounting. The output is a probability distribution, not a fixed number. A good weather forecaster does not say “rain at 3pm.” They say “70% chance of rain between 2pm and 5pm with 0.5 inches expected.” A good SEO forecaster does not say “we will hit 2.3M organic sessions in 2027.” They say “60% probability of landing between 1.8M and 2.6M, with the central estimate at 2.2M, contingent on shipping 4 of 5 named initiatives.”
This matters because finance teams understand probabilistic forecasts. They live in them — sales pipelines, FX hedging, demand planning are all probabilistic. The thing finance does not trust is a single confident number with no error bars. When you present a range with assumptions, you are speaking their language.
The other half: forecasting is a contract. Once you submit a number, headcount, budget, and quarterly goals will be allocated against it. The discipline is forecasting accurately enough that the business plans around real expectations, not so optimistically that you over-allocate, and not so conservatively that you under-resource the SEO program.
Deep dive: the 2026 reality
The bottom-up forecasting method, step by step.
Step 1: Define the keyword universe. Three sources:
- Existing rankings (GSC). Project lift from current position to target position.
- Keyword gap vs competitors (Ahrefs / Semrush). Project capture rate on currently uncaptured queries.
- New content opportunities (Keyword research). Project ranking probability and traffic from net-new content.
Step 2: Assign target positions and capture probabilities. A keyword you currently rank #18 for has a different lift profile than a keyword you do not rank for at all.
| Current rank | Target rank (12 months) | Capture probability |
|---|---|---|
| 1-3 | 1-2 | 70% |
| 4-10 | 1-5 | 55% |
| 11-20 | 5-10 | 40% |
| 21-50 | 10-20 | 25% |
| 51-100 | 20-50 | 15% |
| Not ranking | 20-50 | 10% |
These probabilities should be calibrated against your own historical data. New domains get lower numbers; established domains in low-competition niches get higher.
Step 3: Apply a CTR curve. Use your own GSC data to derive position-specific CTR. Vendor curves are generic and badly fit specific verticals.
# Pseudocode: derive CTR by integer position from GSC URL Impression export
import pandas as pd
gsc = pd.read_csv("gsc_url_impression_last_90d.csv")
gsc["position_int"] = gsc["position"].round().astype(int)
ctr_by_position = (
gsc.groupby("position_int")
.agg(clicks=("clicks", "sum"), impressions=("impressions", "sum"))
.assign(ctr=lambda d: d["clicks"] / d["impressions"])
.query("impressions >= 1000") # filter low-volume noise
)
Step 4: Account for AI Overviews and AI Mode. In 2026, AIO appears on roughly 30-50% of informational queries in most categories. When AIO appears, the classic organic CTR curve compresses — clicks at position 1 drop ~30-40% because the AI summary absorbs the click. Forecast separately for AIO-likely vs AIO-unlikely query subsets.
| Query class | CTR at #1 (no AIO) | CTR at #1 (with AIO) |
|---|---|---|
| Informational | 28% | 17% |
| Commercial investigation | 25% | 19% |
| Transactional | 26% | 22% |
| Navigational | 45% | 38% |
Step 5: Apply conversion and revenue.
projected_revenue_keyword = MSV * target_CTR * capture_probability * landing_page_CR * AOV
Where:
MSV= monthly search volumetarget_CTR= CTR at projected position, AIO-adjustedcapture_probability= the probability you reach the targetlanding_page_CR= conversion rate of the destination, from GA4AOV= average order value or LTV per conversion
Sum across keywords, sum across clusters, you have a revenue projection.
Step 6: Apply the ramp curve. Organic traffic gains compound. A typical ramp:
| Month | % of steady-state captured |
|---|---|
| 1-3 | 5-15% |
| 4-6 | 25-40% |
| 7-9 | 50-70% |
| 10-12 | 75-90% |
| 13+ | 90-105% (with continued investment) |
Step 7: Build the three scenarios.
| Scenario | Capture probability adjustment | Use for |
|---|---|---|
| Pessimistic (P10) | Multiply baseline by 0.5 | Worst-case planning |
| Expected (P50) | Baseline | Hiring and budget |
| Optimistic (P90) | Multiply baseline by 1.5 | Stretch goals |
The pessimistic case is the one the CFO will plan against. Make it defensible.
Visualizing it
flowchart TD
A[Keyword universe: GSC + gap + net-new] --> B[Assign target rank per keyword]
B --> C[Apply CTR curve from your own GSC]
C --> D[Adjust CTR for AI Overview prevalence]
D --> E[Multiply by capture probability]
E --> F[Multiply by landing page CR and AOV]
F --> G[Apply ramp curve over 12 months]
G --> H[Sum to monthly revenue forecast]
H --> I[Generate P10 P50 P90 scenarios]
I --> J[Business case with assumptions log]
Bad vs. expert
The bad approach
The agency builds a forecast in a slide deck claiming “+200% organic traffic in 12 months” based on an Ahrefs Traffic Potential number divided by 12.
Forecast methodology:
Ahrefs "Traffic Potential" for top 50 keywords: 850,000 monthly visits
Today's organic traffic: 280,000 monthly visits
Therefore: +203% lift over 12 months, ramping linearly.
Month 1: 303,000 sessions (+23,000)
Month 2: 326,000 sessions
...
Month 12: 850,000 sessions
This fails for five reasons. There is no capture probability — Ahrefs Traffic Potential assumes you take #1 across the entire bucket. There is no AIO adjustment, in a category where AIO appears on 60% of queries. There is no ramp curve — months 1-4 will undershoot a linear projection by 50-70%. There is no conversion or revenue tie. And there is no scenario range, so when reality lands at +60% the forecast is 4x off.
The expert approach
A bottom-up model in a spreadsheet (or Python notebook), with explicit assumptions, three scenarios, and a confidence band over time.
import pandas as pd
import numpy as np
# Per-keyword model
kw = pd.read_csv("keyword_universe.csv") # cols: keyword, msv, current_rank, cluster, aio_likely, lp_cr, aov
# Calibrated target positions and capture probabilities by current rank bucket
def target_and_prob(rank):
if pd.isna(rank): return (35, 0.10)
if rank <= 3: return (2, 0.70)
if rank <= 10: return (5, 0.55)
if rank <= 20: return (10, 0.40)
if rank <= 50: return (20, 0.25)
return (35, 0.15)
kw[["target_rank", "p_capture"]] = kw["current_rank"].apply(
lambda r: pd.Series(target_and_prob(r))
)
# CTR table from your own GSC, AIO-adjusted
ctr_no_aio = {1:0.282, 2:0.156, 3:0.106, 4:0.080, 5:0.062,
10:0.024, 20:0.012, 35:0.005, 50:0.002}
ctr_aio = {1:0.170, 2:0.099, 3:0.069, 4:0.054, 5:0.043,
10:0.018, 20:0.009, 35:0.004, 50:0.0015}
def ctr_lookup(rank, aio):
table = ctr_aio if aio else ctr_no_aio
closest = min(table.keys(), key=lambda k: abs(k - rank))
return table[closest]
kw["target_ctr"] = kw.apply(
lambda r: ctr_lookup(r.target_rank, r.aio_likely), axis=1
)
# Steady-state monthly clicks per keyword (expected scenario)
kw["expected_clicks"] = kw["msv"] * kw["target_ctr"] * kw["p_capture"]
kw["expected_revenue"] = kw["expected_clicks"] * kw["lp_cr"] * kw["aov"]
# Apply ramp curve: month 1 = 8%, month 12 = 85%
ramp = {1:0.08, 2:0.12, 3:0.18, 4:0.27, 5:0.38, 6:0.48,
7:0.58, 8:0.68, 9:0.75, 10:0.80, 11:0.83, 12:0.85}
forecast = pd.DataFrame({
"month": list(ramp.keys()),
"expected_clicks": [kw["expected_clicks"].sum() * r for r in ramp.values()],
"expected_revenue": [kw["expected_revenue"].sum() * r for r in ramp.values()],
})
forecast["pessimistic_revenue"] = forecast["expected_revenue"] * 0.5
forecast["optimistic_revenue"] = forecast["expected_revenue"] * 1.5
print(forecast)
This works because every assumption is explicit and changeable, the AIO adjustment reflects the 2026 SERP reality, the ramp curve matches how SEO actually performs, and the three scenarios give finance the risk-adjusted view they expect from any forecast.
Do this today
- Pull your last 90 days of GSC URL Impression data with dimensions
query, page, position, clicks, impressions. Compute your own CTR-by-position curve from this — vendor curves are wrong for your vertical. - Define your keyword universe in a single CSV: existing ranking keywords (from GSC), gap keywords (from Ahrefs/Semrush Keyword Gap), and net-new opportunity keywords (from research). Tag by cluster and AIO-likelihood (informational queries are usually AIO-likely).
- Assign target rank and capture probability to each keyword using the rank-bucket table. If your domain is new or the niche is competitive, halve every probability as a baseline.
- Pull landing page conversion rates and AOV from GA4 for the top destination per cluster. Use the cluster-level CR if individual page data is sparse.
- Compute per-keyword expected clicks and revenue, then sum by cluster and total. This is your steady-state monthly target.
- Apply a 12-month ramp curve (8% in month 1, 85% in month 12 is a sensible default). Generate P10/P50/P90 scenarios by multiplying P50 by 0.5 and 1.5 respectively.
- Build the forecast as a Google Sheets model with named ranges for each assumption:
ctr_curve,capture_probabilities,aio_adjustment,ramp_curve. Lock the formulas; expose only the assumptions for finance to challenge. - Document explicit dependencies: forecast is contingent on shipping initiatives X, Y, Z by month N. If any dependency slips, restate the forecast.
- Backtest your model quarterly. Pull last quarter’s GSC data, compute what your model predicted vs what happened, and calibrate the capture probabilities. After 4 quarters of backtesting your model is meaningfully better than the textbook one.
- Present the forecast in one slide: P10/P50/P90 monthly chart, a sentence on dependencies, a sentence on key assumptions, a date for the next reforecast. Keep the spreadsheet linked for anyone who wants to interrogate the math.
Mark complete
Toggle to remember this module as mastered. Saved to your browser only.
More in this part