Start here

Crawl database

Every crawl session, page record, link, image, and diagnostic finding is stored in a single SQLite file. The database is yours — open it with any SQLite-compatible tool to run ad-hoc queries, build reports, or feed data into other systems.

Overview

The database opens in WAL mode with the following pragmas set at connection time:

PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;

WAL (write-ahead logging) allows concurrent readers while a crawl is in progress — you can query results in one tool while the crawler is still writing in another. Foreign keys are enforced, so deleting a session cascades to its pages, links, and related records.

The file is a standard SQLite database. Open it with the sqlite3 CLI, DB Browser for SQLite, DBeaver, or any language driver that speaks SQLite.

sqlite3 /path/to/crawls.db ".tables"

Tables

crawl_sessions

One row per crawl. The config column stores the full crawl configuration as JSON — depth limits, concurrency, rate limits, and any custom rules that were active.

ColumnTypeNotes
id INTEGER Primary key
url TEXT Start URL for the crawl
started_at TEXT ISO 8601 timestamp
completed_at TEXT NULL while the crawl is still running
config TEXT JSON — full crawl configuration

pages

The core table. One row per URL discovered during a crawl. A unique constraint on (session_id, url) prevents duplicate entries for the same URL within a session. The seo_data column holds extended SEO analysis as JSON — structured data findings, Open Graph tags, and additional metadata that does not warrant its own column.

ColumnTypeNotes
id INTEGER Primary key
session_id INTEGER FK → crawl_sessions.id
url TEXT Requested URL
final_url TEXT URL after redirects (same as url if none)
status_code INTEGER HTTP response status
content_type TEXT Content-Type header value
response_time_ms INTEGER Time to first byte, in milliseconds
size_bytes INTEGER Response body size
depth INTEGER Click depth from the start URL
title TEXT Content of the <title> element
title_length INTEGER Character count of the title
meta_description TEXT Content of the meta description tag
meta_description_length INTEGER Character count of the meta description
canonical_url TEXT Value of rel="canonical", if present
h1_count INTEGER Number of <h1> elements on the page
word_count INTEGER Visible text word count
content_hash TEXT SHA-256 hash of the response body
seo_data TEXT JSON — extended SEO analysis

Unique constraint: UNIQUE(session_id, url).

links

Every link discovered on every page. The region column records where on the page the link appeared, which is useful for distinguishing editorial links from navigation or footer boilerplate.

ColumnTypeNotes
id INTEGER Primary key
session_id INTEGER FK → crawl_sessions.id
source_page_id INTEGER FK → pages.id — the page the link was found on
target_url TEXT Resolved destination URL
anchor_text TEXT Visible link text
rel_attributes TEXT Space-separated rel values (nofollow, ugc, etc.)
is_internal INTEGER 1 if the target is on the same host, 0 otherwise
link_type TEXT anchor, image, script, stylesheet, etc.
region TEXT in-content, navigation, footer, sidebar, breadcrumb, or unknown

images

One row per image element found on a crawled page. Missing alt_text is stored as NULL, distinguishing images with no alt attribute from images with an empty one.

ColumnTypeNotes
id INTEGER Primary key
page_id INTEGER FK → pages.id
src TEXT Resolved image URL
alt_text TEXT Alt attribute (NULL if absent, empty string if present but blank)
title TEXT Title attribute, if any
width INTEGER Width attribute in pixels
height INTEGER Height attribute in pixels
loading TEXT Loading attribute (lazy, eager, or NULL)

headless_analysis

Results from the headless renderer, when a crawl runs with --headless enabled. Stores Core Web Vitals measurements, console output, resource loading data, accessibility findings, rendering diagnostics, security headers, and mobile-readiness checks.

ColumnTypeNotes
page_id INTEGER FK → pages.id
screenshot_path TEXT Path to the captured screenshot, if screenshots were enabled
lcp_ms REAL Largest Contentful Paint in milliseconds
cls REAL Cumulative Layout Shift score
fid_ms REAL First Input Delay in milliseconds
inp_ms REAL Interaction to Next Paint in milliseconds
fcp_ms REAL First Contentful Paint in milliseconds
ttfb_ms REAL Time to First Byte in milliseconds
tbt_ms REAL Total Blocking Time in milliseconds
tti_ms REAL Time to Interactive in milliseconds
speed_index REAL Speed Index score
performance_score REAL Composite performance score (0 – 100). This is a Consuela-computed score based on the metrics above, not a Lighthouse score.
console_data TEXT JSON — console messages captured during rendering
resource_data TEXT JSON — sub-resource requests, sizes, and timing
accessibility_data TEXT JSON — accessibility findings from the rendered page
rendering_data TEXT JSON — differences between static HTML and rendered output
security_data TEXT JSON — security headers and mixed-content findings
mobile_data TEXT JSON — viewport, tap-target, and font-size findings

redirect_chains

Redirect chain analysis for pages that returned a 3xx status or followed one or more redirects before landing. The chain_data column stores each hop as a JSON array of objects with URL, status code, and headers.

ColumnTypeNotes
page_id INTEGER FK → pages.id
hop_count INTEGER Number of redirects in the chain
is_long_chain INTEGER 1 if three or more hops
has_loop INTEGER 1 if the chain revisits a URL
has_temporary_redirect INTEGER 1 if any hop is a 302 or 307
chain_data TEXT JSON — array of hops with URL, status, and headers

page_link_metrics

Link graph metrics computed after the crawl completes. The link_score is a 0 – 100 composite that considers inbound count, PageRank, and graph depth. Orphan pages — those with zero inbound internal links — are flagged with is_orphan = 1.

ColumnTypeNotes
page_id INTEGER FK → pages.id
pagerank REAL Internal PageRank score
link_score INTEGER Composite score, 0 – 100
inbound_count INTEGER Number of internal pages linking to this page
outbound_count INTEGER Number of links on this page
graph_depth INTEGER Shortest path from the start URL in the link graph
is_orphan INTEGER 1 if no other internal page links here

page_extractions

Values captured by custom extraction rules. Each row records one rule's output for one page, with the extracted values stored as a JSON array.

ColumnTypeNotes
page_id INTEGER FK → pages.id
rule_name TEXT Name of the extraction rule that produced this row
values_json TEXT JSON — array of extracted values

tls_inspections

TLS certificate inspection results, one row per host encountered during a crawl. The inspection column stores the full certificate chain details as JSON.

ColumnTypeNotes
session_id INTEGER FK → crawl_sessions.id
host TEXT Hostname that was inspected
is_valid INTEGER 1 if the certificate is valid and trusted
days_until_expiry INTEGER Days remaining before the certificate expires
inspection TEXT JSON — full certificate chain and cipher details

Example queries

These queries run against any Consuela crawl database. Replace :session_id with the session you want to inspect, or drop the filter to query across all sessions.

Pages returning 4xx or 5xx

SELECT url, status_code, response_time_ms
FROM pages
WHERE session_id = :session_id
  AND status_code >= 400
ORDER BY status_code;

Pages missing a title

SELECT url, status_code
FROM pages
WHERE session_id = :session_id
  AND status_code = 200
  AND (title IS NULL OR title = '')
ORDER BY url;

Internal broken links with source pages

SELECT
  src.url   AS source_url,
  l.target_url,
  l.anchor_text,
  tgt.status_code
FROM links l
JOIN pages src ON src.id = l.source_page_id
JOIN pages tgt ON tgt.session_id = l.session_id
  AND tgt.url = l.target_url
WHERE l.session_id = :session_id
  AND l.is_internal = 1
  AND tgt.status_code >= 400
ORDER BY tgt.status_code DESC, src.url;

Slowest pages

SELECT url, response_time_ms, size_bytes, status_code
FROM pages
WHERE session_id = :session_id
  AND status_code = 200
ORDER BY response_time_ms DESC
LIMIT 20;

Orphan pages (no inbound internal links)

SELECT p.url, p.status_code, p.depth
FROM pages p
JOIN page_link_metrics m ON m.page_id = p.id
WHERE p.session_id = :session_id
  AND m.is_orphan = 1
ORDER BY p.url;