Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mavera.io/llms.txt

Use this file to discover all available pages before exploring further.

Scenario

Find keywords your competitors rank for but you don’t. Pull domain_domains comparing your domain against a competitor, parse semicolon-delimited results, filter for high-volume / low-difficulty opportunities, cluster by topic, then send each cluster to Mavera for a content brief.

Architecture

Code

import os, requests, csv, io, time
from collections import defaultdict

SR, MV = os.environ["SEMRUSH_API_KEY"], os.environ["MAVERA_API_KEY"]
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

resp = requests.get("https://api.semrush.com/", params={
    "type": "domain_domains", "key": SR,
    "domains": "-|OR|yourdomain.com|*|OR|competitor.com",
    "database": "us", "display_limit": 200,
    "display_filter": "+|Kd|Lt|60|+|Nq|Gt|100",
    "export_columns": "Ph,Nq,Kd,Co,Nr",
})
reader = csv.reader(io.StringIO(resp.text), delimiter=";")
next(reader)
gaps = [{"keyword": r[0], "volume": int(r[1]), "difficulty": int(r[2])}
        for r in reader if len(r) >= 5]
gaps.sort(key=lambda g: g["volume"], reverse=True)

clusters = defaultdict(list)
for g in gaps:
    clusters[g["keyword"].split()[0]].append(g)
top = sorted(clusters.items(), key=lambda c: sum(g["volume"] for g in c[1]), reverse=True)[:5]

for root, kws in top:
    kw_block = "\n".join(f"- {g['keyword']} (vol: {g['volume']}, KD: {g['difficulty']})"
                         for g in sorted(kws, key=lambda x: -x["volume"])[:10])
    vol = sum(g["volume"] for g in kws)
    mave = requests.post(f"{MB}/mave/chat", headers=MH, json={
        "message": f"Content brief for cluster '{root}' ({len(kws)} kws, {vol} vol).\n\n"
                   f"KEYWORDS:\n{kw_block}\n\nGenerate: 1) SEO title 2) Meta desc "
                   f"3) Primary + secondary kws 4) H2/H3 outline 5) Word count 6) CTA",
    }).json()
    print(f"=== {root} ({len(kws)} kws, {vol} vol) ===")
    print(mave.get("content", "")[:600], "\n")
    time.sleep(0.5)

Example Output

{
  "keyword_gaps_found": 147,
  "clusters_processed": 5,
  "sample_brief": {
    "cluster": "content", "keywords": 23, "total_volume": 18400,
    "title": "Content Marketing Strategy: The 2026 Playbook for B2B Teams",
    "primary_keyword": "content marketing strategy", "word_count": 2500
  }
}

Error Handling

Your SEMrush API key is invalid or expired. Verify the key in your profile settings and check that your subscription is active.
You’ve exhausted your monthly API units. Check your balance at SEMrush → Subscription Info. Cache responses locally to avoid repeated calls.
If the response has only a header row, no keyword gaps exist for these domains or the filters are too strict. Lower volume threshold or raise difficulty ceiling.
Rarely, a keyword contains a semicolon. Python’s csv.reader handles quoted fields; in JavaScript, verify column count before parsing.