Every market runs on a demand calendar. “Ski jacket” peaks in the same December week almost every year, “tax deadline” spikes in mid-April, “wedding invitations” climbs from January to June, and “standing desk” surges each January when resolution-makers rebuild their home offices. The pattern repeats with remarkable consistency — yet most content teams publish seasonal pieces when the peak is already visible in their analytics, which is six to twelve weeks too late.
Google Trends is the only free instrument that exposes this calendar directly, with weekly resolution reaching back to 2004. Used correctly, it lets you predict traffic peaks instead of reacting to them, and time your publishing so a page has been indexed, internally linked, and settled into position before demand arrives.
This guide covers what the Trends normalization model actually measures, how to read a seasonality curve without fooling yourself, how to compute a monthly seasonal index with pytrends, and the lead-time rule we apply to every editorial calendar MarketLens builds.
What Google Trends Actually Measures (and What It Hides)
Google Trends does not report search volume. Each series is normalized: Google samples its search logs, computes the query’s share of total search activity in each time bucket and region, then rescales the whole series so its maximum equals 100. Three consequences matter for seasonality work:
- A value of 50 means half of peak interest within your chosen window — not half of any absolute number. Change the date range and every point on the curve is recomputed.
- Rising total search activity deflates flat queries. A term with stable absolute volume drifts slowly downward on the chart as overall search grows, which is easy to misread as decline.
- Low-volume terms get noisy or flatline at zero. Below a few hundred monthly searches, weekly buckets are unreliable. Switch from the literal query string to the Trends “topic” entity, which aggregates spelling variants and translations.
The working rule: Trends gives you shape — when demand rises and falls. Absolute size comes from Google Search Console or a keyword tool. Seasonality analysis only needs shape.
How to Read a Seasonality Curve Correctly
Set the timeframe to five years, never twelve months. One year cannot separate a recurring pattern from a one-off spike, and it hides secular trends entirely. Then work through four steps:
- Overlay the years mentally (or literally, in pandas). A true seasonal query shows the same rise and fall in at least four of the five years. If the “pattern” appears once, it was news, not seasonality.
- Mark the ramp, not the peak. The ramp is the week interest starts climbing from its baseline. The peak is irrelevant for planning — by then the ranking contest is over.
- Check for secular drift underneath the cycle. A query can be seasonal and dying. If each successive peak is lower than the last, the seasonal play has a shrinking ceiling.
- Classify the pattern, because each one implies a different publishing cadence:
| Pattern | Typical Shape | Example Category | Planning Implication |
|---|---|---|---|
| Single annual peak | One spike, long quiet baseline | Tax, holidays, costume ideas | One hard publishing deadline per year |
| Dual peak | Two spikes (e.g., spring + fall) | Gardening, running gear, HVAC | Two refresh cycles; share one evergreen hub |
| Sawtooth / weekly | Repeating intra-week cycle | B2B software, recipes | Publish weekday mornings; seasonality secondary |
| Ramp plateau | Slow climb, long high plateau | Wedding planning, ski season | Publish before the climb; refresh mid-plateau |
The Lead-Time Rule: Rank Before the Ramp, Not Before the Peak
A new page does not rank the week it is published. It has to be crawled and indexed, earn its place in your internal link graph, and survive the position volatility that almost every fresh URL goes through on competitive queries. A page published at ramp start is spending the season doing that work, and tends to reach its settled position only once the peak has passed — the whole year’s harvest forfeited to a few weeks of delay.
So the rule is: schedule publication 8-12 weeks before the ramp month, with the longer end for competitive commercial queries and the shorter end for long-tail informational ones. That buffer absorbs indexing latency, gives you one full Search Console feedback cycle to catch pages stalling in positions 4-20 and fix title and heading mismatches, and leaves room for a pre-ramp internal linking pass from your highest-authority pages.
This is also why a content calendar built from Trends data is constructed backwards from ramp dates, never from peak dates: the ramp minus ten weeks is the real deadline.
Computing a Monthly Seasonal Index with pytrends
The seasonal index turns a wiggly five-year chart into one number per calendar month. Values above 100 are above-average demand months; the first month that crosses 100 heading upward is your ramp month.
from pytrends.request import TrendReq
pytrends = TrendReq(hl="en-US", tz=120)
pytrends.build_payload(["wedding invitations"],
timeframe="today 5-y", geo="US")
df = pytrends.interest_over_time()
df = df[~df["isPartial"]].drop(columns="isPartial")
monthly = df.resample("ME").mean()
seasonal_index = (
monthly.groupby(monthly.index.month).mean()
/ monthly.mean() * 100
).round(1)
print(seasonal_index)Two details in that snippet prevent the most common errors. Dropping isPartial rows removes the current, incomplete bucket — leaving it in makes the latest month look like a collapse in demand. And grouping by calendar month across five years averages out one-off anomalies, so a single viral news cycle cannot masquerade as a recurring season. For spike-prone niches (anything news-adjacent), swap the mean for a median and the index gets even more robust.
Where Seasonality Analysis Goes Wrong
Four traps account for most bad seasonal calls:
- The partial-period trap. The newest week or month in any Trends export is incomplete. It is the single most common cause of a false “demand just crashed” reading, and the
isPartialcolumn exists precisely so you can drop it. - Cross-payload comparison. Trends normalizes each request (up to five terms) against its own maximum. Comparing values across two separate requests is meaningless — put terms in the same payload or rank them within a shared basket.
- Hemisphere and region blindness. “Lawn care” peaks in May for a US audience and in November for an Australian one. Always set
geoto your actual market; a worldwide series blends opposing seasons into mush. - Mistaking decline for seasonality. A downward staircase is not a cycle. If the five-year trendline falls while the annual wiggle persists, treat the keyword as a harvest-while-declining play, not a growth investment.
From Curve to Calendar
The end product of this analysis is not a chart — it is a publishing queue with dates. For each target query, record the ramp month, subtract ten weeks, and that is the deadline for the article to be live, linked, and indexed. Before committing, run one cross-platform check: some topics ramp earlier on YouTube than in web search because audiences research visually before they buy, so compare gprop='youtube' against the default web property for anything demonstrable. Then slot each piece into the broader roadmap alongside your evergreen priorities, as described in building a content strategy from Google Trends data.
Start tonight with your ten highest-value keywords: pull five years each, compute the indexes, and put the ramp-minus-ten-weeks dates in your editorial calendar. If you want the full demand calendar built and cross-referenced against your GA4 and Search Console data, that analysis is part of the MarketLens Premium audit — but the pytrends loop above is free, and it is the 20% that delivers most of the value.
MarketLens