# URL Filtering Capabilities in OpenSEO: Research Scopes and Endpoint Filters Explained

> Explore OpenSEO's robust URL filtering. Discover hierarchical research scopes for exact URL, subfolder, domain, and subdomain matching, plus Zod-validated endpoint filters for spam and authority scores.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: deep-dive
- Published: 2026-09-01

---

**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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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. The `hostMatches` helper enforces strict equality between the URL hostname and target hostname.
- **`subdomains`**: Matches the hostname plus any of its subdomains. The same `hostMatches` function accepts any hostname ending with `.<targetHostname>`, allowing `sub.example.com` to match an `example.com` target.

## URL Matching Implementation

The actual filtering logic resides in the `urlMatchesResearchTarget` function exported from [`src/shared/researchScope.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/researchScope.ts). This utility combines the scope-based logic with path normalization to determine inclusion.

```typescript
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`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts) (lines 4-16 and 27-38). Available filters include:

- **Include/Exclude patterns**: Free-form strings matched against `url_from` and `url_to` fields
- **Numeric bounds**: Minimum and maximum thresholds for domain rank, link authority, and spam scores
- **Domain-specific filters**: `domainFrom` parameter for exact matching on the linking domain

```typescript
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`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/search-performance.ts) (lines 19-25), the `searchPerformanceFilterShape` supports filtering by project, date range, device type, and country.
- **Site Audit**: Implemented in [`src/server/mcp/tools/site-audit-tools.ts`](https://github.com/every-app/open-seo/blob/main/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 `filterSchema` in [`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts) supporting 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.

```typescript
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`) in [`src/shared/researchScope.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/researchScope.ts) define the fundamental URL boundaries for any analysis.
- **The `urlMatchesResearchTarget` function** implements path and hostname matching logic that respects these scope definitions.
- **Zod-validated filter schemas** in [`src/types/schemas/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts) and 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.