Streaming results
A full lookup checks hundreds of platforms. Streaming means you show the first matches in well under a second instead of waiting for the slowest one.
Opening the stream
The stream takes the same inputs as POST /v2/lookup, as query parameters: email, phone, query, ip, platforms (comma-separated) and checks (comma-separated module keys, e.g. email,ip,riskSignals).
curl -N "https://api.digifootprint.dev/v2/lookup/stream?email=someone@example.com&ip=45.83.91.1&checks=email,ip" \ -H "Authorization: Bearer dfp_your_key"
Depth on a stream
The stream accepts ?deep=true to sweep the full platform set including the long tail. It is the natural pairing: a deep lookup takes the longest, and streaming is what stops that from meaning a long blank wait.
?instant=true does the opposite: the same high-signal subset the batch instant path uses, closed at a latency ceiling. Results still arrive as each check resolves; the ceiling only decides when the stream ends. Use it when you want progressive results but cannot wait for a full sweep - a typical instant stream closes in under a second, against roughly twelve seconds for a standard one.
Its done event fills in meta.instant, meta.deadlineMs, and meta.platformsPending - how many checks were still in flight when the ceiling fired. Anything still pending is omitted, never reported as “not registered”. On any depth, done also carries footprint.lockedPlatforms when the account’s plan held some platforms back - see the lookup reference.
Event types
footprint.result fires once per answered platform, each naming the identity it belongs to. email, phone and ip fire once each, as soon as that module answers, in the same shape as the response’s module objects - with risk.score still null, because scores need every fact in. risk then carries the overall and per-module scores (and signals, if asked for) just before done, which carries the footprint counts and closes the stream. A failure is an error event.
event: footprint.result
data: {"platform":"spotify","registered":true,"method":"account_enumeration","icon":"...","checkedAt":"...","identity":"email"}
event: email
data: {"address":"someone@example.com","deliverable":true,"disposable":false,"freeProvider":false,...,"risk":{"score":null}}
event: ip
data: {"address":"45.83.91.1","isVpn":true,"isHosting":true,"operator":"NordVPN",...,"risk":{"score":null}}
event: footprint.breaches
data: {"breaches":[{"name":"Adobe","date":"2013-10-04"}]}
event: footprint.mentions
data: {"webMentions":null}
event: risk
data: {"risk":{"score":26},"email":{"score":27},"phone":null,"ip":{"score":25}}
event: done
data: {"lookupId":"b12a...","footprint":{"platformsChecked":844,"matches":3,"socialMatches":2,"servedFromCache":false},"meta":{"instant":false,"deadlineMs":null,"platformsPending":null}}Early answer on a stream
Add early=true (and optionally hits=3) and the stream also sends one footprint.early event - with { profiles, elapsedMs } - the moment that many real profiles have arrived and every module you asked for has answered. It is your cue to act; the stream carries on to done either way. Email and phone sent together stream live, each result naming its identity.
In the browser
EventSource cannot send an Authorization header, so proxy the stream through your own backend and attach the key there. That is a feature, not an obstacle: your API key should never reach a browser in the first place.
const source = new EventSource(url); // attach the key server-side
source.addEventListener('footprint.result', (event) => {
const result = JSON.parse(event.data);
render(result); // paint as each one lands
});
source.addEventListener('risk', (event) => {
const { risk, email, phone, ip } = JSON.parse(event.data);
showScores(risk, { email, phone, ip });
});
source.addEventListener('done', (event) => {
const summary = JSON.parse(event.data);
source.close(); // the stream ends here
});