How Audit Limits Are Calculated and Enforced in OpenSEO
OpenSEO enforces tiered audit limits by clamping page counts to plan boundaries, estimating total capacity units (pages + Lighthouse checks), and validating usage against subscription tiers before allowing new audits to start.
The OpenSEO platform (available at every-app/open-seo) controls resource consumption during site audits through a multi-layered limiting system. This mechanism prevents infrastructure abuse while ensuring fair resource allocation across free and paid subscription tiers. Understanding exactly how audit limits are calculated and enforced helps API consumers handle quota errors gracefully and optimize their crawling strategies.
Defining Static Bounds in audit-limits.ts
All numerical constraints originate in src/shared/audit-limits.ts, which serves as the single source of truth for plan boundaries. This file exports constants that define the absolute minimum, default, and maximum page counts allowed per audit:
MIN_AUDIT_PAGES = 10DEFAULT_AUDIT_PAGES = 50FREE_MAX_AUDIT_PAGES = 50PAID_MAX_AUDIT_PAGES = 10_000
These constants are imported across the codebase, ensuring that any change to plan limits propagates consistently to capacity calculations and enforcement logic.
Calculating Audit Capacity
The src/server/features/audit/services/audit-capacity.ts module transforms static bounds into runtime capacity estimates through two primary functions: clampAuditMaxPages and getEstimatedAuditCapacity.
Clamping User-Requested Page Counts
Before an audit begins, OpenSEO sanitizes the user-provided maxPages parameter using clampAuditMaxPages. This function guarantees the requested page count falls within absolute system limits regardless of the user's subscription tier:
export function clampAuditMaxPages(maxPages?: number) {
return Math.min(
Math.max(maxPages ?? DEFAULT_AUDIT_PAGES, MIN_AUDIT_PAGES),
PAID_MAX_AUDIT_PAGES,
);
}
The logic ensures requests below 10 pages are raised to the minimum, unspecified values default to 50, and no request exceeds the hard ceiling of 10,000 pages.
Estimating Total Capacity Units
OpenSEO tracks consumption using "capacity units" that combine page crawls with Lighthouse performance checks. The getEstimatedAuditCapacity function calculates this total workload:
export function getEstimatedAuditCapacity(input: {
maxPages?: number;
lighthouseStrategy?: LighthouseStrategy;
}) {
const pagesTotal = clampAuditMaxPages(input.maxPages);
const lighthouseStrategy = input.lighthouseStrategy ?? "auto";
const lighthouseChecks = lighthouseStrategy === "auto" ? 20 : 0;
return {
pagesTotal,
lighthouseTotal: lighthouseChecks,
total: pagesTotal + lighthouseChecks,
};
}
When the Lighthouse strategy is set to "auto", the system reserves an additional 20 capacity units per audit, ensuring sufficient resources are allocated for performance analysis.
Runtime Enforcement in AuditService.ts
Actual limit validation occurs in src/server/features/audit/services/AuditService.ts, which orchestrates the audit lifecycle and enforces tier-specific restrictions at runtime.
Resolving the Subscription Tier
The service determines applicable limits via resolveAuditLimitTier. In hosted deployments, this checks the organization's subscription status; self-hosted instances default to the "paid" tier, effectively granting maximum resource allocation:
const tier = await AuditService.resolveAuditLimitTier(organizationId);
const limits = AUDIT_LIMITS[tier];
Validating Limits Before Execution
After clamping the page count, the service performs three sequential validations:
-
Page count ceiling: If
maxPagesexceedslimits.maxPagesPerAuditfor the resolved tier, the service throwsAUDIT_PAGE_LIMIT_EXCEEDED. -
Concurrent audit throttle: The system queries current usage via
AuditRepository.getAuditUsageForUserand rejects requests withAUDIT_ALREADY_RUNNINGifrunningCountexceedslimits.maxRunningAudits. -
Capacity unit quota: If the estimated
capacityUnits(pages + Lighthouse checks) exceedslimits.maxCapacityUnits, the service throwsAUDIT_CAPACITY_REACHED.
const maxPages = clampAuditMaxPages(input.maxPages);
if (maxPages > limits.maxPagesPerAudit) {
throw new AppError("AUDIT_PAGE_LIMIT_EXCEEDED");
}
const reservation = getEstimatedAuditCapacity({ maxPages, lighthouseStrategy });
// ... audit row creation ...
const usage = await AuditRepository.getAuditUsageForUser(input.actorUserId);
if (usage.runningCount > limits.maxRunningAudits) {
throw new AppError("AUDIT_ALREADY_RUNNING");
}
if (usage.capacityUnits > limits.maxCapacityUnits) {
throw new AppError("AUDIT_CAPACITY_REACHED");
}
Notably, enforcement occurs after inserting the audit row, preventing race conditions where parallel requests might otherwise bypass limits.
Summary
- Free accounts are restricted to 50 pages per audit, 1 concurrent audit, and 2,000 total capacity units.
- Paid accounts support up to 10,000 pages, unlimited concurrent audits, and 100,000 capacity units.
- The
clampAuditMaxPagesfunction enforces absolute bounds (10–10,000 pages) before tier-specific validation occurs. - Capacity calculations include both page crawls and Lighthouse checks (20 units when strategy is
"auto"). - All enforcement logic resides in
AuditService.ts, which validates limits post-insertion to eliminate race conditions.
Frequently Asked Questions
What is the maximum number of pages allowed for a free audit?
Free-tier audits are capped at 50 pages per scan. The system enforces this via the FREE_MAX_AUDIT_PAGES constant in src/shared/audit-limits.ts, and exceeding this value triggers an AUDIT_PAGE_LIMIT_EXCEEDED error during runtime validation.
How does OpenSEO calculate capacity units for an audit?
Capacity units represent the sum of pages to be crawled plus Lighthouse checks. According to src/server/features/audit/services/audit-capacity.ts, each page counts as one unit, and audits using the "auto" Lighthouse strategy incur an additional 20 units. The total must remain below the tier-specific maxCapacityUnits threshold (2,000 for free, 100,000 for paid).
What error codes indicate limit violations?
OpenSEO returns three specific error codes when limits are breached: AUDIT_PAGE_LIMIT_EXCEEDED when the requested page count exceeds the tier maximum, AUDIT_ALREADY_RUNNING when concurrent audit limits are reached, and AUDIT_CAPACITY_REACHED when the total capacity unit quota is exhausted.
Do self-hosted deployments have different audit limits?
Self-hosted OpenSEO installations default to the "paid" tier in resolveAuditLimitTier, granting access to 10,000 pages per audit and 100,000 capacity units. This behavior ensures that private deployments are not artificially constrained by the free tier limits designed for the hosted SaaS offering.
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 →