Skip to main content

Pytrends keyword research: 429 ceiling at 12 queries

Wanted Google Trends to help pick the next two blog posts. pytrends installed clean. Script wrote in a few minutes. First run: all four batches failed identically with a urllib3 TypeError that the error message doesn’t explain. Patched it. Second run: three batches succeeded, the fourth got 429’d on the thirteenth HTTP request. And the data we did get flipped two of our prior SEO recommendations — one of which we’d been planning to write a post around.

What I ran

Invoked a project-local pytrends-seo skill (the SKILL.md lives in .claude/skills/pytrends-seo/ — gitignored, project-private). The skill wraps four common pytrends use cases: comparison via interest_over_time(), long-tail discovery via related_queries(), seasonality, and regional comparison.

Four-batch plan, scoped to gaps identified in an internal SEO findings memo from launch day:

  • Batch A (wiki verticals): claude code hooks, claude code skills, claude code mcp, claude code subagents, claude code agent
  • Batch B (MCP-specific): mcp server, model context protocol, claude mcp, mcp claude code
  • Batch C (marketing/CRO): claude code marketing, claude code cro, claude code copywriting
  • Batch D (competitive): cursor vs claude code, claude code vs copilot, claude code review

Defaults: hl='en-US', tz=0, geo='US', timeframe='today 12-m', SLEEP=10 between calls. JSON outputs go to data/.seo-research/<slug>.json (gitignored — research is local, doesn’t belong in the repo).

What happened

Run 1 — all four batches died on the same line

[batch-a-wiki-verticals] ERROR: TypeError: Retry.__init__() got an unexpected keyword argument 'method_whitelist'
[batch-b-mcp-specific]   ERROR: TypeError: Retry.__init__() got an unexpected keyword argument 'method_whitelist'
[batch-c-marketing-cro]  ERROR: TypeError: Retry.__init__() got an unexpected keyword argument 'method_whitelist'
[batch-d-competitive]    ERROR: TypeError: Retry.__init__() got an unexpected keyword argument 'method_whitelist'

Root cause: pytrends 4.9.2 (the latest released version) passes the deprecated method_whitelist= keyword to urllib3.util.Retry. urllib3 ≥2.0 renamed it to allowed_methods= and removed the old name entirely. The error message names the parameter but doesn’t explain the version mismatch.

Fix is a six-line monkey-patch shim placed before the from pytrends.request import TrendReq import:

from urllib3.util import Retry as _Retry
_orig_retry_init = _Retry.__init__
def _patched_retry_init(self, *args, **kwargs):
    if 'method_whitelist' in kwargs:
        kwargs.setdefault('allowed_methods', kwargs.pop('method_whitelist'))
    return _orig_retry_init(self, *args, **kwargs)
_Retry.__init__ = _patched_retry_init

The alternative is pinning urllib3<2.0 in the venv, which risks breaking other tools that already moved to urllib3 2.x. The shim is non-invasive — keep it until pytrends ships a fix.

Run 2 — three batches landed, the fourth hit 429 on the thirteenth call

After the shim, Batches A, B, and C completed cleanly with 53 weeks of interest_over_time data each plus related_queries() dicts. Batch D failed on its final HTTP roundtrip:

RetryError: HTTPSConnectionPool(host='trends.google.com', port=443):
Max retries exceeded ... (Caused by ResponseError('too many 429 error responses'))

The skill notes claimed ~1400 consecutive queries within 4h triggers a block. Today’s ceiling was tighter by two orders of magnitude: twelve HTTP requests across ~2 minutes of sustained activity from one IP. Batch D had completed build_payload and interest_over_time — the 429 fired on the related_queries() call that came thirteenth.

The 1400/4h figure is a ceiling under fresh-IP conditions. The working budget is much smaller when your IP has any recent Trends traffic. Plan in single-batch sessions if you need to run more than ~10 queries.

The data that survived the run

Three batches’ worth of interest_over_time and related_queries, 12-month mean per keyword:

KeywordMean (0-100)Batch
mcp server47.1B
claude code mcp35.4A
claude code agent30.5A
claude code marketing29.9C
claude code skills21.6A
claude mcp21.8B
model context protocol16.1B
mcp claude code10.1B
claude code hooks7.1A
claude code subagents5.1A
claude code cro0.1C
claude code copywriting0.0C

Rising queries (breakout, 1M%+ growth) clustered around two themes: “best claude code skills” listicle intent (best claude skills +2,109,250%, best claude code skills +2,014,600%) and specific MCP tool integrations (playwright mcp claude code +412,300%, figma mcp +411,550%, chrome devtools mcp server +25,700%, sentry mcp server +21,900%).

Where it drifted — two findings flipped the editorial plan

The launch-day SEO memo had recommended two specific next posts based on SERP gap analysis (no demand data). Both got flipped by the actual numbers.

Finding 1: CRO is uncontested in SERPs because demand is near-zero. The memo flagged “Claude Code CRO” as a “nearly uncontested keyword” based on the empty page-one SERP. Trends shows claude code cro = 0.1 mean, claude code copywriting = 0.0. The uncontested-keyword + zero-searcher combination is not an opportunity — it’s a graveyard. There’s no traffic to win because no one is searching. Revised pick: write under claude code marketing (29.9 mean) instead, where there are actually searchers.

Finding 2: Hooks are lower-demand than the memo assumed. The memo recommended a hooks case study as the next post. claude code hooks is only 7.1 mean — 3-5× less than MCP, skills, or agents (35.4, 21.6, 30.5 respectively). Hooks posts still work as depth-of-engagement plays for practitioners who land via direct or referrer traffic. But they won’t be traffic engines via search.

What I’d change

Add the urllib3 shim to the skill’s quickstart block. Anyone running pytrends 4.9.2 against urllib3 2.x today fails on call one with no diagnostic hint. The skill should ship with the shim inline (or pin urllib3<2.0 in setup notes) so the first run works.

Bound the batch size by IP-traffic history, not the 1400/4h docs figure. For an IP with any recent Trends activity, plan ~10 queries per session and put 60-second sleeps between calls instead of 10. Spread larger sweeps across multiple sessions across multiple hours.

Validate SERP-gap hypotheses with demand data before committing to a post topic. The CRO finding is the cautionary tale. SERP gap analysis tells you what’s not there. Trends tells you whether anyone is looking for it. Both signals matter. Either alone is a coin flip.