Appearance
SOP: Keyword Research Pipeline Fresh
This SOP covers the full cycle: seed keywords in, prioritized list out. Uses two API sections — Keywords Data for volume data and DataForSEO Labs for discovery.
Research Flow
flowchart TD
A[Seed keywords from client brief] --> B[Keyword Ideas via Labs API]
B --> C[Related Keywords via Labs API]
A --> D[Search Volume via Keywords Data API]
C --> D
D --> E[Filter by volume + difficulty]
E --> F[Group by intent]
F --> G[Priority keyword list]
G --> H[Export to tracking / content plan]Step 1: Get Search Volume for Known Keywords
When you already have keywords and need to validate them with volume data, use the Keywords Data API.
python
import os
import requests
from requests.auth import HTTPBasicAuth
auth = HTTPBasicAuth(
os.environ["DATAFORSEO_LOGIN"],
os.environ["DATAFORSEO_PASSWORD"]
)
# Your seed / known keywords
seed_keywords = [
"roofing contractor miami",
"roof repair miami",
"metal roof installation miami",
"flat roof repair miami",
"emergency roofing miami"
]
payload = [
{
"keywords": seed_keywords,
"location_code": 2840, # United States
"language_code": "en",
"date_from": "2024-01-01",
"date_to": "2024-12-31"
}
]
response = requests.post(
"https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live",
auth=auth,
json=payload
)
data = response.json()
task = data["tasks"][0]
volume_data = {}
for item in task["result"]:
keyword = item["keyword"]
avg_volume = item.get("search_volume", 0)
competition = item.get("competition", "unknown")
cpc = item.get("cpc", 0)
volume_data[keyword] = {
"volume": avg_volume,
"competition": competition,
"cpc": cpc,
"monthly_searches": item.get("monthly_searches", [])
}
print(f"{keyword}: {avg_volume}/mo | CPC ${cpc:.2f} | Competition: {competition}")Step 2: Discover New Keywords via Labs API
Use keywords_for_keywords to expand your list. This is the main keyword discovery endpoint.
python
def get_keyword_ideas(auth, seed_keywords, location_code=2840, limit=100):
"""Get keyword ideas based on seed keywords."""
payload = [
{
"keywords": seed_keywords[:5], # Labs accepts up to 5 seeds per call
"location_code": location_code,
"language_code": "en",
"include_serp_info": True,
"include_seed_keyword": False,
"filters": [
["keyword_info.search_volume", ">", 100] # min 100 searches/mo
],
"order_by": ["keyword_info.search_volume,desc"],
"limit": limit
}
]
response = requests.post(
"https://api.dataforseo.com/v3/dataforseo_labs/google/keywords_for_keywords/live",
auth=auth,
json=payload
)
data = response.json()
task = data["tasks"][0]
ideas = []
for item in task["result"][0].get("items", []):
ideas.append({
"keyword": item["keyword"],
"volume": item["keyword_info"].get("search_volume", 0),
"difficulty": item.get("keyword_properties", {}).get("keyword_difficulty", 0),
"cpc": item["keyword_info"].get("cpc", 0),
"intent": item.get("search_intent_info", {}).get("main_intent", "unknown")
})
return ideas
ideas = get_keyword_ideas(auth, seed_keywords)
print(f"Found {len(ideas)} keyword ideas")Step 3: Get Related Keywords
Use related_keywords to find semantically related terms — good for topic clustering.
python
def get_related_keywords(auth, keyword, location_code=2840, limit=50):
"""Get related keywords for a single target keyword."""
payload = [
{
"keyword": keyword,
"location_code": location_code,
"language_code": "en",
"limit": limit,
"filters": [
["keyword_info.search_volume", ">", 50]
],
"order_by": ["keyword_info.search_volume,desc"]
}
]
response = requests.post(
"https://api.dataforseo.com/v3/dataforseo_labs/google/related_keywords/live",
auth=auth,
json=payload
)
data = response.json()
task = data["tasks"][0]
related = []
for item in task["result"][0].get("items", []):
related.append({
"keyword": item["keyword_data"]["keyword"],
"volume": item["keyword_data"]["keyword_info"].get("search_volume", 0),
"difficulty": item["keyword_data"].get("keyword_properties", {}).get("keyword_difficulty", 0)
})
return related
# Get related keywords for your top seed
related = get_related_keywords(auth, "roofing contractor miami")Step 4: Filter and Prioritize
Apply business rules to trim the raw list to actionable targets.
python
def prioritize_keywords(all_keywords, min_volume=100, max_difficulty=60):
"""
Filter and score keywords.
Scoring formula:
- High volume + low difficulty = best opportunity
- Score = volume / (difficulty + 1)
"""
filtered = [
kw for kw in all_keywords
if kw["volume"] >= min_volume
and kw["difficulty"] <= max_difficulty
]
# Calculate opportunity score
for kw in filtered:
difficulty = kw["difficulty"] or 1
kw["opportunity_score"] = round(kw["volume"] / difficulty, 1)
# Sort by opportunity score descending
filtered.sort(key=lambda x: x["opportunity_score"], reverse=True)
return filtered
# Merge all keyword sources
all_keywords = list(volume_data.values()) # from step 1
all_keywords.extend(ideas) # from step 2
all_keywords.extend(related) # from step 3
# Deduplicate by keyword text
seen = set()
unique_keywords = []
for kw in all_keywords:
key = kw.get("keyword", kw.get("kw", "")).lower().strip()
if key and key not in seen:
seen.add(key)
unique_keywords.append(kw)
prioritized = prioritize_keywords(unique_keywords)
# Print top 20
print("\nTop 20 Keyword Opportunities:")
print(f"{'Keyword':<45} {'Volume':>8} {'KD':>6} {'Score':>8}")
print("-" * 70)
for kw in prioritized[:20]:
kw_text = kw.get("keyword", "")[:44]
print(f"{kw_text:<45} {kw['volume']:>8,} {kw['difficulty']:>6} {kw['opportunity_score']:>8.1f}")Step 5: Export to CSV
python
import csv
def export_keywords_csv(keywords, filepath="keyword-research.csv"):
"""Export prioritized keywords to CSV."""
fieldnames = ["keyword", "volume", "difficulty", "cpc", "intent", "opportunity_score"]
with open(filepath, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
writer.writeheader()
writer.writerows(keywords)
print(f"Exported {len(keywords)} keywords to {filepath}")
export_keywords_csv(prioritized)Keyword Difficulty Interpretation
| KD Score | Interpretation | Recommended Action |
|---|---|---|
| 0–20 | Very easy | Target immediately |
| 21–40 | Achievable | Build content now |
| 41–60 | Moderate | Good for established sites |
| 61–80 | Hard | Long-term play, need authority |
| 81–100 | Very hard | Avoid unless high-authority domain |
Search Intent Categories
DataForSEO Labs returns intent classifications. Use them to route keywords to the right content type:
| Intent | Meaning | Content Type |
|---|---|---|
informational | User wants to learn | Blog posts, guides, FAQs |
navigational | User wants a specific site | Brand pages |
commercial | User is comparing options | Comparison pages, reviews |
transactional | User wants to buy/hire | Service pages, landing pages |