How OpenSEO's Target Detection Algorithm Works for SERP Analysis

OpenSEO uses a deterministic heuristic in the detectTarget function to classify user input as either a domain or keyword, routing SERP queries to the appropriate DataForSEO endpoints.

The every-app/open-seo repository implements a lightweight but robust target detection system that sits at the heart of its SERP analysis pipeline. This algorithm determines whether a user-provided string represents a domain (e.g., example.com) or a keyword/brand phrase (e.g., best coffee maker), ensuring accurate API routing and predictable credit consumption.

The Detection Algorithm Explained

The complete logic lives in src/shared/targetDetection.ts within the detectTarget exported function. The algorithm follows five deterministic steps:

1. Input Sanitization

Raw input is trimmed of leading and trailing whitespace:

const trimmed = rawInput.trim();

2. Domain Pattern Recognition

The algorithm checks three conditions to flag domain-like input:

  • Non-empty string (trimmed.length > 0)
  • No internal whitespace (!/\s/.test(trimmed))
  • Contains at least one dot (trimmed.includes("."))

3. Domain Normalization

Candidates passing the pattern check are passed to normalizeDomain from src/types/schemas/domain.ts. This function:

  • Converts to lowercase
  • Strips protocols (e.g., https://)
  • Validates proper hostname structure

4. Post-Normalization Validation

After normalization, the result must still contain a dot to qualify as a valid domain:

if (hostname.includes(".")) {
  return { type: "domain", value: hostname };
}

5. Type Assignment

Success returns { type: "domain", value: hostname }. Any failure—including normalization errors—falls back to { type: "keyword", value: trimmed }.

Complete Implementation

Here's the full detectTarget function as implemented in src/shared/targetDetection.ts (lines 13-29):

export function detectTarget(rawInput: string): DetectedTarget {
  const trimmed = rawInput.trim();
  const looksLikeDomain =
    trimmed.length > 0 && !/\s/.test(trimmed) && trimmed.includes(".");

  if (looksLikeDomain) {
    try {
      const hostname = normalizeDomain(trimmed);
      if (hostname.includes(".")) {
        return { type: "domain", value: hostname };
      }
    } catch {
      // Normalization failed → fall back to keyword.
    }
  }

  return { type: "keyword", value: trimmed };
}

Why Target Detection Matters for SERP Workflows

SERP-related tools in OpenSEO—including get-serp-results, rank tracking, and local SERP endpoints—accept a target parameter that can be either type. The detectTarget algorithm enables:

  • Domain-level queries: "Who ranks for example.com?" → routed to domain-centric DataForSEO endpoints
  • Keyword-level queries: "SERP for 'best coffee maker'" → triggers keyword-focused fetches

This separation prevents accidental API misuse and keeps credit consumption predictable.

Practical Usage Examples

import { detectTarget } from "@/shared/targetDetection";

const inputs = [
  "example.com",
  "  https://example.com  ",
  "Best coffee maker",
  "sub.domain.co.uk",
  "invalid domain",
];

inputs.forEach((i) => {
  const result = detectTarget(i);
  console.log(`${i} → ${result.type}: ${result.value}`);
});

/* Expected output:
example.com → domain: example.com
  https://example.com   → domain: example.com
Best coffee maker → keyword: Best coffee maker
sub.domain.co.uk → domain: sub.domain.co.uk
invalid domain → keyword: invalid domain
*/

Key Source Files

Summary

  • The detectTarget algorithm uses a simple deterministic heuristic: no whitespace + contains dot = potential domain
  • Normalization via normalizeDomain validates and canonicalizes domain candidates before final classification
  • Graceful fallback treats any invalid domain as a keyword, ensuring robustness
  • Critical for API routing — correct classification prevents wasted DataForSEO credits and returns relevant SERP data

Frequently Asked Questions

How accurate is OpenSEO's target detection algorithm?

The algorithm achieves high accuracy for typical inputs through its conservative approach. It only classifies strings as domains when they pass strict pattern matching and survive normalization. Strings with spaces, missing dots, or invalid hostname structures automatically fall back to keyword classification. According to the source code, edge cases like protocol-prefixed URLs (https://example.com) are handled during the normalization phase in src/types/schemas/domain.ts.

Can the target detection be bypassed or overridden?

The current implementation in src/shared/targetDetection.ts does not expose override parameters—classification is fully automatic. Tools consuming the output (such as get-serp-results.ts) receive the structured { type, value } object and route requests accordingly. If you need to force keyword treatment for a domain-like string, you could introduce intentional whitespace (e.g., "example .com"), though this is not a recommended pattern.

What happens if domain normalization throws an error?

Failed normalization triggers the catch block in detectTarget, immediately falling back to keyword classification without crashing the pipeline. This defensive design means malformed domains like "not..valid.com" or strings with invalid characters become keywords rather than causing exceptions. The original trimmed input is preserved as the keyword value.

Does the algorithm support internationalized domain names (IDNs)?

The detection logic itself is IDN-agnostic—it checks for dots and whitespace only. Actual IDN support depends on the normalizeDomain implementation in src/types/schemas/domain.ts. Review that file's source code to verify whether punycode conversion or Unicode normalization is applied during hostname canonicalization.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →