FAQ
Common questions about WhisperGraph: query performance, edge types, threat scoring, freshness, anonymous tier, and SDKs.
FAQ Documentation
Common questions about WhisperGraph and the documentation. If a question is missing, open a ticket at console.whisper.security/support or email support@whisper.security.
Why are some queries slow?
The two most common causes:
- Unanchored MATCH —
MATCH (h:HOSTNAME)scans 2.6 billion nodes. Anchor with{name: "..."}or another indexed property. - Fan-out without LIMIT —
(:HOSTNAME)-[:RESOLVES_TO]->(:IPV4)on a CDN hostname can return hundreds of rows per match. Default toLIMIT 20.
See Cypher Best Practices for the full do-this/not-that table.
What's the difference between physical and virtual edges?
Physical edges are backed by stored rows from observations (e.g. RESOLVES_TO from a DNS observation). They support edge-property filters: MATCH (a)-[r:RESOLVES_TO {firstSeen: "..."}]->(b).
Virtual edges are computed at query time from indexed properties. LISTED_IN, ANNOUNCED_BY, OPERATES, CONFLICTS_WITH are virtual. You can traverse them like any edge, but you can't filter on edge properties (because there are none), the source node must be anchored, and they can't be used inside a variable-length pattern.
See Graph Schema and Glossary.
How is the threat score computed?
explain() returns a composite score from:
- Feed count and weights — listed in 5 high-weight feeds scores higher than listed in 1 low-weight feed.
- Recency boost — recent listings get a multiplier; stale listings decay.
- Network density — IPs in dense malicious neighborhoods (other bad indicators in the same /24) score higher.
The score is a weighted composite, not a fixed 0–10 scale — a heavily-listed network can score well into the dozens. The exact formula and weights evolve as feeds are added or rebalanced. Use the factors array in the explain() response to see how the score for a specific indicator was built, and branch automated rules on the level tier (NONE … CRITICAL) rather than the raw number. See Cypher Functions & Procedures for usage.
Can I write to the graph?
No. WhisperGraph is read-only via the public Cypher API. CREATE, MERGE, SET, and DELETE are not supported. The graph is populated by Whisper's ingestion pipeline (DNS, BGP, WHOIS, threat feeds) on a continuous basis.
If you need to keep your own enrichment alongside graph data, store it in your environment (Splunk KV Store, your SIEM, a side database) and JOIN at query time.
How fresh is the data?
| Data source | Refresh cadence |
|---|---|
| DNS observations (A, AAAA, CNAME, NS, MX) | Continuous, sub-minute on hot zones |
| BGP routing | Continuous, real-time updates from public collectors |
| WHOIS | Daily |
| Threat-intel feeds | Hourly incremental, daily full refresh — see Feed Catalog |
| RPKI status | Hourly |
| GeoIP | Weekly |
The whisper.history() procedure returns timestamped snapshots so you can see how data evolved over time.
What happens if my API key isn't being recognised?
If your key is missing, malformed, or revoked, requests run on the unauthenticated tier — they don't return 401. Symptoms: queries that worked previously now hit a depth limit, or rate limits look tighter than expected.
Fix: run CALL whisper.quota() to see which tier you're on, then check your key in the console. For tier limits see the pricing page.
What happens at quota?
Quota is enforced on an hourly and a daily window. When you exceed it you get a 429, and the response carries the rate-limit headers:
X-RateLimit-Limit/X-RateLimit-Remaining/X-RateLimit-Reset— your hourly quota, queries left this hour, and when the hourly window resets.X-DailyLimit-Limit/X-DailyLimit-Remaining/X-DailyLimit-Reset— the matching values for the daily window.
Wait until the relevant reset time and retry, or run CALL whisper.quota() to see your current usage. For per-plan limits, see the pricing page.
Why isn't whisper.history() returning anything for my subdomain?
whisper.history() returning anything for my subdomain?History is keyed on registrable domains. whisper.history("www.example.com") returns nothing; use whisper.history("example.com") and inspect subdomain structure through the graph instead — there is no DOMAIN label or SUBDOMAIN_OF edge, so walk the hostname hierarchy with CHILD_OF:
MATCH (sub:HOSTNAME)-[:CHILD_OF*1..3]->(parent:HOSTNAME {name: "example.com"})
RETURN sub.name LIMIT 50
Are there language-specific SDKs?
Not yet — official Python and JavaScript SDKs are on the roadmap. In the meantime, see the Python and JavaScript examples in Getting Started. Community contributions welcome.
How do I report a bug or ask for help?
See How to ask for help.