The tempting API for a username lookup is GET /exists?site=x&username=y, returning one boolean. It is also the wrong abstraction for a public web that rate-limits, redirects, changes markup, and occasionally returns a page that looks valid without proving anything about identity. I reviewed the public Beeko AI interface because it exposes a more honest contract: one asynchronous search job, more than 400 catalogued public services, and a status per source instead of one overconfident answer.
I did not submit a search or run an authenticated API call for this review. The observations below come from the live product, workflow, pricing, FAQ, and API sections.
A network failure is not a negative fact
Beeko presents five normalized statuses: FOUND, NOT_FOUND, BLOCKED, UNKNOWN, and ERROR. This looks like a small modeling choice. It prevents several large data-quality mistakes.
Imagine a source responds with HTTP 403. Mapping that to false silently changes “the platform refused this request” into “the username does not exist.” A timeout has the same problem. A parser can also fail when a site ships a new layout. None of those outcomes support a negative claim.
A source record therefore needs more than exists: boolean. At minimum, I would keep:
type SourceResult = {
source: string;
profileUrl: string;
status: 'found' | 'not_found' | 'blocked' | 'unknown' | 'error';
checkedAt: string;
responseTimeMs?: number;
context?: Record<string, string>;
};
The exact schema is less important than preserving the unresolved states. A later retry can update a blocked result without rewriting history as if the first check had never happened. An analyst can filter for confirmed public profiles while still seeing the coverage gaps.
The job should be asynchronous by design
The live API example creates a search job, returns an ID with queued status, then expects the client to poll for progress and fetch a normalized report. That is a better fit than holding one browser request open while hundreds of unrelated services respond.
Sources have different latency, protection, and failure behavior. An asynchronous model lets useful records appear while slower checks continue. It also gives the server room to cap concurrency, apply per-source backoff, and avoid retry storms. The client can render partial progress without pretending the report is complete.
This is where aggregate status deserves careful naming. completed_with_gaps is often more accurate than success. A job may finish its scheduled work while retaining blocked or unknown sources. Completion describes orchestration; source statuses describe evidence. Conflating them makes both harder to reason about.
Beeko says it creates a private search job and offers private history and saved reports to signed-in users. For an implementation, that implies authorization checks on every job and report route. Guessable IDs, public result URLs, or client-only ownership checks would undermine the privacy claim even if every source being checked is public.
Evidence links belong in the response
A FOUND status is still not proof that two accounts belong to one person. Usernames are copied, reassigned, sold, abandoned, and independently chosen. The useful output is a lead that a human can review.
That means the original public profile URL should remain attached to the record. Reviewers need to compare visible names, biographies, linked sites, dates, and other public context. If the API strips the source down to a score, it makes verification harder and encourages clients to treat the score as identity.
Product copy should reinforce the boundary. The Beeko site explicitly says a username match is not ownership proof. It also says the service does not use face recognition, private databases, breach records, passwords, or private-account access. Those are not footnotes; they define the system being sold.
Pricing should preserve the same clarity
The live page currently offers up to three free previews per day across sampled public sources, with no card required. A full report across 400+ sources uses one search credit. Pro and Business plans are shown as a paid beta opening in stages, so I would not build a client that assumes checkout is generally available yet.
Scope belongs in the API response too. A sampled preview and a full report should not share an indistinguishable complete: true flag. Return the requested scope, sources scheduled, sources resolved, and remaining work. A client can then explain what the user actually received.
The broader lesson is straightforward: uncertainty is part of the result, not a backend inconvenience to hide. Preserve it in the schema, expose it in progress, attach it to the source, and let the human decide whether a public lead deserves closer review.
Top comments (0)