Five-state modeling for asynchronous public-source checks
Public-source username checks look like an embarrassingly parallel problem: take one handle, expand hundreds of profile patterns, request every URL, and collect the results. The network portion is parallel. The meaning of the responses is not. I reviewed Beeko AI as an example of a product that treats the interpretation layer as part of the interface rather than hiding it behind an exists flag.
The live site says it checks more than 400 catalogued public platforms using Sherlock, creates a private search job, and exposes source-level statuses as results arrive. I inspected those public screens without submitting a search, consuming a credit, or exercising the authenticated API.
Model source truth separately from transport outcomes
The visible status vocabulary is FOUND, NOT_FOUND, BLOCKED, UNKNOWN, and ERROR. That is enough to avoid the most common category collapse.
FOUND means the check observed a page or response consistent with a public profile. It does not prove who owns it. NOT_FOUND should be reserved for a source response that genuinely supports absence. BLOCKED describes refusal or protection. UNKNOWN covers an insufficiently interpretable outcome. ERROR says the check failed operationally.
I would model this as a discriminated union rather than an enum attached to loosely related optional fields:
type Result =
| { status: 'found'; sourceUrl: string; checkedAt: string; context: Context }
| { status: 'not_found'; sourceUrl: string; checkedAt: string }
| { status: 'blocked'; sourceUrl: string; checkedAt: string; retryAfter?: string }
| { status: 'unknown'; sourceUrl: string; checkedAt: string; reason: string }
| { status: 'error'; sourceUrl: string; checkedAt: string; code: string };
The union forces callers to handle each state. More importantly, it makes it harder to add a convenience conversion from “anything that is not found” to false. Transport success and evidence truth remain separate concerns.
Let job progress and evidence confidence disagree
The product’s API example creates an asynchronous job, returns an ID with queued status, then expects progress polling and a later report fetch. Hundreds of unrelated sources are a natural fit for this architecture. They have different latency, rate limits, availability, and parser stability.
An orchestration job can be complete while its evidence set contains blocked and unknown records. That is not a contradiction. “Complete” means the scheduled checks reached terminal states. It does not mean every source produced a binary fact.
A progress response might expose:
{
"scope": "full",
"scheduled": 412,
"terminal": 389,
"found": 17,
"notFound": 341,
"blocked": 18,
"unknown": 9,
"error": 4
}
The numbers above are illustrative, not observed search output. The point is that the client can render useful partial information without promoting unresolved work to a negative result.
Idempotent job creation also matters. A browser retry should not create two credit charges and two scans. An idempotency key tied to the authenticated principal, normalized username, scope, and short time window can protect the boundary. Server-side authorization must cover every job and report request because the site describes search history as private even though the upstream pages are public.
Keep the source URL because a hit is only a lead
A normalized record should retain the original public URL and check time. A consumer needs to review visible names, biographies, linked websites, dates, and activity before deciding whether a profile is relevant. Turning those details into a single identity score discards the exact information required to challenge the score.
The Beeko copy repeatedly states that a matching handle does not prove account ownership. It also excludes face recognition, private databases, breach records, passwords, and private-account access. These boundaries should appear in API documentation and downstream UI, not only in marketing FAQs.
If a client uses the results for marketplace trust or brand-protection triage, it should still require platform-native verification before acting. High-impact decisions involving jobs, housing, credit, insurance, or legal outcomes need stronger, purpose-specific processes. A username lookup is not a substitute.
Make scope and commercial availability explicit
The live pricing section currently describes a free preview with up to three daily searches across sampled public sources and no card requirement. A full report across more than 400 sources uses one search credit. Pro and Business plans are shown as a paid beta opening in stages.
That difference belongs in the schema. Return scope: preview or scope: full, plus scheduled and resolved counts. Do not let the same completed label imply that a sampled preview covered the catalog.
The interesting engineering work is not sending 400 requests. It is preserving enough state that neither the server nor the human reader has to lie about what those requests mean.
