# SEO Analyzer - Score Any Website (`lazymac/seo-analyzer`) Actor

Audit any URL in 30 seconds — title/meta/schema.org/Open Graph/canonical/h1-h6 hierarchy/alt text/internal-link graph/Lighthouse-style scores. Returns prioritized issue list with fix suggestions. Pay per analyzed page. Useful for SEO consultants, devs shipping site changes, content teams.

- **URL**: https://apify.com/lazymac/seo-analyzer.md
- **Developed by:** [2x lazymac](https://apify.com/lazymac) (community)
- **Categories:** SEO tools
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$50.00 / 1,000 seo analyses

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## SEO Analyzer

Analyze the SEO health of any webpage in seconds. Get a detailed score (0-100), letter grade (A-F), and actionable recommendations across 9 key categories. Compare two URLs side by side to see which one wins. No API keys required, no browser needed -- just pass a URL and get results.

Built for marketers, developers, agencies, and anyone who wants fast, automated SEO audits at scale.

---

### What It Does

SEO Analyzer fetches any public webpage and runs a comprehensive audit covering titles, meta descriptions, headings, images, links, Open Graph tags, technical SEO, Schema.org structured data, and content quality. Each category receives an individual score, and these are combined into a weighted overall score with a letter grade.

You can also compare two URLs head-to-head. The comparison output shows per-category winners and an overall champion, making it ideal for competitive analysis, A/B testing landing pages, or benchmarking against competitors.

#### Key Capabilities

- **Single URL Analysis**: Full audit with score, grade, issues, and recommendations
- **Two-URL Comparison**: Side-by-side SEO comparison with per-category winners
- **9 Analysis Categories**: Title, Meta Description, Headings, Images, Links, Open Graph, Technical SEO, Schema.org, Content Quality
- **Weighted Scoring**: Each category has a specific weight reflecting its SEO importance
- **Actionable Output**: Every issue comes with a clear recommendation for how to fix it
- **Zero Dependencies**: No external APIs, no browser rendering -- uses lightweight HTML parsing only
- **Fast Execution**: Typical analysis completes in 2-5 seconds

---

### What Data You Get

#### Single URL Analysis Output Fields

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | The final URL after redirects |
| `analyzedAt` | string | ISO 8601 timestamp of the analysis |
| `statusCode` | number | HTTP status code returned by the page |
| `fetchTimeMs` | number | Time taken to fetch the page in milliseconds |
| `overallScore` | number | Weighted overall score from 0 to 100 |
| `grade` | string | Letter grade: A (90+), B (80-89), C (70-79), D (60-69), F (below 60) |
| `scores` | object | Individual scores for each of the 9 categories |
| `details.title` | object | Title tag text, length, score, issues, recommendations |
| `details.metaDescription` | object | Meta description content, length, score, issues, recommendations |
| `details.headings` | object | Full heading hierarchy (H1-H6), counts, texts, score, issues |
| `details.images` | object | Total images, missing alt count, empty alt count, problematic sources |
| `details.links` | object | Internal/external/nofollow/broken link counts, score, issues |
| `details.openGraph` | object | All OG tags found, Twitter Card tags, completeness percentage, missing tags |
| `details.technical` | object | Viewport, canonical, lang, charset, robots, favicon, HTML size, resource counts |
| `details.schema` | object | JSON-LD, Microdata, and RDFa schemas detected with types |
| `details.content` | object | Word count, estimated reading time in minutes, score |
| `summary.totalIssues` | number | Total number of issues found across all categories |
| `summary.issues` | array | Complete list of all issues found |
| `summary.recommendations` | array | Complete list of all actionable recommendations |

#### Comparison Output Fields

| Field | Type | Description |
|-------|------|-------------|
| `comparedAt` | string | ISO 8601 timestamp |
| `results` | array | Full analysis results for both URLs (same structure as single analysis) |
| `comparison` | object | Per-category scores for both URLs with winner |
| `overallWinner` | string | URL of the overall winner, or "tie" |

---

### How to Use

#### Basic Usage

1. Open the SEO Analyzer on Apify
2. Enter a URL in the "URL" field (e.g., `https://example.com`)
3. Click "Start"
4. View results in the "Dataset" tab

#### Comparison Mode

1. Enter the primary URL in the "URL" field
2. Enter a second URL in the "Compare URL" field
3. Click "Start"
4. The output will include full analysis for both URLs plus a comparison summary

---

### Input Configuration

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `url` | string | Yes | -- | The URL to analyze. Must be a publicly accessible webpage. Supports both HTTP and HTTPS. If the protocol is omitted, HTTPS is assumed. The analyzer follows up to 5 redirects automatically. |
| `compareUrl` | string | No | -- | A second URL for side-by-side SEO comparison. When provided, the actor runs a full analysis on both URLs and outputs a comparison with per-category winners and an overall winner. |

---

### Output Example

#### Single URL Analysis

```json
{
  "url": "https://example.com",
  "analyzedAt": "2026-04-16T12:00:00.000Z",
  "statusCode": 200,
  "fetchTimeMs": 342,
  "overallScore": 72,
  "grade": "C",
  "scores": {
    "title": 100,
    "metaDescription": 0,
    "headings": 60,
    "images": 100,
    "links": 70,
    "openGraph": 0,
    "technical": 76,
    "schema": 0,
    "content": 30
  },
  "details": {
    "title": {
      "text": "Example Domain",
      "length": 14,
      "score": 100,
      "issues": ["Title too short (14 chars, recommended 30-60)"],
      "recommendations": ["Expand the title to include primary keywords"]
    },
    "metaDescription": {
      "content": null,
      "length": 0,
      "score": 0,
      "issues": ["Missing meta description"],
      "recommendations": ["Add a compelling meta description between 120-160 characters"]
    },
    "headings": {
      "hierarchy": {
        "h1": { "count": 1, "texts": ["Example Domain"] },
        "h2": { "count": 0, "texts": [] },
        "h3": { "count": 0, "texts": [] },
        "h4": { "count": 0, "texts": [] },
        "h5": { "count": 0, "texts": [] },
        "h6": { "count": 0, "texts": [] }
      },
      "totalHeadings": 1,
      "score": 60,
      "issues": [],
      "recommendations": []
    },
    "images": {
      "total": 0,
      "missingAlt": 0,
      "emptyAlt": 0,
      "missingAltSrcs": [],
      "score": 100,
      "issues": [],
      "recommendations": []
    },
    "links": {
      "total": 1,
      "internal": 0,
      "external": 1,
      "nofollow": 0,
      "broken": 0,
      "score": 70,
      "issues": ["No internal links found"],
      "recommendations": ["Add internal links to improve site structure and crawlability"]
    },
    "openGraph": {
      "ogTags": {},
      "twitterTags": {},
      "completeness": 0,
      "missing": ["og:title", "og:description", "og:image", "og:url", "og:type"],
      "score": 0,
      "issues": ["No Open Graph tags found", "No Twitter Card meta tags found"],
      "recommendations": [
        "Add og:title, og:description, og:image, og:url, and og:type",
        "Add twitter:card, twitter:title, twitter:description for better social sharing"
      ]
    },
    "technical": {
      "viewport": "width=device-width, initial-scale=1",
      "canonical": null,
      "lang": null,
      "charset": "utf-8",
      "robots": null,
      "favicon": null,
      "htmlSizeKB": 1,
      "resources": { "scripts": 0, "stylesheets": 0, "inlineStyles": 1, "iframes": 0 },
      "score": 76,
      "issues": ["Missing canonical URL", "Missing lang attribute on <html>", "No favicon detected"],
      "recommendations": [
        "Add a canonical link to prevent duplicate content issues",
        "Add lang attribute (e.g., <html lang=\"en\">)",
        "Add a favicon for better branding and browser tab appearance"
      ]
    },
    "schema": {
      "schemas": [],
      "count": 0,
      "score": 0,
      "issues": ["No structured data (Schema.org) detected"],
      "recommendations": ["Add JSON-LD structured data for rich search results"]
    },
    "content": {
      "wordCount": 28,
      "readingTimeMin": 1,
      "score": 9,
      "issues": ["Low word count (28)"],
      "recommendations": ["Aim for at least 300 words of meaningful content for better SEO"]
    }
  },
  "summary": {
    "totalIssues": 6,
    "issues": [
      "Title too short (14 chars, recommended 30-60)",
      "Missing meta description",
      "No internal links found",
      "No Open Graph tags found",
      "No Twitter Card meta tags found",
      "Missing canonical URL",
      "Missing lang attribute on <html>",
      "No favicon detected",
      "No structured data (Schema.org) detected",
      "Low word count (28)"
    ],
    "recommendations": [
      "Expand the title to include primary keywords",
      "Add a compelling meta description between 120-160 characters",
      "Add internal links to improve site structure and crawlability",
      "Add og:title, og:description, og:image, og:url, and og:type",
      "Add twitter:card, twitter:title, twitter:description for better social sharing",
      "Add a canonical link to prevent duplicate content issues",
      "Add lang attribute (e.g., <html lang=\"en\">)",
      "Add a favicon for better branding and browser tab appearance",
      "Add JSON-LD structured data for rich search results",
      "Aim for at least 300 words of meaningful content for better SEO"
    ]
  }
}
````

#### Comparison Output

```json
{
  "comparedAt": "2026-04-16T12:00:00.000Z",
  "results": [
    { "url": "https://github.com", "overallScore": 78, "grade": "C", "..." : "..." },
    { "url": "https://gitlab.com", "overallScore": 71, "grade": "C", "..." : "..." }
  ],
  "comparison": {
    "title": { "url1": 100, "url2": 85, "winner": "https://github.com" },
    "metaDescription": { "url1": 90, "url2": 100, "winner": "https://gitlab.com" },
    "headings": { "url1": 80, "url2": 70, "winner": "https://github.com" },
    "images": { "url1": 100, "url2": 90, "winner": "https://github.com" },
    "links": { "url1": 85, "url2": 85, "winner": "tie" },
    "openGraph": { "url1": 100, "url2": 80, "winner": "https://github.com" },
    "technical": { "url1": 92, "url2": 84, "winner": "https://github.com" },
    "schema": { "url1": 100, "url2": 0, "winner": "https://github.com" },
    "content": { "url1": 30, "url2": 50, "winner": "https://gitlab.com" }
  },
  "overallWinner": "https://github.com"
}
```

***

### Scoring System

#### Category Weights

| Category | Weight | What It Checks |
|----------|--------|----------------|
| **Title** | 15% | Presence of `<title>` tag, optimal length (30-60 characters), no duplicates |
| **Meta Description** | 10% | Presence, optimal length (120-160 characters) |
| **Headings** | 10% | H1 presence and uniqueness, heading hierarchy, no skipped levels |
| **Images** | 8% | Alt text on all `<img>` elements for accessibility and SEO |
| **Links** | 7% | Internal/external link balance, no broken or empty hrefs |
| **Open Graph** | 10% | og:title, og:description, og:image, og:url, og:type, Twitter Card tags |
| **Technical SEO** | 20% | Viewport meta, canonical URL, lang attribute, charset, favicon, HTML size, script/stylesheet counts |
| **Schema.org** | 10% | JSON-LD, Microdata, or RDFa structured data detection |
| **Content Quality** | 10% | Word count (minimum 300 recommended), reading time estimation |

#### Grade Scale

| Grade | Score Range | Meaning |
|-------|-------------|---------|
| A | 90 - 100 | Excellent SEO -- minimal or no issues |
| B | 80 - 89 | Good SEO -- minor improvements possible |
| C | 70 - 79 | Average SEO -- several areas need attention |
| D | 60 - 69 | Below average -- significant issues found |
| F | 0 - 59 | Poor SEO -- major problems across multiple categories |

***

### Cost Estimation

This actor uses the pay-per-event pricing model. You are charged per analysis performed.

| Action | Event | Estimated Cost |
|--------|-------|----------------|
| Single URL analysis | 1 event | ~$0.01 - $0.05 per run |
| Two-URL comparison | 1 event | ~$0.01 - $0.05 per run |

Typical run uses minimal compute (128 MB RAM, under 10 seconds) because there is no browser involved -- only lightweight HTTP fetching and HTML parsing.

***

### Integration Guide

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

## Single URL analysis
run = client.actor("lazymac/seo-analyzer").call(run_input={
    "url": "https://example.com"
})

## Get results
dataset = client.dataset(run["defaultDatasetId"])
for item in dataset.iterate_items():
    print(f"Score: {item['overallScore']}/100 ({item['grade']})")
    print(f"Issues: {item['summary']['totalIssues']}")
    for issue in item['summary']['issues']:
        print(f"  - {issue}")
```

```python
## Compare two URLs
run = client.actor("lazymac/seo-analyzer").call(run_input={
    "url": "https://github.com",
    "compareUrl": "https://gitlab.com"
})

dataset = client.dataset(run["defaultDatasetId"])
for item in dataset.iterate_items():
    print(f"Winner: {item['overallWinner']}")
```

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

// Single URL analysis
const run = await client.actor('lazymac/seo-analyzer').call({
    url: 'https://example.com',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const result = items[0];

console.log(`Score: ${result.overallScore}/100 (${result.grade})`);
console.log(`Total issues: ${result.summary.totalIssues}`);
result.summary.recommendations.forEach(rec => console.log(`  - ${rec}`));
```

```javascript
// Compare two URLs
const compareRun = await client.actor('lazymac/seo-analyzer').call({
    url: 'https://github.com',
    compareUrl: 'https://gitlab.com',
});

const { items: compareItems } = await client.dataset(compareRun.defaultDatasetId).listItems();
console.log(`Winner: ${compareItems[0].overallWinner}`);
```

#### Apify API (cURL)

```bash
## Start a run
curl -X POST "https://api.apify.com/v2/acts/lazymac~seo-analyzer/runs" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

## Get results from the default dataset
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?format=json" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

#### Scheduled Monitoring

You can schedule this actor to run daily or weekly on your most important pages to track SEO health over time. Set up a schedule in Apify Console and export results to Google Sheets, Slack, or any webhook.

***

### Use Cases

- **Site Audit**: Run a quick SEO health check on any page before publishing
- **Competitive Analysis**: Compare your landing page against a competitor's page
- **CI/CD Integration**: Add SEO checks to your deployment pipeline
- **Client Reporting**: Generate SEO reports for agency clients
- **Content Optimization**: Identify missing meta tags, heading issues, and content gaps
- **Technical Debt**: Find missing canonical URLs, viewport tags, and structured data
- **Social Media Preview**: Verify Open Graph and Twitter Card tags are properly set
- **Migration Validation**: Verify SEO elements are preserved after a site redesign or CMS migration
- **Multi-Language Sites**: Audit hreflang and lang attributes across localized pages
- **E-Commerce Product Pages**: Ensure product Schema.org markup, canonical URLs, and meta descriptions are in place

***

### Integration with Other Tools

#### Zapier

1. Create a new Zap with a trigger (e.g., "Every Day" schedule or "New Row in Google Sheets")
2. Add an action: **Apify -- Run Actor**
3. Select `lazymac/seo-analyzer` and configure the URL input
4. Add a downstream action to send results to Slack, Google Sheets, email, or any other Zapier-connected app
5. Map the `overallScore`, `grade`, and `summary.issues` fields to your destination

#### Make (Integromat)

1. Create a new scenario
2. Add the **Apify** module and select "Run an Actor"
3. Set the actor to `lazymac/seo-analyzer` with your desired URL
4. Add a router to branch based on the score (e.g., send Slack alert if score < 70)
5. Connect to Google Sheets, Airtable, or a webhook for downstream processing

#### Google Sheets Integration

Export SEO results directly to a spreadsheet for tracking and reporting:

```python
from apify_client import ApifyClient
import gspread
from oauth2client.service_account import ServiceAccountCredentials

## Run the SEO analysis
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("lazymac/seo-analyzer").call(run_input={"url": "https://example.com"})
dataset = client.dataset(run["defaultDatasetId"])
results = list(dataset.iterate_items())

## Write to Google Sheets
scope = ["https://spreadsheets.google.com/feeds"]
creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
gc = gspread.authorize(creds)
sheet = gc.open("SEO Tracker").sheet1

for item in results:
    sheet.append_row([
        item["url"],
        item["overallScore"],
        item["grade"],
        item["summary"]["totalIssues"],
        item["analyzedAt"]
    ])
```

#### Webhooks

Use the Apify webhook feature to get notified when an analysis completes:

1. Go to **Actor runs** in the Apify Console
2. Set up a webhook with event `ACTOR.RUN.SUCCEEDED`
3. Point the webhook URL to your endpoint (Slack incoming webhook, Discord webhook, custom API)
4. The webhook payload includes the dataset ID so you can fetch full results

#### CI/CD Pipeline Integration

Add SEO checks to your GitHub Actions, GitLab CI, or Jenkins pipeline:

```yaml
## GitHub Actions example
- name: SEO Check
  run: |
    RESULT=$(curl -s -X POST "https://api.apify.com/v2/acts/lazymac~seo-analyzer/run-sync-get-dataset-items" \
      -H "Authorization: Bearer ${{ secrets.APIFY_TOKEN }}" \
      -H "Content-Type: application/json" \
      -d '{"url": "${{ env.DEPLOY_URL }}"}')
    SCORE=$(echo $RESULT | jq '.[0].overallScore')
    echo "SEO Score: $SCORE"
    if [ "$SCORE" -lt 70 ]; then
      echo "::warning::SEO score is below 70 ($SCORE). Review the issues before merging."
    fi
```

***

### Tips and Tricks

1. **Start with your most important pages.** Analyze your homepage, top landing pages, and highest-traffic blog posts first. These have the biggest impact on organic search performance.

2. **Use comparison mode for competitive research.** Compare your page against a direct competitor to see where you win and where you lose. Focus improvement efforts on categories where the competitor outscores you.

3. **Schedule weekly audits for key pages.** SEO health can degrade over time as content changes, plugins update, or developers modify templates. A weekly scheduled run catches regressions early.

4. **Pay attention to Technical SEO first.** It carries the highest weight (20%) and includes foundational elements like canonical URLs and viewport tags. Fixing these often provides the biggest score improvement.

5. **Don't chase a perfect 100.** A score of 85+ (grade B or A) means the page is well-optimized. Diminishing returns kick in quickly above that threshold. Focus on fixing critical issues rather than perfecting every metric.

6. **Use the recommendations array for action items.** Each issue in the output comes with a specific recommendation. These can be exported directly to a project management tool as tasks.

7. **Check Open Graph tags before social campaigns.** If you're about to share a page on social media or run ads, verify that OG tags are set correctly. Missing og:image means your shared link will have no preview image.

8. **Monitor Schema.org markup after CMS updates.** Structured data is often generated by CMS plugins that can break during updates. A sudden drop in the Schema.org score signals that your rich results may disappear from Google.

***

### Frequently Asked Questions

**Q: Does this actor render JavaScript?**
A: No. The analyzer fetches raw HTML using a lightweight HTTP client. This means it analyzes what search engine crawlers see by default (server-rendered HTML). For single-page apps (React, Vue, Angular) that require JavaScript rendering, the raw HTML may differ from what users see in a browser.

**Q: How many URLs can I analyze at once?**
A: In single mode, one URL per run. In comparison mode, two URLs per run. For bulk analysis, you can call the actor programmatically in a loop or use the Apify scheduler.

**Q: Does it follow redirects?**
A: Yes. The analyzer follows up to 5 HTTP redirects (301, 302, 303, 307, 308) and reports the final URL in the output.

**Q: What happens if the page returns an error?**
A: The actor will report the HTTP status code and still attempt to analyze whatever HTML was returned. For non-HTML responses or timeouts (15-second limit), the actor will throw an error.

**Q: Is the scoring algorithm customizable?**
A: The current version uses fixed weights. The category weights reflect industry-standard SEO priorities, with Technical SEO (20%) and Title (15%) having the highest impact on the overall score.

**Q: How accurate is the SEO score?**
A: The score is based on well-established SEO best practices. It checks for the same issues that tools like Lighthouse, Moz, and Ahrefs flag. However, no automated tool can fully replace human SEO expertise -- treat the score as a directional indicator and prioritize the specific issues listed.

**Q: Can I use this for monitoring SEO changes over time?**
A: Yes. Schedule the actor to run on a regular interval (daily, weekly) and store results in a dataset. You can then track score changes, new issues, and resolved issues over time using the Apify API or integrations.

**Q: Does this actor check for broken external links?**
A: The actor identifies empty, hash-only, and javascript: href values as "broken." It does not follow external links to check their HTTP status, as that would significantly increase execution time and cost.

**Q: What is the difference between this and Google Lighthouse?**
A: Lighthouse runs in a full browser and measures performance, accessibility, PWA compliance, and more. SEO Analyzer is a focused, lightweight tool that runs without a browser and concentrates exclusively on SEO factors. It is faster, cheaper, and easier to integrate into automated workflows.

**Q: Can I analyze pages behind authentication?**
A: No. The analyzer can only access publicly available URLs. Pages requiring login or behind paywalls will return the login page HTML instead of the actual content.

**Q: How does the weighted scoring work?**
A: Each of the 9 categories has a weight (e.g., Technical SEO = 20%, Title = 15%). The overall score is the sum of each category score multiplied by its weight. For example, if your Title score is 80 and its weight is 15%, that category contributes 12 points to the overall score.

**Q: Can I use this actor with the Apify CLI?**
A: Yes. Install the Apify CLI (`npm install -g apify-cli`), then run: `apify call lazymac/seo-analyzer -i '{"url": "https://example.com"}'`. Results are saved to the local dataset.

**Q: What HTTP method does the analyzer use?**
A: It sends a GET request with a standard browser-like User-Agent header. The request includes `Accept: text/html` and follows standard HTTP semantics.

**Q: Does the actor support HTTPS-only pages?**
A: Yes. HTTPS is fully supported and is the default protocol if you omit the scheme. The actor handles TLS certificates automatically. Self-signed certificates will cause the request to fail.

**Q: How large can the analyzed page be?**
A: There is no hard limit on page size, but extremely large pages (over 5 MB of HTML) may increase processing time. Typical web pages (under 500 KB) are processed in 2-5 seconds.

**Q: Can I get results in CSV format?**
A: Yes. After the run completes, access your results via the Apify API with `?format=csv` appended to the dataset items endpoint. You can also download CSV directly from the Apify Console dataset view.

***

### Limitations

- Does not execute JavaScript (static HTML analysis only)
- Cannot access pages behind authentication
- Maximum 5 redirects followed
- 15-second timeout for page fetching
- Single URL or two-URL comparison per run (use API for bulk)
- Does not check external link HTTP status codes

***

### Changelog

- **v1.0** - Initial release with 9 analysis categories, comparison mode, and pay-per-event pricing

# Actor input Schema

## `url` (type: `string`):

The URL to analyze for SEO health. Must be a publicly accessible webpage. Supports HTTP and HTTPS. If the protocol is omitted, HTTPS is assumed. The analyzer follows up to 5 redirects automatically.

## `compareUrl` (type: `string`):

A second URL for side-by-side SEO comparison. When provided, the actor runs a full analysis on both URLs and outputs a comparison showing per-category winners (title, meta, headings, images, links, OG, technical, schema, content) and an overall winner.

## `timeout` (type: `integer`):

Maximum time in milliseconds to wait for the page to respond. If the page does not respond within this time, the actor throws an error. Default is 15000 (15 seconds). Increase for slow servers or large pages.

## `followRedirects` (type: `boolean`):

Whether to follow HTTP redirects (301, 302, 303, 307, 308). When enabled, the actor follows up to 5 redirects and reports the final URL. Disable to analyze the redirect page itself.

## `userAgent` (type: `string`):

Custom User-Agent header for the HTTP request. By default, the actor uses a standard browser-like User-Agent. Set a custom value to simulate a specific bot or browser. Example: 'Googlebot/2.1 (+http://www.google.com/bot.html)'.

## `includeRawHtml` (type: `boolean`):

When enabled, the raw HTML of the page is included in the output under the 'rawHtml' field. Useful for debugging or further processing. Disabled by default to keep output size small.

## `categories` (type: `array`):

Select which SEO categories to include in the analysis. By default, all 9 categories are analyzed. Narrow the analysis to specific areas by listing only the categories you need. Valid values: title, metaDescription, headings, images, links, openGraph, technical, schema, content.

## `proxyConfiguration` (type: `object`):

Apify proxy configuration. Use Apify's residential or datacenter proxies to avoid IP blocking when analyzing pages that rate-limit requests. Format: {"useApifyProxy": true, "apifyProxyGroups": \["RESIDENTIAL"]}.

## Actor input object example

```json
{
  "url": "https://example.com",
  "compareUrl": "https://competitor.com",
  "timeout": 15000,
  "followRedirects": true,
  "includeRawHtml": false,
  "categories": [
    "title",
    "metaDescription",
    "technical"
  ]
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "url": "https://example.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("lazymac/seo-analyzer").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "url": "https://example.com" }

# Run the Actor and wait for it to finish
run = client.actor("lazymac/seo-analyzer").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "url": "https://example.com"
}' |
apify call lazymac/seo-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=lazymac/seo-analyzer",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "SEO Analyzer - Score Any Website",
        "description": "Audit any URL in 30 seconds — title/meta/schema.org/Open Graph/canonical/h1-h6 hierarchy/alt text/internal-link graph/Lighthouse-style scores. Returns prioritized issue list with fix suggestions. Pay per analyzed page. Useful for SEO consultants, devs shipping site changes, content teams.",
        "version": "1.0",
        "x-build-id": "hdOXoIjgmyCfaKeVi"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/lazymac~seo-analyzer/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-lazymac-seo-analyzer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/lazymac~seo-analyzer/runs": {
            "post": {
                "operationId": "runs-sync-lazymac-seo-analyzer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/lazymac~seo-analyzer/run-sync": {
            "post": {
                "operationId": "run-sync-lazymac-seo-analyzer",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "url"
                ],
                "properties": {
                    "url": {
                        "title": "URL to Analyze",
                        "type": "string",
                        "description": "The URL to analyze for SEO health. Must be a publicly accessible webpage. Supports HTTP and HTTPS. If the protocol is omitted, HTTPS is assumed. The analyzer follows up to 5 redirects automatically."
                    },
                    "compareUrl": {
                        "title": "Compare URL (Optional)",
                        "type": "string",
                        "description": "A second URL for side-by-side SEO comparison. When provided, the actor runs a full analysis on both URLs and outputs a comparison showing per-category winners (title, meta, headings, images, links, OG, technical, schema, content) and an overall winner."
                    },
                    "timeout": {
                        "title": "Request Timeout (ms)",
                        "minimum": 1000,
                        "maximum": 60000,
                        "type": "integer",
                        "description": "Maximum time in milliseconds to wait for the page to respond. If the page does not respond within this time, the actor throws an error. Default is 15000 (15 seconds). Increase for slow servers or large pages.",
                        "default": 15000
                    },
                    "followRedirects": {
                        "title": "Follow Redirects",
                        "type": "boolean",
                        "description": "Whether to follow HTTP redirects (301, 302, 303, 307, 308). When enabled, the actor follows up to 5 redirects and reports the final URL. Disable to analyze the redirect page itself.",
                        "default": true
                    },
                    "userAgent": {
                        "title": "Custom User-Agent",
                        "type": "string",
                        "description": "Custom User-Agent header for the HTTP request. By default, the actor uses a standard browser-like User-Agent. Set a custom value to simulate a specific bot or browser. Example: 'Googlebot/2.1 (+http://www.google.com/bot.html)'."
                    },
                    "includeRawHtml": {
                        "title": "Include Raw HTML",
                        "type": "boolean",
                        "description": "When enabled, the raw HTML of the page is included in the output under the 'rawHtml' field. Useful for debugging or further processing. Disabled by default to keep output size small.",
                        "default": false
                    },
                    "categories": {
                        "title": "Categories to Analyze",
                        "type": "array",
                        "description": "Select which SEO categories to include in the analysis. By default, all 9 categories are analyzed. Narrow the analysis to specific areas by listing only the categories you need. Valid values: title, metaDescription, headings, images, links, openGraph, technical, schema, content.",
                        "default": [
                            "title",
                            "metaDescription",
                            "headings",
                            "images",
                            "links",
                            "openGraph",
                            "technical",
                            "schema",
                            "content"
                        ]
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Apify proxy configuration. Use Apify's residential or datacenter proxies to avoid IP blocking when analyzing pages that rate-limit requests. Format: {\"useApifyProxy\": true, \"apifyProxyGroups\": [\"RESIDENTIAL\"]}."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
