How Sherlock's QueryNotify Notification System Enables Real-Time Updates

Sherlock's notification system uses an abstract QueryNotify base class and a concrete QueryNotifyPrint implementation to stream live results to the terminal as each asynchronous HTTP request completes, rather than waiting for the entire scan to finish.

The Sherlock QueryNotify notification system powers the tool's live feedback during username investigations across hundreds of social platforms. In sherlock_project/sherlock.py, the system instantiates a QueryNotifyPrint object that receives callbacks immediately after each site query resolves, delivering real-time status updates directly to the console.

Architecture of the QueryNotify System

The notification architecture follows an observer pattern with three lifecycle hooks. According to the source code in sherlock_project/notify.py, the base class defines the interface while the concrete implementation handles the actual rendering.

The QueryNotify Base Class

Located in sherlock_project/notify.py (lines 14-30), the QueryNotify abstract class establishes the contract for all notifiers. It defines three methods that subclasses must override:

  • start(message) – Called once when scanning begins
  • update(result) – Invoked after every individual site check completes
  • finish(message) – Executed once when the batch finishes

The base implementation is intentionally minimal, allowing different output formats (JSON, CSV, silent mode) to extend the same interface.

QueryNotifyPrint: The Real-Time Console Implementation

The QueryNotifyPrint class (lines 11-87 in sherlock_project/notify.py) provides the colorful terminal output users see during scans. Its constructor accepts three boolean flags that control verbosity:

  • verbose – Appends response times in milliseconds
  • print_all – Displays "not found" and error states, not just successful matches
  • browse – Automatically opens discovered profiles in the default web browser

The update() method receives a QueryResult object containing the site name, URL, status code (CLAIMED, AVAILABLE, WAF, etc.), and timing data. It immediately prints a formatted line with color-coded symbols: green + for claimed usernames, red - for available ones, and yellow ! for errors or WAF blocks.

Integration with Asynchronous Scanning

The real-time behavior emerges from Sherlock's use of SherlockFuturesSession for concurrent HTTP requests. In sherlock_project/sherlock.py (lines 81-91), the core sherlock() function wraps each site query in a future. As soon as any future resolves, the code constructs a QueryResult and calls query_notify.update(result).

Because threads complete independently based on network latency, updates appear in the order responses arrive, not the order sites were defined in the manifest. This creates the characteristic "live" scrolling effect where results from fast-responding sites appear before slower ones.

The lifecycle follows this sequence:

  1. main() instantiates QueryNotifyPrint (lines 111-113 in sherlock_project/sherlock.py)
  2. query_notify.start(username) prints the header banner
  3. Each completed future triggers query_notify.update(result) with real-time coloring
  4. query_notify.finish() emits the final count (line 38 in main())

Configuration and Browser Integration

Users control the notification behavior through command-line arguments passed to the constructor:

query_notify = QueryNotifyPrint(
    result=None,
    verbose=args.verbose,
    print_all=args.print_all,
    browse=args.browse,
)

When --browse (-b) is enabled, the update() method executes webbrowser.open() immediately upon finding a claimed username (lines 12-14 in sherlock_project/notify.py). This launches the profile URL in the default browser before the scan completes, providing instant access to discovered accounts.

The countResults() helper (using a global globvar) tracks how many claimed accounts have been printed. This tally drives the final summary line displayed by finish(), such as "Search completed with 12 results."

Practical Implementation Example

To use the notification system programmatically outside the CLI:

from sherlock_project.notify import QueryNotifyPrint
from sherlock_project.result import QueryResult, QueryStatus

# Initialize with full verbosity and browser auto-open

notifier = QueryNotifyPrint(verbose=True, print_all=True, browse=True)

# Signal scan start

notifier.start("target_user")

# Simulate a discovered account

github_result = QueryResult(
    username="target_user",
    site_name="GitHub",
    site_url_user="https://github.com/target_user",
    status=QueryStatus.CLAIMED,
    query_time=0.245,
)
notifier.update(github_result)  # Prints green + line, opens browser

# Simulate an available username

twitter_result = QueryResult(
    username="target_user",
    site_name="Twitter",
    site_url_user="https://twitter.com/target_user",
    status=QueryStatus.AVAILABLE,
    query_time=0.189,
)
notifier.update(twitter_result)  # Prints red - line (visible because print_all=True)

# Complete the scan

notifier.finish()  # Shows total count

This produces immediate terminal output identical to the standard Sherlock CLI experience, demonstrating how the QueryNotify abstraction decouples result generation from presentation.

Summary

  • Sherlock's notification system centers on the QueryNotify abstract base class in sherlock_project/notify.py, which defines start(), update(), and finish() lifecycle methods.
  • QueryNotifyPrint provides the concrete implementation that renders color-coded, real-time terminal output as each asynchronous HTTP request completes.
  • The system integrates with SherlockFuturesSession to stream results immediately upon response arrival, rather than batching output at scan completion.
  • Configuration flags (verbose, print_all, browse) control output detail and enable automatic browser opening for discovered profiles.
  • The architecture separates concerns between result generation (sherlock.py) and presentation (notify.py), allowing future extensions for JSON, CSV, or webhook notifications.

Frequently Asked Questions

How does Sherlock print results immediately instead of waiting for the full scan?

Sherlock uses Python's concurrent.futures via SherlockFuturesSession to execute HTTP requests in parallel background threads. In sherlock_project/sherlock.py, each completed future immediately calls query_notify.update(result), which triggers QueryNotifyPrint to render the line to the terminal. This callback-based approach ensures output appears in real-time as network responses arrive, rather than collecting all results first.

What do the different colors and symbols mean in Sherlock's output?

According to sherlock_project/notify.py, the update() method assigns visual indicators based on QueryStatus: a green + indicates a claimed username (profile found), a red - marks an available username (profile not found), and a yellow ! signals errors, WAF blocks, or illegal characters. When verbose=True, response times in milliseconds appear after the site name.

Can I use Sherlock's notification system for custom reporting or webhooks?

Yes. The QueryNotify base class in sherlock_project/notify.py provides a clean extension point. By subclassing QueryNotify and overriding start(), update(), and finish(), developers can redirect output to JSON logs, HTTP webhooks, or databases instead of the console. The QueryNotifyPrint implementation demonstrates the pattern for terminal output, but the abstraction supports any consumer that implements the three-method interface.

Does the browser auto-open feature work during the scan or only at the end?

The browser opens immediately upon discovery when using the --browse flag. Inside QueryNotifyPrint.update(), the code checks if self.browse and result.status == QueryStatus.CLAIMED and executes webbrowser.open() synchronously (lines 12-14 in sherlock_project/notify.py). This happens while other threads continue scanning remaining sites, providing instant access to profiles without waiting for the batch to complete.

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 →