Keyword research produces spreadsheets, not decisions
Teams export thousands of keywords and then stall. Which ones share intent? Which are winnable given domain authority? Where are competitors weak? Answering that by hand is slow, subjective, and stale the moment the SERP shifts.
This solution turns raw keyword data into a prioritised plan you can hand straight to writers.
Expand → cluster → analyse SERP → prioritise
- ✓ Intent-based keyword clustering
- ✓ Competitor gap analysis
- ✓ Opportunity & winnability scoring
- ✓ SERP feature detection (PAA, snippets)
- ✓ Cannibalisation warnings
- ✓ Auto-generated content calendar
- ✓ Embeddings for semantic clustering
- ✓ Claude for intent labelling & briefs
- ✓ Keyword & SERP data APIs
- ✓ Vector store for topic maps
- ✓ Sheets / Notion / Airtable export
- ✓ SEO Content Engine (briefs)
- ✓ Social Media Autopilot (topics)
- ✓ Ad Copy Optimizer (angles)
- ✓ Weekly refresh as SERPs move
Semantic intent clustering
Group thousands of keywords by what the searcher actually wants, then score each cluster for opportunity.
import numpy as np from sklearn.cluster import AgglomerativeClustering from embed import embed_batch # -> unit-norm vectors def cluster_by_intent(keywords: list[str], threshold: float = 0.35): vecs = embed_batch(keywords) # semantic, not string, match model = AgglomerativeClustering( n_clusters=None, metric="cosine", linkage="average", distance_threshold=threshold, ) labels = model.fit_predict(vecs) clusters: dict[int, list] = {} for kw, label in zip(keywords, labels): clusters.setdefault(int(label), []).append(kw) return clusters def opportunity_score(volume, difficulty, our_da) -> float: # reward reachable, high-volume clusters; punish out-of-reach ones reach = max(0.0, (our_da - difficulty) / 100) return np.log1p(volume) * reach
Turn keyword data into a plan
Standalone, or paired with the Content Engine for research-to-publish in one flow.