Run 20 SEO checks on any URL with a single API call. Get a score, grade, and actionable recommendations in under 2 seconds.
https://seopeek.web.app
All API endpoints live under /api/v1/. All requests and responses use JSON.
The free-tier audit endpoint (GET) requires no authentication and is rate-limited. For higher limits, create an API key and include it in the x-api-key header:
curl -H "x-api-key: seo_your_key_here" \
"https://seopeek.web.app/api/v1/audit?url=https://example.com"
API keys are prefixed with seo_ and tied to the email address used during registration. Keep your key secret and never expose it in client-side code.
Run a full SEO audit on any publicly accessible URL. Returns a score (0-100), letter grade (A-F), summary counts, and all 20 individual check results.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | The URL to audit. Must be publicly accessible. The https:// prefix is added automatically if omitted. |
Restrictions: Private, internal, and localhost URLs are blocked (127.0.0.1, 192.168.*, 10.*, 172.*, *.local). Target URLs have a 10-second fetch timeout.
Register for an API key using your email address. You start on the free plan with 50 audits per day.
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | A valid email address to associate with the key. |
curl -X POST "https://seopeek.web.app/api/v1/keys" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
Response:
{
"apiKey": "seo_abc123def456...",
"plan": "free",
"limit": "50 audits/day"
}
Returns the service status, name, and version. Useful for monitoring uptime.
{
"status": "ok",
"service": "seopeek",
"version": "1.0.0"
}
A successful audit returns a JSON object with the following structure:
{
"url": "https://example.com",
"score": 82,
"grade": "B",
"summary": {
"passed": 15,
"warnings": 4,
"failed": 1,
"total": 20
},
"checks": [
{
"id": "title",
"status": "pass",
"message": "Title is good (52 chars)",
"detail": "Example Domain - The Best Example Site"
},
{
"id": "meta-description",
"status": "fail",
"message": "Missing meta description",
"detail": "Add a compelling meta description (120-160 characters)."
},
// ... 18 more checks
],
"meta": {
"scanDuration": "847ms",
"timestamp": "2026-03-28T14:22:01.000Z"
}
}
| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier for the check (e.g. title, meta-description) |
| status | string | One of pass, warn, or fail |
| message | string | Human-readable summary of the result |
| detail | string | Additional context or the actionable recommendation |
Every audit runs these 20 checks against the target page. Each check returns pass, warn, or fail.
Checks for a <title> tag and validates its length. Optimal: 50-60 characters. Too short (<10) or too long (>70) triggers a warning.
Validates the meta[name="description"] tag exists and is 70-170 characters. Missing descriptions get a fail; too short or long gets a warning.
Ensures exactly one <h1> tag exists. Missing H1 is a fail; multiple H1s trigger a warning.
Validates that heading levels (H1-H6) follow a sequential order without skipping levels (e.g. H1 directly to H3).
Checks for og:title, og:description, and og:image. All three present is a pass; none is a fail; partial is a warning.
Looks for meta[name="twitter:card"]. Missing Twitter Card meta tag triggers a warning.
Checks for a link[rel="canonical"] tag. Missing canonical URL can cause duplicate content issues.
Validates the meta[name="viewport"] tag includes width=device-width. Missing viewport is a fail; incorrect config is a warning.
Counts images with and without alt attributes. More than 50% missing alt text is a fail; any missing is a warning.
Checks for a lang attribute on the <html> element. Helps search engines serve the correct audience.
Detects script[type="application/ld+json"] blocks. Structured data enhances rich search result appearance.
Checks the meta[name="robots"] tag. A noindex directive is flagged as a fail since it prevents indexing.
Verifies the URL uses https://. Non-HTTPS sites receive a fail as Google penalizes insecure connections.
Counts internal vs. external links on the page. No internal links triggers a warning; a healthy link structure passes.
Measures word count of the page body. Under 100 words is thin content (warning); under 300 is light content; 300+ passes.
Looks for link[rel="icon"], shortcut icon, or apple-touch-icon. Missing favicons hurt brand recognition.
Checks for meta[charset] or a Content-Type meta with charset. Missing encoding can cause rendering issues.
Validates the HTTP Content-Type response header contains text/html. Unexpected types trigger a warning.
Counts CSS without media/preload and JS without async/defer/module. 1-3 is a warning; 4+ is a fail.
Scans for obsolete elements: <font>, <center>, <marquee>, <blink>, <frame>, <frameset>. Any found triggers a warning.
The overall score is calculated from the results of all 20 checks using a weighted formula:
// Score calculation
const score = Math.round(
((passed + warnings * 0.5) / total) * 100
);
With 20 checks, a perfect score of 100 requires all 20 to pass. A page with 15 passes, 4 warnings, and 1 fail scores ((15 + 4*0.5) / 20) * 100 = 85.
| Grade | Score Range | Meaning |
|---|---|---|
| A | 90 - 100 | Excellent SEO. Minor optimizations at most. |
| B | 80 - 89 | Good SEO. A few checks need attention. |
| C | 70 - 79 | Fair. Several issues should be addressed. |
| D | 60 - 69 | Poor. Significant SEO problems detected. |
| F | 0 - 59 | Failing. Critical SEO issues require immediate action. |
# Basic audit (free tier, no auth)
curl "https://seopeek.web.app/api/v1/audit?url=https://example.com"
# With API key for higher limits
curl -H "x-api-key: seo_your_key_here" \
"https://seopeek.web.app/api/v1/audit?url=https://example.com"
# Pretty-print the JSON output
curl -s "https://seopeek.web.app/api/v1/audit?url=https://example.com" | jq .
# Register for an API key
curl -X POST "https://seopeek.web.app/api/v1/keys" \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
// Node.js (native fetch, v18+)
const auditUrl = async (url, apiKey) => {
const endpoint = `https://seopeek.web.app/api/v1/audit?url=${encodeURIComponent(url)}`;
const headers = apiKey ? { 'x-api-key': apiKey } : {};
const res = await fetch(endpoint, { headers });
const data = await res.json();
console.log(`Score: ${data.score}/100 (Grade: ${data.grade})`);
console.log(`Passed: ${data.summary.passed} | Warnings: ${data.summary.warnings} | Failed: ${data.summary.failed}`);
// Log any failing checks
data.checks
.filter(c => c.status === 'fail')
.forEach(c => console.log(` FAIL: ${c.message}`));
return data;
};
auditUrl('https://example.com', 'seo_your_key_here');
# Python 3 with requests
import requests
def audit_url(url, api_key=None):
endpoint = "https://seopeek.web.app/api/v1/audit"
headers = {"x-api-key": api_key} if api_key else {}
params = {"url": url}
resp = requests.get(endpoint, headers=headers, params=params)
resp.raise_for_status()
data = resp.json()
print(f"Score: {data['score']}/100 (Grade: {data['grade']})")
print(f"Passed: {data['summary']['passed']} | "
f"Warnings: {data['summary']['warnings']} | "
f"Failed: {data['summary']['failed']}")
# Show failing checks
for check in data["checks"]:
if check["status"] == "fail":
print(f" FAIL: {check['message']}")
return data
result = audit_url("https://example.com", api_key="seo_your_key_here")
| Plan | Daily Limit | Monthly Limit | Per-Minute |
|---|---|---|---|
| Free | 50 | 500 | 10 req/min |
| Starter | 100 | 1,000 | 30 req/min |
| Pro | 500 | 10,000 | 60 req/min |
Limits reset daily at midnight UTC and monthly on the 1st. When you exceed a limit, the API returns a 429 status code.
url parameter, invalid URL format, or private/internal URL.All error responses return JSON with an error field:
{
"error": "Missing required parameter: url",
"example": "/api/v1/audit?url=https://example.com"
}