An SEO keyword gap finder searches a list of target keywords, finds which ones competitors rank for in the top 10 while your domain does not appear, and outputs the gaps as actionable content opportunities.
Prerequisites
- Python 3.9+
- Scavio API key
Walkthrough
Step 1: Define your domain and competitors
Set up the domains you want to compare.
YOUR_DOMAIN = "yoursite.com"
COMPETITOR_DOMAINS = ["competitor1.com", "competitor2.com", "competitor3.com"]
KEYWORDS = [
"project management software",
"task tracker for teams",
"agile board online",
"kanban tool free",
"sprint planning tool"
]Step 2: Search each keyword and extract positions
For each keyword, check if your domain and competitors appear in the top 20 results.
import requests
API_KEY = "your-scavio-api-key"
def get_positions(keyword: str, domains: list) -> dict:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": keyword, "num_results": 20},
headers={"x-api-key": API_KEY},
timeout=15
)
r.raise_for_status()
results = r.json().get("organic_results", [])
positions = {}
for domain in domains:
pos = next((i + 1 for i, res in enumerate(results) if domain in res.get("link","")), None)
positions[domain] = pos
return positionsStep 3: Identify keyword gaps
A gap is a keyword where at least one competitor ranks in the top 10 but your domain does not.
def find_gaps(keywords: list, your_domain: str, competitor_domains: list) -> list:
all_domains = [your_domain] + competitor_domains
gaps = []
for kw in keywords:
positions = get_positions(kw, all_domains)
your_pos = positions.get(your_domain)
competitor_positions = {d: positions.get(d) for d in competitor_domains}
top10_competitors = [d for d, p in competitor_positions.items() if p and p <= 10]
if top10_competitors and (not your_pos or your_pos > 10):
gaps.append({
"keyword": kw,
"your_position": your_pos or "not ranking",
"competitors_ranking": {d: competitor_positions[d] for d in top10_competitors}
})
return gaps
gaps = find_gaps(KEYWORDS, YOUR_DOMAIN, COMPETITOR_DOMAINS)
for g in gaps:
print(f"GAP: {g['keyword']} | You: {g['your_position']} | {g['competitors_ranking']}")Python Example
import requests
from datetime import date
API_KEY = "your-scavio-api-key"
YOUR_DOMAIN = "yoursite.com"
COMPETITORS = ["asana.com", "monday.com", "clickup.com"]
KEYWORDS = [
"project management software", "task tracker for teams",
"agile board online", "kanban tool free",
"sprint planning tool", "team task management"
]
def get_positions(keyword, domains):
r = requests.post("https://api.scavio.dev/api/v1/search",
json={"query": keyword, "num_results": 20},
headers={"x-api-key": API_KEY}, timeout=15)
r.raise_for_status()
results = r.json().get("organic_results", [])
return {d: next((i+1 for i, res in enumerate(results) if d in res.get("link","")), None)
for d in domains}
def find_gaps(keywords, your_domain, competitors):
all_domains = [your_domain] + competitors
gaps, covered = [], []
for kw in keywords:
pos = get_positions(kw, all_domains)
your_pos = pos[your_domain]
comp_top10 = {d: pos[d] for d in competitors if pos.get(d) and pos[d] <= 10}
if comp_top10 and (not your_pos or your_pos > 10):
gaps.append({"keyword": kw, "your_position": your_pos or "NR",
"gap_competitors": comp_top10})
elif your_pos and your_pos <= 10:
covered.append({"keyword": kw, "position": your_pos})
return {"gaps": gaps, "covered": covered}
if __name__ == "__main__":
result = find_gaps(KEYWORDS, YOUR_DOMAIN, COMPETITORS)
print(f"Keyword Gap Analysis — {YOUR_DOMAIN} — {date.today()}")
print(f"\nGAPS ({len(result['gaps'])} keywords — content opportunities):")
for g in result["gaps"]:
comp_str = ", ".join(f"{d}@{p}" for d, p in g["gap_competitors"].items())
print(f" {g['keyword']:<35} | You: {str(g['your_position']):<12} | {comp_str}")
print(f"\nCOVERED ({len(result['covered'])} keywords — protect these):")
for c in result["covered"]:
print(f" {c['keyword']:<35} | Position {c['position']}")JavaScript Example
const API_KEY = 'your-scavio-api-key';
const YOUR_DOMAIN = 'yoursite.com';
const COMPETITORS = ['asana.com', 'monday.com'];
const KEYWORDS = ['project management software', 'task tracker for teams', 'kanban tool free'];
async function getPositions(keyword, domains) {
const res = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': API_KEY },
body: JSON.stringify({ query: keyword, num_results: 20 })
});
const data = await res.json();
const results = data.organic_results ?? [];
return Object.fromEntries(domains.map(d => [d, (results.findIndex(r => r.link?.includes(d)) + 1) || null]));
}
for (const kw of KEYWORDS) {
const positions = await getPositions(kw, [YOUR_DOMAIN, ...COMPETITORS]);
const yourPos = positions[YOUR_DOMAIN];
const compTop10 = COMPETITORS.filter(d => positions[d] && positions[d] <= 10);
if (compTop10.length && (!yourPos || yourPos > 10)) {
console.log(`GAP: ${kw} | You: ${yourPos ?? 'NR'} | Competitors: ${compTop10.join(', ')}`);
}
}Expected Output
Keyword Gap Analysis - yoursite.com - 2026-05-22
GAPS (4 keywords - content opportunities):
project management software | You: NR | asana.com@2, monday.com@5
agile board online | You: NR | clickup.com@3
kanban tool free | You: 14 | asana.com@1, monday.com@7
sprint planning tool | You: NR | clickup.com@4, asana.com@8
COVERED (2 keywords - protect these):
task tracker for teams | Position 6
team task management | Position 9