URL Filtering Capabilities in OpenSEO: Research Scopes and Endpoint Filters Explained
OpenSEO provides multi-layered URL filtering through a hierarchical research scope system that supports exact URL, subfolder, domain, and subdomain matching, combined with Zod-validated endpoint-specific filters for attributes like spam score, authority scores, and domain origin.
OpenSEO offers comprehensive URL filtering capabilities that allow developers to precisely control which pages are analyzed across backlinks, search console data, and site audits. The architecture centers on a flexible research scope system defined in src/shared/researchScope.ts, complemented by granular filter schemas for each API endpoint. These layers work together to ensure that data retrieval respects both provider-side constraints and application-specific boundaries before results reach the client.
The Research Scope System
At the heart of OpenSEO's URL filtering is the research scope enumeration defined in src/shared/researchScope.ts (lines 18-30). This system establishes four distinct boundary types that determine how URLs are matched against a target.
Exact URL Matching
The exact_url scope restricts analysis to a single, normalized page URL. When this scope is active, the urlMatchesResearchTarget function (lines 212-233) returns true only when the URL path matches the target path exactly. This is ideal for analyzing specific landing pages or critical conversion URLs without noise from surrounding content.
Subfolder Matching
The subfolder scope captures all URLs under a given path while excluding sibling paths. The implementation requires the URL's path to equal the target path or start with targetPath/. According to the source code in src/shared/researchScope.ts, this logic ensures that /blog/post-1 matches a target of /blog, while /blogging does not.
Domain and Subdomain Matching
For broader analysis, OpenSEO supports hostname-based filtering through two distinct scopes:
domain: Matches only the exact hostname without subdomains. ThehostMatcheshelper enforces strict equality between the URL hostname and target hostname.subdomains: Matches the hostname plus any of its subdomains. The samehostMatchesfunction accepts any hostname ending with.<targetHostname>, allowingsub.example.comto match anexample.comtarget.
URL Matching Implementation
The actual filtering logic resides in the urlMatchesResearchTarget function exported from src/shared/researchScope.ts. This utility combines the scope-based logic with path normalization to determine inclusion.
import { parseResearchTarget, urlMatchesResearchTarget } from "@/shared/researchScope";
// Define a subfolder target for "example.com/blog"
const target = parseResearchTarget("example.com/blog");
// target.ok === true
// target.target.scope === "subfolder"
// target.target.path === "/blog"
// Filter an array of URLs against the target
const urls = [
"https://example.com/blog/post-1",
"https://example.com/blogging",
"https://sub.example.com/blog/post-2",
];
const filtered = urls.filter((u) => urlMatchesResearchTarget(u, target.target));
// Result: ["https://example.com/blog/post-1"]
// Excludes: "/blogging" (different path) and subdomain (requires "subdomains" scope)
Endpoint-Specific Filter Schemas
Beyond the high-level research scope, each API endpoint defines its own filter schema using Zod validation. These schemas enable fine-grained control over the returned dataset.
Backlinks API Filters
The Backlinks endpoint implements the most comprehensive filtering surface through backlinksRowsFiltersSchema in src/types/schemas/backlinks.ts (lines 4-16 and 27-38). Available filters include:
- Include/Exclude patterns: Free-form strings matched against
url_fromandurl_tofields - Numeric bounds: Minimum and maximum thresholds for domain rank, link authority, and spam scores
- Domain-specific filters:
domainFromparameter for exact matching on the linking domain
import { backlinksRowsFiltersSchema } from "@/types/schemas/backlinks";
const filterPayload = backlinksRowsFiltersSchema.parse({
include: "example.com",
minSpamScore: 10,
maxSpamScore: 80,
domainFrom: "trusted-source.com"
});
// Use in API request
await fetch("/api/backlinks", {
method: "POST",
body: JSON.stringify({
target: "example.com",
scope: "subfolder",
filters: filterPayload,
}),
});
Search Performance and Site Audit Filters
Other modules expose tailored filter interfaces:
- Search Performance: Defined in
src/types/schemas/search-performance.ts(lines 19-25), thesearchPerformanceFilterShapesupports filtering by project, date range, device type, and country. - Site Audit: Implemented in
src/server/mcp/tools/site-audit-tools.ts, filters are applied as post-fetch operations on pages, images, headings, and other crawl data. - Search Console: Uses a dedicated
filterSchemainsrc/server/mcp/tools/search-console-tools.tssupporting dimension, operator, and expression combinations.
Combining Research Scope with Provider Filters
The architecture ensures that provider-side constraints (from DataForSEO) are respected first, followed by application-level post-filtering. This two-stage approach optimizes API usage while maintaining strict boundary enforcement.
import {
parseResearchTarget,
urlMatchesResearchTarget,
} from "@/shared/researchScope";
import { backlinksRowsFiltersSchema } from "@/types/schemas/backlinks";
// 1. Parse the research target
const research = parseResearchTarget("example.com/blog");
// 2. Fetch data from provider (provider handles numeric filters)
const rawBacklinks = await getBacklinksFromProvider({
filters: backlinksRowsFiltersSchema.parse({ minDomainRank: 50 })
});
// 3. Apply URL scope filtering post-fetch
const filtered = rawBacklinks.filter((bk) => {
return urlMatchesResearchTarget(bk.url_to, research.target);
});
Summary
- Four research scopes (
exact_url,subfolder,domain,subdomains) insrc/shared/researchScope.tsdefine the fundamental URL boundaries for any analysis. - The
urlMatchesResearchTargetfunction implements path and hostname matching logic that respects these scope definitions. - Zod-validated filter schemas in
src/types/schemas/backlinks.tsand related files provide granular control over numeric attributes, string patterns, and domain origins. - Layered filtering architecture applies provider-side constraints first, then server-side URL scope verification before returning results to clients.
- Modular design allows each tool (Backlinks, Search Console, Site Audit) to expose endpoint-specific filter surfaces while sharing the core research scope utilities.
Frequently Asked Questions
How does OpenSEO handle subdomain filtering versus root domain filtering?
According to src/shared/researchScope.ts, OpenSEO distinguishes these using separate scope values. The domain scope requires exact hostname equality through the hostMatches utility, while the subdomains scope accepts any hostname ending with .<targetHostname>. This allows blog.example.com and shop.example.com to match an example.com target only when the subdomains scope is explicitly selected.
Can I combine multiple filter types in a single API request?
Yes. OpenSEO supports combining a research scope (which filters by URL path and hostname) with endpoint-specific filter schemas that restrict numeric attributes like spam score or authority. The request payload validates both layers through Zod schemas such as backlinksRowsFiltersSchema, applying AND logic between all specified constraints.
What is the difference between provider-side filtering and post-filtering in OpenSEO?
Provider-side filtering occurs at the DataForSEO API level and handles constraints like numeric ranges for domain rank or link authority. Post-filtering happens within OpenSEO's server logic—specifically in functions like urlMatchesResearchTarget and tools in src/server/mcp/tools/site-audit-tools.ts—to enforce research scope boundaries (exact URL, subfolder, etc.) after data retrieval.
Where are the filter schemas defined for the Backlinks API?
The Backlinks API filters are defined in src/types/schemas/backlinks.ts (lines 4-16 and 27-38). This file exports backlinksRowsFiltersSchema and related Zod schemas that validate include/exclude strings, numeric bounds for authority metrics, and domain-specific parameters before processing requests.
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 →