Technical SEO Audit Fixed

Fixed technical SEO audit workflow with improved error handling and compatibility

Back
Workflow Information

ID: technical_seo_audit_fixed

Namespace: default

Version: 1.0.0

Created: 2025-07-07

Updated: 2025-07-07

Tasks: 6

Quick Actions
Manage Secrets
Inputs
Name Type Required Default
target_url string Required None
audit_depth string Optional standard
Outputs
Name Type Source
seo_score string Overall SEO health score (0-100)
full_report string Complete Technical SEO Audit Report
critical_issues string List of critical issues found
recommendations string AI-generated recommendations
Tasks
parse_url
script

No description

python_analysis
script

No description

try_dataforseo
mcp

No description

calculate_score
script

No description

generate_recommendations
ai_agent

No description

compile_final_report
script

No description

YAML Source
id: technical_seo_audit_fixed
name: Technical SEO Audit Fixed
tasks:
- id: parse_url
  type: script
  script: "import json\nfrom urllib.parse import urlparse\nimport os\n\ntarget_url\
    \ = os.environ.get('target_url', '')\nparsed = urlparse(target_url)\n\nresult\
    \ = {\n    \"domain\": parsed.netloc,\n    \"scheme\": parsed.scheme,\n    \"\
    path\": parsed.path or \"/\",\n    \"base_url\": f\"{parsed.scheme}://{parsed.netloc}\"\
    ,\n    \"is_homepage\": parsed.path in [\"\", \"/\"]\n}\n\nprint(f\"__OUTPUTS__\
    \ {json.dumps(result)}\")\n"
  timeout_seconds: 10
- id: python_analysis
  type: script
  script: "import json\nimport requests\nfrom bs4 import BeautifulSoup\nimport time\n\
    import os\nfrom urllib.parse import urlparse, urljoin\n\ntarget_url = os.environ.get('target_url',\
    \ '')\n\nresults = {\n    \"url\": target_url,\n    \"status\": \"analyzing\"\
    ,\n    \"metrics\": {},\n    \"seo_elements\": {},\n    \"issues\": [],\n    \"\
    warnings\": []\n}\n\ntry:\n    # Fetch page\n    headers = {\n        'User-Agent':\
    \ 'Mozilla/5.0 (compatible; SEO-Analyzer/1.0)'\n    }\n    start_time = time.time()\n\
    \    response = requests.get(target_url, timeout=15, headers=headers, allow_redirects=True)\n\
    \    load_time = time.time() - start_time\n    \n    soup = BeautifulSoup(response.text,\
    \ 'html.parser')\n    \n    # Basic metrics\n    results[\"metrics\"] = {\n  \
    \      \"status_code\": response.status_code,\n        \"load_time\": round(load_time,\
    \ 2),\n        \"content_length\": len(response.text),\n        \"is_https\":\
    \ target_url.startswith('https'),\n        \"final_url\": response.url\n    }\n\
    \    \n    # SEO elements\n    title = soup.find('title')\n    meta_desc = soup.find('meta',\
    \ attrs={'name': 'description'})\n    canonical = soup.find('link', attrs={'rel':\
    \ 'canonical'})\n    \n    # Headers\n    h1_tags = soup.find_all('h1')\n    h2_tags\
    \ = soup.find_all('h2')\n    \n    # Images\n    images = soup.find_all('img')\n\
    \    images_without_alt = [img for img in images if not img.get('alt', '').strip()]\n\
    \    \n    # Open Graph\n    og_tags = {}\n    for tag in soup.find_all('meta',\
    \ property=True):\n        if tag.get('property', '').startswith('og:'):\n   \
    \         og_tags[tag['property']] = tag.get('content', '')\n    \n    results[\"\
    seo_elements\"] = {\n        \"title\": title.text.strip() if title else \"\"\
    ,\n        \"title_length\": len(title.text.strip()) if title else 0,\n      \
    \  \"description\": meta_desc.get('content', '') if meta_desc else \"\",\n   \
    \     \"description_length\": len(meta_desc.get('content', '')) if meta_desc else\
    \ 0,\n        \"canonical\": canonical.get('href', '') if canonical else \"\"\
    ,\n        \"h1_count\": len(h1_tags),\n        \"h2_count\": len(h2_tags),\n\
    \        \"h1_texts\": [h1.text.strip() for h1 in h1_tags[:3]],\n        \"images_total\"\
    : len(images),\n        \"images_without_alt\": len(images_without_alt),\n   \
    \     \"has_og_tags\": len(og_tags) > 0,\n        \"og_title\": og_tags.get('og:title',\
    \ ''),\n        \"og_description\": og_tags.get('og:description', '')\n    }\n\
    \    \n    # Issues detection\n    if results[\"metrics\"][\"status_code\"] !=\
    \ 200:\n        results[\"issues\"].append(f\"Page returns {results['metrics']['status_code']}\
    \ status code\")\n    \n    if not results[\"metrics\"][\"is_https\"]:\n     \
    \   results[\"issues\"].append(\"Site is not using HTTPS\")\n    \n    if not\
    \ results[\"seo_elements\"][\"title\"]:\n        results[\"issues\"].append(\"\
    Missing page title\")\n    elif results[\"seo_elements\"][\"title_length\"] >\
    \ 60:\n        results[\"warnings\"].append(f\"Title too long ({results['seo_elements']['title_length']}\
    \ chars)\")\n    elif results[\"seo_elements\"][\"title_length\"] < 30:\n    \
    \    results[\"warnings\"].append(f\"Title too short ({results['seo_elements']['title_length']}\
    \ chars)\")\n    \n    if not results[\"seo_elements\"][\"description\"]:\n  \
    \      results[\"issues\"].append(\"Missing meta description\")\n    elif results[\"\
    seo_elements\"][\"description_length\"] > 160:\n        results[\"warnings\"].append(f\"\
    Description too long ({results['seo_elements']['description_length']} chars)\"\
    )\n    elif results[\"seo_elements\"][\"description_length\"] < 70:\n        results[\"\
    warnings\"].append(f\"Description too short ({results['seo_elements']['description_length']}\
    \ chars)\")\n    \n    if results[\"seo_elements\"][\"h1_count\"] == 0:\n    \
    \    results[\"issues\"].append(\"Missing H1 tag\")\n    elif results[\"seo_elements\"\
    ][\"h1_count\"] > 1:\n        results[\"warnings\"].append(f\"Multiple H1 tags\
    \ found ({results['seo_elements']['h1_count']})\")\n    \n    if results[\"seo_elements\"\
    ][\"images_without_alt\"] > 0:\n        results[\"warnings\"].append(f\"{results['seo_elements']['images_without_alt']}\
    \ images missing alt text\")\n    \n    if results[\"metrics\"][\"load_time\"\
    ] > 3:\n        results[\"warnings\"].append(f\"Slow page load time ({results['metrics']['load_time']}s)\"\
    )\n    \n    if not results[\"seo_elements\"][\"has_og_tags\"]:\n        results[\"\
    warnings\"].append(\"Missing Open Graph tags\")\n    \n    results[\"status\"\
    ] = \"success\"\n    \nexcept Exception as e:\n    results[\"status\"] = \"error\"\
    \n    results[\"error\"] = str(e)\n    results[\"issues\"].append(f\"Analysis\
    \ failed: {str(e)}\")\n\nprint(f\"__OUTPUTS__ {json.dumps(results)}\")\n"
  depends_on:
  - parse_url
  requirements:
  - requests==2.31.0
  - beautifulsoup4==4.12.2
  - lxml==4.9.3
  timeout_seconds: 30
- id: try_dataforseo
  type: mcp
  when: audit_depth != 'basic'
  tool_name: dataforseo_labs_google_domain_rank_overview
  depends_on:
  - parse_url
  retry_policy:
    max_attempts: 2
    initial_interval: 3
  deployment_id: pod-hl7wh2lc
  tool_arguments:
    target: ${parse_url.domain}
    language_code: en
    location_name: United States
  timeout_seconds: 60
- id: calculate_score
  type: script
  script: "import json\nimport os\nfrom datetime import datetime\n\n# Get analysis\
    \ data\nanalysis = json.loads(os.environ.get('python_analysis', '{}'))\ndataforseo\
    \ = json.loads(os.environ.get('try_dataforseo', '{}'))\n\n# Extract domain metrics\
    \ if available\ndomain_metrics = {}\nif dataforseo and 'tasks' in dataforseo:\n\
    \    try:\n        result = dataforseo['tasks'][0]['result'][0]\n        metrics\
    \ = result.get('metrics', {})\n        domain_metrics = {\n            \"organic_keywords\"\
    : metrics.get('organic', {}).get('count', 0),\n            \"organic_traffic\"\
    : metrics.get('organic', {}).get('etv', 0),\n            \"domain_rank\": result.get('rank',\
    \ 0)\n        }\n    except:\n        pass\n\n# Calculate score\nissues_count\
    \ = len(analysis.get('issues', []))\nwarnings_count = len(analysis.get('warnings',\
    \ []))\nscore = max(0, 100 - issues_count * 15 - warnings_count * 5)\n\n# Compile\
    \ results\nresults = {\n    \"audit_date\": datetime.now().isoformat(),\n    \"\
    target_url\": os.environ.get('target_url', ''),\n    \"audit_depth\": os.environ.get('audit_depth',\
    \ 'standard'),\n    \"score\": score,\n    \"issues_count\": issues_count,\n \
    \   \"warnings_count\": warnings_count,\n    \"issues\": analysis.get('issues',\
    \ []),\n    \"warnings\": analysis.get('warnings', []),\n    \"metrics\": analysis.get('metrics',\
    \ {}),\n    \"seo_elements\": analysis.get('seo_elements', {}),\n    \"domain_metrics\"\
    : domain_metrics\n}\n\nprint(f\"__OUTPUTS__ {json.dumps(results)}\")\n"
  depends_on:
  - python_analysis
  timeout_seconds: 20
- id: generate_recommendations
  type: ai_agent
  prompt: "You are an expert SEO consultant. Analyze this technical SEO audit data\
    \ and provide comprehensive, actionable recommendations.\n\nSEO Audit Results:\n\
    ${calculate_score}\n\nBased on the data, create a detailed analysis with:\n\n\
    1. **Executive Summary** - A 2-3 sentence overview of the site's SEO health\n\
    2. **Critical Issues** - Must-fix problems that severely impact SEO\n3. **High\
    \ Priority Recommendations** - Important improvements for better rankings\n4.\
    \ **Quick Wins** - Easy fixes that will have immediate positive impact\n5. **Long-term\
    \ Optimization** - Strategic improvements for sustained growth\n\nReturn your\
    \ response as a well-structured JSON object with these exact keys:\n{\n  \"executive_summary\"\
    : \"string\",\n  \"critical_issues\": [\"issue1\", \"issue2\"],\n  \"high_priority_recommendations\"\
    : [\"rec1\", \"rec2\"],\n  \"quick_wins\": [\"win1\", \"win2\"],\n  \"long_term_improvements\"\
    : [\"improvement1\", \"improvement2\"]\n}\n"
  agent_type: analyst
  depends_on:
  - calculate_score
  model_client_id: seo_analyzer
  timeout_seconds: 60
- id: compile_final_report
  type: script
  script: "import json\nimport os\nfrom datetime import datetime\n\n# Get data\nscore_data\
    \ = json.loads(os.environ.get('calculate_score', '{}'))\nai_recs = os.environ.get('generate_recommendations',\
    \ '{}')\n\n# Parse AI recommendations\ntry:\n    # Handle both string and dict\
    \ responses\n    if isinstance(ai_recs, str):\n        # Try to extract JSON from\
    \ the response\n        start = ai_recs.find('{')\n        end = ai_recs.rfind('}')\
    \ + 1\n        if start >= 0 and end > start:\n            recommendations = json.loads(ai_recs[start:end])\n\
    \        else:\n            recommendations = {\"error\": \"Could not parse AI\
    \ response\"}\n    else:\n        recommendations = ai_recs\nexcept:\n    recommendations\
    \ = {\n        \"executive_summary\": \"SEO audit completed. Please review the\
    \ findings below.\",\n        \"critical_issues\": score_data.get('issues', []),\n\
    \        \"high_priority_recommendations\": [\"Fix all critical issues\", \"Optimize\
    \ page speed\"],\n        \"quick_wins\": [\"Add missing meta tags\", \"Fix image\
    \ alt texts\"],\n        \"long_term_improvements\": [\"Implement structured data\"\
    , \"Build quality backlinks\"]\n    }\n\n# Create final report\nreport = {\n \
    \   \"report_metadata\": {\n        \"title\": \"Technical SEO Audit Report\"\
    ,\n        \"generated_at\": datetime.now().isoformat(),\n        \"target_url\"\
    : score_data.get('target_url', ''),\n        \"audit_depth\": score_data.get('audit_depth',\
    \ 'standard'),\n        \"workflow_version\": \"1.0.0\"\n    },\n    \"executive_summary\"\
    : {\n        \"overall_score\": score_data.get('score', 0),\n        \"critical_issues_count\"\
    : score_data.get('issues_count', 0),\n        \"warnings_count\": score_data.get('warnings_count',\
    \ 0),\n        \"summary\": recommendations.get('executive_summary', '')\n   \
    \ },\n    \"technical_analysis\": {\n        \"performance_metrics\": score_data.get('metrics',\
    \ {}),\n        \"seo_elements\": score_data.get('seo_elements', {}),\n      \
    \  \"domain_authority\": score_data.get('domain_metrics', {})\n    },\n    \"\
    issues_and_warnings\": {\n        \"critical_issues\": score_data.get('issues',\
    \ []),\n        \"warnings\": score_data.get('warnings', [])\n    },\n    \"recommendations\"\
    : {\n        \"critical_fixes\": recommendations.get('critical_issues', []),\n\
    \        \"high_priority\": recommendations.get('high_priority_recommendations',\
    \ []),\n        \"quick_wins\": recommendations.get('quick_wins', []),\n     \
    \   \"long_term\": recommendations.get('long_term_improvements', [])\n    },\n\
    \    \"next_steps\": [\n        \"1. Address all critical issues immediately\"\
    ,\n        \"2. Implement quick wins for fast improvements\",\n        \"3. Plan\
    \ high-priority recommendations implementation\",\n        \"4. Schedule re-audit\
    \ in 30 days to measure progress\"\n    ],\n    \"audit_status\": \"completed\"\
    \n}\n\nprint(f\"__OUTPUTS__ {json.dumps(report)}\")\n"
  depends_on:
  - calculate_score
  - generate_recommendations
  timeout_seconds: 20
inputs:
- name: target_url
  type: string
  required: true
  validation:
    pattern: ^https?://[\w\-\.]+(\.[\w\-]+)+.*$
  description: The website URL to audit (e.g., https://example.com)
- enum:
  - basic
  - standard
  - comprehensive
  name: audit_depth
  type: string
  default: standard
  description: 'Depth of audit: basic, standard, or comprehensive'
outputs:
  seo_score:
    source: compile_final_report.executive_summary.overall_score
    description: Overall SEO health score (0-100)
  full_report:
    source: compile_final_report
    description: Complete Technical SEO Audit Report
  critical_issues:
    source: compile_final_report.issues_and_warnings.critical_issues
    description: List of critical issues found
  recommendations:
    source: compile_final_report.recommendations
    description: AI-generated recommendations
version: 1.0.0
description: Fixed technical SEO audit workflow with improved error handling and compatibility
model_clients:
- id: seo_analyzer
  config:
    model: gpt-4o-mini
    api_key: ${env.OPENAI_API_KEY}
    max_tokens: 2000
    temperature: 0.3
  provider: openai
Execution ID Status Started Duration Actions
4cddd026... COMPLETED 2025-07-07
09:07:00
N/A View