# How OpenSEO Handles Backlink Data Retrieval and Presentation

> Discover how OpenSEO retrieves and presents backlink data using a layered architecture, Zod validation, Data-for-SEO API integration, caching, React UI, and an MCP tool for AI.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-06-26

---

**OpenSEO retrieves backlink data through a layered architecture that validates requests with Zod schemas, fetches from the Data-for-SEO API via a dedicated service layer with caching, and presents filtered results through both a React-based UI and an MCP tool for AI agents.**

OpenSEO, an open-source SEO platform maintained in the `every-app/open-seo` repository, implements a robust pipeline for backlink analysis. The system separates concerns across multiple layers—from API validation to business logic—ensuring type-safe **backlink data retrieval** and flexible presentation. Understanding how OpenSEO handles backlink data retrieval and presentation reveals a pattern that balances external API dependencies with efficient caching and multi-interface support.

## Architecture Overview

OpenSEO organizes its backlink functionality into five distinct layers:

| Layer | Responsibility | Key File |
|-------|---------------|----------|
| **API Layer** | Validates input and exposes server-side entry points | [`src/serverFunctions/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/backlinks.ts) |
| **Business Layer** | Communicates with Data-for-SEO API, handles caching and pagination | [`src/server/features/backlinks/services/BacklinksService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/backlinks/services/BacklinksService.ts) |
| **Schema Layer** | Defines Zod schemas for request validation and response shaping | [`src/types/schemas/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts) |
| **MCP Layer** | Provides AI-agent tools for programmatic access | [`src/server/mcp/tools/get-backlinks-overview.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/get-backlinks-overview.ts) |
| **UI Layer** | Renders tables, filters, and export functionality | [`web/src/lib/feature-pages.ts`](https://github.com/every-app/open-seo/blob/main/web/src/lib/feature-pages.ts) |

This separation ensures that the core **backlink retrieval logic** remains isolated from presentation concerns, allowing the same service to power both the web interface and AI integrations.

## The Retrieval Flow

The **backlink data retrieval** process follows a strict validation pipeline before reaching external APIs.

### Request Validation

Every incoming request first passes through **Zod** schema validation. In [`src/serverFunctions/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/backlinks.ts), the server functions use `backlinksOverviewInputSchema` and `backlinksRowsPageRequestSchema` to validate parameters:

```typescript
// src/serverFunctions/backlinks.ts
import { BacklinksService } from "@/server/features/backlinks/services/BacklinksService";
import { backlinksRowsPageRequestSchema } from "@/types/schemas/backlinks";

export const getBacklinks = createServerFunction()
  .inputValidator((data) => backlinksRowsPageRequestSchema.parse(data))
  .handler(async (req, ctx) => {
    // req contains: target, tab, page, pageSize, sortField, sortOrder, filters, mode
    return BacklinksService.profileBacklinksPage(req, ctx, /* options */);
  });

```

The validation wrapper ensures that all inputs conform to the expected shape before processing continues.

### Service Layer Execution

Once validated, requests forward to `BacklinksService`, which orchestrates four main operations:

- **`profileOverview`**: Gathers summary statistics (total backlinks, referring domains, top pages)
- **`profileBacklinksPage`**: Retrieves paginated backlink rows
- **`profileReferringDomainsPage`**: Fetches domain-level aggregates
- **`profileTopPagesPage`**: Returns the most linked-to pages

The service maintains a default cache in [`backlinksServiceData.ts`](https://github.com/every-app/open-seo/blob/main/backlinksServiceData.ts) to minimize external API calls and respects pagination parameters (`page`, `pageSize`) to handle large datasets efficiently.

### External API Communication

`BacklinksService` communicates directly with the **Data-for-SEO API**. The service implements caching layers to store results and reduce latency for repeated queries. All pagination, filtering, and sorting logic occurs after retrieval but before the response returns to the client.

## Data Processing and Filtering

After retrieving raw data from the external API, OpenSEO applies business logic to refine results.

### Filtering Logic

The system supports complex filtering through [`backlinksApiFilters.ts`](https://github.com/every-app/open-seo/blob/main/backlinksApiFilters.ts), which processes boolean flags for:

- **Spam**: Identifies low-quality links
- **Broken**: Detects non-responsive backlinks
- **Lost**: Flags removed backlinks
- **Nofollow**: Identifies non-passing links

### Sorting Capabilities

Results sort according to `backlinksRowsSortFieldSchema`, supporting fields like `rank DESC` to prioritize high-authority domains. The service constructs the final payload to match the Zod output schema defined in [`src/types/schemas/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts), ensuring type consistency between server and client.

## Presentation Layer

The **backlink data presentation** layer transforms JSON payloads into interactive tables. The Backlink Checker page, registered in [`web/src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/web/src/routeTree.gen.ts) and configured in [`web/src/lib/feature-pages.ts`](https://github.com/every-app/open-seo/blob/main/web/src/lib/feature-pages.ts) (lines 184-214), renders:

| Column | Data Source |
|--------|-------------|
| **Backlink** | URL of the linking page |
| **Rank** | Domain authority estimate |
| **Signal Flags** | Boolean values for spam, broken, lost, nofollow |
| **Referring Domain** | Grouped view when `mode = "one_per_domain"` |

### UI Features

The interface includes **pagination controls** driven by the `page` and `pageSize` values returned by the service. Users can toggle **dynamic filters** through checkboxes that modify the `backlinksRowsFiltersSchema` values, triggering instant re-requests. An **export function** serializes current rows—including all filter flags—to CSV format for offline analysis.

```typescript
// Front-end usage (React hook)
import { useQuery } from "@tanstack/react-query";

function useBacklinkRows(domain: string, page = 1) {
  return useQuery(
    ["backlinks", domain, page],
    async () => {
      const res = await fetch(`/api/backlinks/rows?target=${domain}&page=${page}`);
      if (!res.ok) throw new Error("Failed to load backlinks");
      return res.json(); // matches the Zod output schema
    },
    { keepPreviousData: true }
  );
}

```

## MCP Integration for AI Agents

OpenSEO exposes backlink functionality to AI agents through the Model Context Protocol (MCP). The tool defined in [`src/server/mcp/tools/get-backlinks-overview.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/get-backlinks-overview.ts) provides a concise interface:

```typescript
// src/server/mcp/tools/get-backlinks-overview.ts
import { BacklinksService } from "@/server/features/backlinks/services/BacklinksService";

export async function get_backlinks_overview(input: { target: string }) {
  // Returns: { backlinks: number | null, referringDomains: number | null, topPages: number | null }
  return BacklinksService.profileOverview(input, { billing: /* current user billing */ });
}

```

This allows AI agents to fetch **backlink overviews** without rendering the full UI, returning only essential metrics for automated analysis.

## Summary

- OpenSEO implements a **five-layer architecture** separating validation, business logic, and presentation concerns for backlink data.
- The **`BacklinksService`** in [`src/server/features/backlinks/services/BacklinksService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/backlinks/services/BacklinksService.ts) handles all Data-for-SEO API communication with built-in caching.
- **Zod schemas** in [`src/types/schemas/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts) enforce type safety across API boundaries.
- The system supports **multi-modal presentation** through both a React-based web interface and an MCP tool for AI agents.
- **Advanced filtering** (spam, broken, lost, nofollow) and sorting occur at the service layer before data reaches the client.

## Frequently Asked Questions

### How does OpenSEO validate backlink requests?

OpenSEO uses **Zod schemas** defined in [`src/types/schemas/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/backlinks.ts) to validate all incoming requests. The server functions in [`src/serverFunctions/backlinks.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/backlinks.ts) wrap validation through an `.inputValidator()` method that parses inputs against schemas like `backlinksRowsPageRequestSchema` before passing them to the business layer.

### What external API does OpenSEO use for backlink data?

The platform integrates with the **Data-for-SEO API** through the `BacklinksService` class. This service handles authentication, request construction, caching of results in [`backlinksServiceData.ts`](https://github.com/every-app/open-seo/blob/main/backlinksServiceData.ts), and error handling for all external backlink queries.

### Can AI agents access OpenSEO backlink data without the web interface?

Yes. OpenSEO exposes a dedicated **MCP tool** in [`src/server/mcp/tools/get-backlinks-overview.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/get-backlinks-overview.ts) that allows AI agents to call `BacklinksService.profileOverview()` directly. This returns a concise JSON object containing backlink counts, referring domains, and top pages without requiring HTML rendering.

### How does the UI handle large backlink datasets?

The presentation layer implements **server-side pagination** controlled by `page` and `pageSize` parameters. The service returns paginated subsets of data, and the React frontend maintains state through TanStack Query with `keepPreviousData` enabled to prevent UI flickering during navigation.