How to Interpret OpenSEO Site Audit Report Results: A Complete Guide
An OpenSEO site audit report returns a JSON payload with four main sections—audit, pages, lighthouse, and issues—that map directly to database tables and provide a complete picture of your site's technical health.
The every-app/open-seo repository provides a multi-phase auditing workflow that crawls your site, gathers Lighthouse performance data, and identifies broken links and orphan pages. When you call the getAuditResults endpoint, the system aggregates data from multiple database tables into a single coherent document. Understanding how to read each section allows you to prioritize fixes based on actual technical impact rather than raw issue counts.
Understanding the Four Main Sections of an OpenSEO Audit Report
The JSON response from getAuditResults (defined in src/serverFunctions/audit.ts) contains four top-level keys that correspond to physical database tables: audit, pages, lighthouse, and issues.
The Audit Metadata Section
The audit object contains run metadata including id, startUrl, status, pagesCrawled, pagesTotal, and timestamps. It also includes the parsed configuration such as lighthouseStrategy and maxPages.
Why this matters: This section tells you whether the audit status is "completed", "running", or "error". If pagesCrawled is less than pagesTotal, the crawl stopped early due to hitting the maxPages limit or encountering a fatal error. You can cross-reference error statuses with error codes defined in src/server/lib/errors.ts.
The Pages Section
The pages array lists every URL discovered during the crawl with its HTTP statusCode, contentLength, mimeType, and flags like blocked (indicating robots.txt exclusion).
Why this matters: This inventory reveals which URLs were actually reachable. Look for 4xx/5xx responses here to identify server errors or soft 404s that need immediate attention.
The Lighthouse Performance Section
The lighthouse array contains scores and metrics for sampled pages tested on both mobile and desktop. Each entry includes numeric scores for performance, accessibility, bestPractices, and seo, plus an errorMessage field if the fetch failed.
Why this matters: Lighthouse results provide a quantitative health check for page speed and SEO best practices. The count lighthouseTotal should equal twice the number of sampled pages (mobile + desktop). If lighthouseFailed is greater than zero, inspect the corresponding entries for specific errorMessage values.
The Issues Section
The issues array contains detected problems with an issueType (e.g., broken-internal-link, orphan-page), the affected pageId/pageUrl, a dedupeKey, and type-specific details.
Why this matters: This is your actionable todo list. Common issue types include:
broken-internal-link— A link on a crawled page returned a non-2xx response.orphan-page— A page discovered in the crawl but not linked from any other page.missing-metaorslow-page— Generated by additional multipage check modules insrc/server/lib/audit/issues/multipage.ts.
Each issue's details object contains the exact URL, HTTP status, and remediation context.
How OpenSEO Generates Your Audit Data
Understanding the report structure requires knowing how the data is built. The workflow in src/server/workflows/siteAuditWorkflowPhases.ts orchestrates five distinct phases:
- Discovery Phase (
runDiscoveryPhase) — Parsesrobots.txtand extracts sitemaps, storing seeds in a scratch-pad data object. - Crawl Phase (
runCrawlPhase) — Fetches URLs and persists them to theAuditRepository. - Lighthouse Phase — Selects sample pages via
selectLighthousePagesand fetches Lighthouse data via DataForSEO (stored viastoreLighthouseResult). - Multipage Checks (
runMultipageChecksandrunScratchpadLinkChecks) — Runs after crawling to detect broken links and orphan pages. - Finalize (
finalizeAudit) — Inserts all issues, sets status to completed, and clears the scratch-pad.
The getAuditResults endpoint simply reads these persisted rows via AuditRepository.getAuditResultsForProject and returns the fully joined view.
Reading Key Status Fields and Metrics
When interpreting the report, focus on these specific fields:
status—"completed"means all phases insiteAuditWorkflowPhases.tssucceeded;"error"contains anerrorCodefor debugging.pagesTotalvs.pagesCrawled— A discrepancy indicates the crawl halted early, usually due to themaxPagesconfiguration limit.lighthouseTotal— Should be twice your sampled page count. IflighthouseFailedis non-zero, check theerrorMessagein the Lighthouse array entries.dedupeKey— Found in issue objects; allows you to group identical issues across multiple pages without double-counting.
Querying and Displaying Audit Results
You can consume the audit data via React hooks, direct HTTP requests, or server-side service calls.
Fetching Results in a React Component
Use the generated server function with TanStack Query to display audit data:
import { useQuery } from '@tanstack/react-query';
import { getAuditResults } from '@/routes/api/audit';
function AuditReport({ auditId }: { auditId: string }) {
const { data, isLoading, error } = useQuery(['audit', auditId], () =>
getAuditResults({ auditId })
);
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Failed to load audit.</p>;
const { audit, pages, lighthouse, issues } = data!;
return (
<>
<h2>Audit – {audit.startUrl}</h2>
<p>Status: {audit.status}</p>
<section>
<h3>Crawled Pages ({audit.pagesCrawled}/{audit.pagesTotal})</h3>
<ul>
{pages.map(p => (
<li key={p.id}>
{p.url} – {p.statusCode}
</li>
))}
</ul>
</section>
<section>
<h3>Lighthouse Scores</h3>
<ul>
{lighthouse.map(l => (
<li key={l.id}>
{l.url}: {l.performance} / 100
{l.errorMessage && <em> – Error: {l.errorMessage}</em>}
</li>
))}
</ul>
</section>
<section>
<h3>Issues</h3>
{issues.map(i => (
<div key={i.pageId} style={{ marginBottom: '0.5rem' }}>
<strong>{i.issueType}</strong> on {i.pageUrl}
{i.details && <pre>{JSON.stringify(i.details, null, 2)}</pre>}
</div>
))}
</section>
</>
);
}
Raw API Access via CLI
For automation or external integrations, call the endpoint directly:
curl -X POST https://your-openseo.example.com/api/audit/getAuditResults \
-H "Content-Type: application/json" \
-d '{"auditId":"c1d2e3f4-5678-90ab-cdef-1234567890ab"}'
Filter the JSON response with jq to isolate specific issues:
# Show only broken internal links
jq '.issues[] | select(.issueType=="broken-internal-link")' response.json
Server-Side Service Integration
For custom backends or background jobs, use the AuditService directly:
import { AuditService } from '@/server/features/audit/services/AuditService';
async function printSummary(auditId: string, projectId: string) {
const result = await AuditService.getResults(auditId, projectId);
console.log(`Audit for ${result.audit.startUrl}`);
console.log(`Pages crawled: ${result.audit.pagesCrawled}/${result.audit.pagesTotal}`);
console.log(`Lighthouse failures: ${result.audit.lighthouseFailed}`);
console.log('Issues:');
for (const i of result.issues) {
console.log(`- ${i.issueType} on ${i.pageUrl}`);
}
}
The AuditService.getResults method in src/server/features/audit/services/AuditService.ts handles the database joins and returns the same structured data used by the API endpoint.
Summary
- OpenSEO audit reports contain four main sections (
audit,pages,lighthouse,issues) that map to database tables in the every-app/open-seo architecture. - Always check
audit.statusto confirm the workflow completed successfully before analyzing other sections. - Compare
pagesCrawledtopagesTotalto identify if the crawl stopped early due to limits or errors. - Review the
issuesarray for actionable items likebroken-internal-linkandorphan-page, using thedetailsobject for remediation specifics. - Query results via the React hook, raw HTTP POST, or server-side
AuditServicedepending on your integration needs.
Frequently Asked Questions
What does it mean if pagesCrawled is less than pagesTotal?
This indicates the audit stopped before discovering all potential URLs. According to the workflow logic in src/server/workflows/siteAuditWorkflowPhases.ts, this typically occurs when the crawl hits the maxPages limit defined in your audit configuration or encounters a fatal network error. Check the audit.status field—if it shows "completed", the crawl simply reached its configured cap; if it shows "error", inspect the errorCode for the specific failure reason.
How do I identify which pages failed Lighthouse testing?
Examine the lighthouseFailed count in the audit metadata section, then iterate through the lighthouse array to find entries containing an errorMessage field. The lighthouseTotal should equal twice the number of sampled pages (accounting for both mobile and desktop tests). Failed entries will have null scores and a descriptive error explaining why DataForSEO could not retrieve the metrics.
Can I query the audit data directly from the database instead of using the API?
Yes. The JSON sections returned by getAuditResults correspond directly to the tables audit, audit_pages, audit_lighthouse, and audit_issues. If you need deeper diagnostics or want to run custom SQL analysis, you can query these tables directly. However, the API response in src/serverFunctions/audit.ts provides a fully-joined view that handles the relationships between pages and their associated issues automatically.
What is the difference between broken-internal-link and orphan-page issues?
A broken-internal-link issue (generated in src/server/lib/audit/issues/multipage.ts) indicates that a crawled page contains a hyperlink returning a 4xx or 5xx status code. An orphan-page issue means the page was discovered (usually via sitemap or direct inclusion) but no other page in the crawl links to it. Broken links represent navigation problems for users, while orphan pages represent architectural isolation that may prevent proper indexing by search engines.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →