Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Optimize websites for both traditional search engines (Google, Bing) and AI engines (ChatGPT, Perplexity, Gemini)
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/dataforseo_api.py
1#!/usr/bin/env python32"""3DataForSEO API wrapper4"""5import urllib.request6import urllib.parse7import json8import base649import sys10from credential import get_dataforseo_credentials1112API_BASE = "https://api.dataforseo.com/v3"131415def api_post(endpoint: str, data: list) -> dict:16"""Make POST request to DataForSEO API"""17login, password = get_dataforseo_credentials()18if not login or not password:19print("error: DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD not set", file=sys.stderr)20print("Run: export DATAFORSEO_LOGIN=your_login", file=sys.stderr)21print(" export DATAFORSEO_PASSWORD=your_password", file=sys.stderr)22sys.exit(1)2324url = f"{API_BASE}/{endpoint}"25auth = base64.b64encode(f"{login}:{password}".encode()).decode()26headers = {27"Authorization": f"Basic {auth}",28"Content-Type": "application/json"29}3031req = urllib.request.Request(32url,33data=json.dumps(data).encode(),34headers=headers,35method="POST"36)3738try:39with urllib.request.urlopen(req, timeout=60) as resp:40return json.loads(resp.read().decode())41except urllib.error.HTTPError as e:42error_body = e.read().decode()43print(f"error: HTTP {e.code} - {error_body}", file=sys.stderr)44sys.exit(1)45except Exception as e:46print(f"error: {e}", file=sys.stderr)47sys.exit(1)484950def format_count(n) -> str:51"""Format numbers (1234567 -> 1.2M)"""52if n is None:53return "0"54n = int(n)55if n >= 1_000_000_000:56return f"{n/1_000_000_000:.1f}B"57if n >= 1_000_000:58return f"{n/1_000_000:.1f}M"59if n >= 1_000:60return f"{n/1_000:.1f}K"61return str(n)626364def get_result(response: dict) -> list:65"""Extract result from API response"""66if not response.get("tasks"):67return []68task = response["tasks"][0]69if task.get("status_code") != 20000:70print(f"error: {task.get('status_message', 'Unknown error')}", file=sys.stderr)71return []72return task.get("result", [])737475def print_keywords_list(keywords: list):76"""Print list of keywords"""77print(f"keywords[{len(keywords)}]{{keyword,volume,difficulty}}:")78for kw in keywords:79keyword = kw.get("keyword", "N/A")80volume = format_count(kw.get("search_volume"))81difficulty = kw.get("keyword_difficulty", "N/A")82print(f" {keyword},{volume},{difficulty}")838485def print_serp_list(items: list):86"""Print SERP results"""87organic = [i for i in items if i.get("type") == "organic"]88print(f"serp[{len(organic)}]{{position,title,domain}}:")89for item in organic[:20]:90pos = item.get("rank_absolute", "N/A")91title = (item.get("title") or "")[:50]92domain = item.get("domain", "N/A")93print(f" {pos},{title},{domain}")949596def print_backlinks_list(items: list):97"""Print backlinks"""98print(f"backlinks[{len(items)}]{{from,to,rank,dofollow}}:")99for item in items:100url_from = (item.get("url_from") or "")[:50]101url_to = (item.get("url_to") or "")[:30]102rank = item.get("rank", "N/A")103dofollow = item.get("dofollow", False)104print(f" {url_from},{url_to},{rank},{dofollow}")105