This release is a pre-release and may not be stable for production use.
spyglass-lite
OSINT recon you can import. Username enumeration, email intelligence, IP, ASN and phone lookups — no external binaries, no API keys, no CLI.
Why spyglass-lite?
- Importable: Stop shelling out to subprocesses. Integrate recon directly into your Python apps.
- Precision First: Unlike other tools,
liteemploys a rigorous verification layer to eliminate false positives from bot walls and generic landing pages. - Offline & Safe: No external binary dependencies. No OS-level CLI installations required.
- Transparent: Results are clearly categorized into
found,absent, andunknown(with reasons).
Table of Contents
- Quick Start
- Architecture
- Features
- Installation
- Configuration
- Usage
- Testing
- Repository Structure
- Contributing
- License
Quick Start
import spyglass_lite as sg
# Quick check: stop after 20 seconds, keep whatever was decided by then
result = sg.username("johndoe", deadline=20)
for profile in result.found:
print(f"Found on {profile.platform}: {profile.url} ({profile.confidence})")
A full scan of all 2,285 sites takes a couple of minutes. For a first look,
pass deadline=; for a narrower look, pass sites=. Both return real
results, not an error:
result = sg.username("johndoe", sites=["github.com", "x.com", "reddit.com"])
print(result) # <UsernameResult 'johndoe': 2 found, 1 unknown>
print(result.partial) # True if a deadline cut the scan short
Fewer hits than expected? Read result.unknown. A site that blocks bots
(403), shows a challenge page, or serves the same shell to every handle is
reported there with the reason — it is not silently counted as "no account".
unknown is the honest middle state, and it is usually the bigger list. A
site that is merely slow shows up as a request failed (…Timeout…): the
default per-request timeout is 10 seconds, and it is better to bound the
scan with deadline= than to shorten every request — a 6s timeout was
measured to save no wall-clock time on a full scan while losing
slow-but-real profiles.
Architecture
spyglass-lite is the importable core of Spyglass OSINT, rewritten for in-process execution. It avoids the orchestration of external CLI tools (Sherlock, Maigret, etc.) by embedding the site rules and engines directly, ensuring consistent results and removing environmental fragility.
Features
| Module | Returns | Needs |
|---|---|---|
username() |
Platforms where a handle exists | 2,285 bundled site rules |
email() |
Validation, MX/provider, SPF/DMARC, Gravatar, breach hits | HTTP + DNS |
ip() |
Geolocation, reverse DNS, WHOIS, passive ports | HTTP + WHOIS/43 |
asn() |
Covering prefix, origin AS, RPKI state | RIPEStat |
phone() |
Carrier, region, line type, formats | [phone] extra |
Installation
pip install spyglass-lite
pip install "spyglass-lite[phone]" # optional: phonenumbers
Python 3.11+. Core dependencies: httpx and dnspython>=2.9.0.
Configuration
import spyglass_lite as sg
# Configure a custom client
client = sg.lite(
timeout=8, # per request, seconds (default 10)
concurrency=100, # global in-flight cap (default 50)
per_host=2, # cap per hostname
retries=1, # transient HTTP statuses only
proxy="socks5://127.0.0.1:9050", # needs spyglass-lite[socks]
)
# Use your custom client
client.username("johndoe", limit=500)
Usage
Username Enumeration
# Stop after 30 seconds; take what has been decided by then
r = sg.username("johndoe", deadline=30)
if r.partial:
print("the clock cut the scan short")
# Specific sites only — display names, domains, or full URLs
r = sg.username("johndoe", sites=["github.com", "https://x.com/someone"])
# A full scan of every bundled site (no deadline). ``limit`` keeps the N most
# popular sites, which are often the bot-walled ones — pair it with deadline=
# rather than expecting it to be quick.
r = sg.username("johndoe", limit=300, deadline=20)
# Raw mode: the dataset rule only, no verification layer and no
# catch-all probe. Used to compare against Sherlock/Maigret output.
r = sg.username("johndoe", judge=False)
# High-confidence results only
for profile in r.high_confidence:
print(profile.url)
# Audit unknown results — this is why the found list is short
for u in r.unknown:
print(f"{u.domain} could not be decided: {u.reason}")
By default every hit passes the verification layer: challenge and
registration pages, empty app shells, search/listing URLs, and sites that
serve the same page for any handle are reported as unknown with the
reason, not as accounts.
Email Intelligence
Tier A (passive) is default. Tier B (active account existence) is opt-in:
sg.email("me@example.com", registrations=True, recipes_path="my-recipes.json")
# Or pass recipes inline / pick bundled ones by domain — see docs/RECIPES.md
sg.email("me@example.com", sites=[{"name": "Example", "url": "https://…", "exists_when": {...}}])
Two things a caller should know before using email():
- It talks to third parties about the address. Gravatar receives the MD5 hash (and the plain address during a profile lookup), and the two breach APIs receive the address itself. Use
gravatar=False, breaches=Falseto keep the lookup local to DNS. sites=fetches the URLs you give it. Literal private, loopback and link-local hosts (127.0.0.1,169.254.169.254,[::1],*.internal) are refused, so it is not a naive SSRF primitive — but a hostname that resolves privately, or a public URL that redirects to one, is still reachable. Do not pass untrusted recipe URLs to a service you expose.
Testing
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,phone]"
pytest -q # offline: every HTTP and DNS call is mocked
ruff check src tests bench scripts
ruff format --check src tests bench scripts
mypy
python -m build
Repository Structure
.
├── bench/ # Benchmarking harness vs Sherlock/Maigret
├── docs/ # Documentation (RECIPES.md, AUDIT.md)
├── scripts/ # Dataset generation + maintainer comparison tools
├── src/spyglass_lite/ # Core package source
└── tests/ # 219 mocked offline tests
Contributing
- Fork the repository.
- Create a feature branch.
- Run
pytestandruff. - Submit a Pull Request.
License
MIT. Derived from Spyglass OSINT (MIT, © Atheer), and includes site rules from Sherlock (MIT) and Maigret (MIT). See NOTICE.
Metadata
Release files for spyglass-lite 0.9.0b0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| spyglass_lite-0.9.0b0.tar.gz | 210.4 kB | Without provenance | Details for spyglass_lite-0.9.0b0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| spyglass_lite-0.9.0b0-py3-none-any.whl | Python 3 | none | any | Details for spyglass_lite-0.9.0b0-py3-none-any.whl |
Total release size: 375.1 kB
Release files / spyglass_lite-0.9.0b0.tar.gz
| Download URL | spyglass_lite-0.9.0b0.tar.gz |
|---|---|
| Size | 210.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f84d8b7b717241a1fea5ac9fef3310e71aee7c55807d105a99d3dfcc55ff300a
|
|
BLAKE2b-256 checksum How to use checksums |
e2b4add418c242df57e03d1061d853a30f9edbe11ce335ca6fc195598fac5867
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / spyglass_lite-0.9.0b0-py3-none-any.whl
| Download URL | spyglass_lite-0.9.0b0-py3-none-any.whl |
|---|---|
| Size | 164.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7b15bc51c038d3d21cdc468c0334d13cac38a7459208a04176b0644b9cd3af38
|
|
BLAKE2b-256 checksum How to use checksums |
e024baabcbda8724d2fba0f8890df50632de5221d7670906dde397560c5c5867
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Security for spyglass-lite 0.9.0b0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.