Skip to content

API

Use Have I Been Squatted’s API in security workflows.

For Python, use the haveibeensquatted-python SDK on GitHub. A Go SDK is coming soon.

API tokens are created in the platform. Go to Settings -> API Keys, create a token, select scopes, and optionally set an expiration. The token is only shown once after creation.

API tokens are scoped. Requests made with a token must include the scope required by the endpoint.

Scope Description Details
lookup:squat Look up typosquatted domains Squat
lookup:nxdomain Look up non-existent domains NXDOMAIN
analyze Analyze domains for threats Analyze
ct Certificate transparency lookups Certificate transparency

When creating a token, choose a 7, 30, 60, or 90 day expiration, or select No expiration. Expired or revoked tokens stop working immediately, and the full token value is only shown once at creation.

Endpoint Description Documentation
GET /v2/squat/{domain} Look up typosquatted permutations for a domain. Squat
GET /v2/nxdomain/{domain} Look up unregistered permutations for a domain. NXDOMAIN
GET /v2/discover/{domain} Alias for the NXDOMAIN discovery operation. NXDOMAIN
GET /v2/analyze/{domain} Analyze a domain for infrastructure and threat signals. Analyze
GET /v1/ct/search Search certificate transparency names by pattern. Certificate transparency search
GET /v1/ct/search/domains Search certificate transparency data for exact FQDNs. Certificate transparency search domains
GET /v1/ct/hydrate Hydrate certificate transparency occurrences into certificate details. Certificate transparency occurrences hydration
GET /v1/meta/usage Retrieve usage totals and hourly breakdown. Usage
GET /v1/retrieve/{uuid} Retrieve a retained lookup result. Retained results

Use the API origin https://api.haveibeensquatted.com. Encode the fully qualified domain name (FQDN) as one URL path segment.

Terminal window
export API_TOKEN="YOUR_API_TOKEN"
domain="example.com"
curl -N "https://api.haveibeensquatted.com/v2/squat/$domain" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Accept: application/x-ndjson"

Lookup and Analyze endpoints default to streamed newline-delimited JSON (NDJSON) with Content-Type: application/x-ndjson.

  • Send Accept: application/x-ndjson to request streaming explicitly.
  • Send Accept: application/json to receive one buffered merged JSON object.

The merged object is keyed by domain. A top-level meta member can contain non-progress metadata such as an error, timeout, or retained-result identifier.

Lookup and Analyze responses can include:

  • X-Hibs-Request-Id, which identifies the API request for diagnostics.
  • X-Hibs-Result-Id, which identifies the retained result when persistence applies.

X-Hibs-Result-Id is optional. Its absence does not make an otherwise complete response invalid.

Each non-empty NDJSON line is one object with an op field and operation-specific data. Domain events also include permutation.

Metadata events use op: "Meta" and a nested data.kind value:

  • Progress reports current and total work.
  • Heartbeat keeps an active response open.
  • StoredResult supplies a retained-result identifier.
  • Error marks the request as failed.
  • Timeout marks the response as incomplete.
  • Done marks successful stream completion when no error or timeout was observed.

Do not report success from the HTTP status alone. A streaming client must observe Done; end-of-stream before Done is incomplete.

import json
from urllib.parse import quote
import httpx
async def lookup_domain(domain: str, api_token: str):
encoded_domain = quote(domain, safe="")
url = f"https://api.haveibeensquatted.com/v2/squat/{encoded_domain}"
headers = {
"Authorization": f"Bearer {api_token}",
"Accept": "application/x-ndjson",
}
completed = False
async with httpx.AsyncClient() as client:
async with client.stream("GET", url, headers=headers) as response:
response.raise_for_status()
async for line in response.aiter_lines():
if not line.strip():
continue
event = json.loads(line)
if event.get("op") != "Meta":
# Process the domain signal. Unknown operations should be
# ignored or retained rather than treated as fatal.
continue
metadata = event.get("data", {})
kind = metadata.get("kind")
detail = metadata.get("data")
if kind == "Progress":
print(f"Progress: {detail[0]}/{detail[1]}")
elif kind == "StoredResult":
print(f"Stored result: {detail}")
elif kind == "Error":
raise RuntimeError(detail or "Lookup failed")
elif kind == "Timeout":
raise RuntimeError(f"Lookup timed out: {detail}")
elif kind == "Done":
completed = True
if not completed:
raise RuntimeError("Lookup response ended before completion")
Status Meaning Retry guidance
400 Invalid domain or request parameters Correct the request.
401 Missing or invalid authentication Replace or restore the token.
403 Missing scope, plan access, or authorization Correct access before retrying.
429 Rate limit reached Use bounded backoff and honor Retry-After.
503 The service is unavailable or at capacity Use bounded backoff and honor Retry-After.

Do not automatically repeat a request after its response stream has started. A new invocation creates a new analysis request and can duplicate work.

GET /v1/meta/usage returns usage information for the authenticated API key as a regular JSON body. Pass the optional query parameter t for the usage window length in minutes. The default is 1440 minutes.

Terminal window
export API_TOKEN="YOUR_API_TOKEN"
curl -sS "https://api.haveibeensquatted.com/v1/meta/usage" \
-H "Authorization: Bearer $API_TOKEN"

Authenticated lookup streams can emit Meta messages with kind: StoredResult and a universally unique identifier (UUID). Use the UUID with the retrieval endpoint and select a format path:

Terminal window
curl "https://api.haveibeensquatted.com/v1/retrieve/$uuid"
curl "https://api.haveibeensquatted.com/v1/retrieve/$uuid/ndjson"
curl "https://api.haveibeensquatted.com/v1/retrieve/$uuid/json"
curl "https://api.haveibeensquatted.com/v1/retrieve/$uuid/csv"

These examples operate on a buffered merged JSON response:

Terminal window
# List returned domains
jq -r 'to_entries[] | select(.key != "meta") | .key' results.json
# List domains with a phishing score above 0.5
jq -r 'to_entries[] | select(.key != "meta") | select(.value.classification.phishing? > 0.5) | .key' results.json
# Extract public web identifiers
jq 'to_entries[] | select(.key != "meta") | .value.identifiers[]?' results.json

For stored and application export fields, see the JSON export reference. Rule-facing selectors are documented in the signals reference.

The OpenAPI 3.1 Have I Been Squatted API specification is available as follows.

For an interactive view, open the OpenAPI specification in Swagger Editor.