API
Use Have I Been Squatted’s API in security workflows.
Client SDKs
Section titled “Client SDKs”For Python, use the haveibeensquatted-python SDK on GitHub. A Go SDK is coming soon.
Authentication
Section titled “Authentication”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.
Scopes
Section titled “Scopes”API tokens are scoped. Requests made with a token must include the scope required by the endpoint.
| Scope | Description | Example endpoints | Details |
|---|---|---|---|
lookup:squat | Look up typosquatted domains | GET /v1/lookup/squat/{domain} | Squat |
lookup:nxdomain | Look up non-existent domains | GET /v1/lookup/nxdomain/{domain} | NXDOMAIN |
analyze | Analyze domains for threats | GET /v1/analyze/{domain} | Analyze |
ct | Certificate Transparency lookups | GET /v1/ct/search, GET /v1/ct/hydrate | Certificate Transparency |
Expiration Policy
Section titled “Expiration Policy”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.
OpenAPI
Section titled “OpenAPI”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.
Endpoints
Section titled “Endpoints”| Endpoint | Description | Documentation |
|---|---|---|
GET /v1/lookup/squat/{domain} | Look up typosquatted permutations for a domain. | Squat |
GET /v1/lookup/nxdomain/{domain} | Look up unregistered permutations for a domain. | NXDOMAIN |
GET /v1/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/meta/usage returns usage information for the authenticated API key as a regular JSON body (not newline-delimited streaming). You can pass the optional query parameter t for the usage window length in minutes (default 1440). Responses use 200 for success, 400 for a bad request, 401 when the API key is missing or invalid, and 429 when rate limited. See the /meta/usage response in openapi.yaml under the OpenAPI specification on this page for the response schema.
export API_TOKEN="YOUR_API_TOKEN"
curl -sS "https://api.haveibeensquatted.com/v1/meta/usage" \ -H "Authorization: Bearer $API_TOKEN"Handling streaming responses
Section titled “Handling streaming responses”Lookup and analyze endpoints stream newline-delimited JSON as work completes (see each operation in the OpenAPI specification on this page). Here are examples of how to read those streams:
import httpximport json
async def lookup_domain(domain: str, api_token: str): async with httpx.AsyncClient() as client: url = f"https://api.haveibeensquatted.com/v1/lookup/squat/{domain}" headers = {"Authorization": f"Bearer {api_token}"}
async with client.stream("GET", url, headers=headers) as response: async for line in response.aiter_lines(): if line.strip(): result = json.loads(line) # Handle each result as it arrives if result["op"] == "Meta": print(f"Progress: {result['data']['data'][0]}/{result['data']['data'][1]}") elif result["op"] == "GeoIp": print(f"Found IP in {result['data']['country']['iso_code']}")async function lookupDomain(domain, apiToken) { const url = "https://api.haveibeensquatted.com/v1/lookup/squat/" + encodeURIComponent(domain); const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${apiToken}`, }, });
const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = "";
while (true) { const { done, value } = await reader.read(); if (done) break;
buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || "";
for (const line of lines) { if (line.trim()) { const result = JSON.parse(line); // Handle each result as it arrives if (result.op === "Meta") { console.log( `Progress: ${result.data.data[0]}/${result.data.data[1]}`, ); } } } }}package main
import ( "bufio" "encoding/json" "fmt" "net/http")
func lookupDomain(domain, apiToken string) error { url := fmt.Sprintf("https://api.haveibeensquatted.com/v1/lookup/squat/%s", domain) req, err := http.NewRequest("GET", url, nil) if err != nil { return err }
req.Header.Add("Authorization", "Bearer "+apiToken)
resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { line := scanner.Text() if line == "" { continue }
var result map[string]interface{} if err := json.Unmarshal([]byte(line), &result); err != nil { return err }
// Handle each result as it arrives if result["op"] == "Meta" { data := result["data"].(map[string]interface{}) progress := data["data"].([]interface{}) fmt.Printf("Progress: %v/%v\n", progress[0], progress[1]) } }
return scanner.Err()}Endpoint-specific URLs and curl examples live on Lookup, Analyze, and Certificate Transparency.
Processing results
Section titled “Processing results”Here are some examples of processing the JSON output using jq:
# Find domains hosted on specific ASNsjq '.[] | select(.ips[].asn.number == 16509)' results.json
# List domains with suspicious hosting locationsjq '.[] | select(.ips[].country.iso_code != "US")' results.json
# Extract all unique ASNsjq '.[] | .ips[].asn.organization' results.json | sort | uniqFor the complete JSON schema reference, see the JSON export reference.