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
src/shared/targetDetection.ts— Core detection logic anddetectTargetimplementationsrc/types/schemas/domain.ts—normalizeDomainfunction for hostname canonicalizationsrc/server/mcp/tools/get-serp-results.ts— Consumes detected targets for SERP API callssrc/server/mcp/tools/dataforseo-research-tools.ts— Handles SERP, local SERP, and competitor queries using target typing
Summary
- The
detectTargetalgorithm uses a simple deterministic heuristic: no whitespace + contains dot = potential domain - Normalization via
normalizeDomainvalidates 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →