# Lookups Look up IP addresses and domains, and check your account and the API's health. ## lookup\_ip Returns intelligence for an IPv4 or IPv6 address, given as a `String` or an `IPAddr`. ```ruby def lookup_ip: (String | IPAddr ip) -> IPInfo ``` ```ruby info = client.lookup_ip("213.149.183.127") location = info.location puts "#{location.city}, #{location.country}" if location info.intelligence&.providers&.each do |provider| puts "#{provider.name} (#{provider.type}), last seen #{provider.last_seen}" end ``` - **Cost:** Each lookup costs one credit, and failed lookups are not billed. - **Input:** An address with a prefix or a zone, or one that `IPAddr` cannot parse, raises `ArgumentError` without sending a request. Any other argument type raises `TypeError`. - **Errors:** Private and reserved addresses raise a 422 `APIError`. ## lookup\_ips Looks up as many as 1000 addresses in one request. ```ruby def lookup_ips: (Enumerable[String | IPAddr] ips) -> Array[IPInfo] ``` ```ruby results = client.lookup_ips(["8.8.8.8", IPAddr.new("1.1.1.1")]) results.each do |result| intelligence = result.intelligence if intelligence puts "#{result.ip} #{intelligence.risk_score}" else puts "#{result.ip} no intelligence available" end end ``` - **Cost:** The API bills `ceil(n * 0.9)` credits for the `n` distinct addresses it looks up, and bills nothing if the request fails. - **Results:** The API skips private and reserved addresses and collapses duplicates into one result, so there can be fewer results than addresses. Match results by `ip`, which the API canonicalizes, rather than by position. - **Input:** Any `Enumerable` of `String` and `IPAddr` values works. An empty list returns `[]` without a request. More than 1000 addresses raises `ArgumentError` before anything is sent. An element that `lookup_ip` would reject raises the same error `lookup_ip` would, with its index in the message. An argument that is not `Enumerable`, such as a single `String`, raises `TypeError`. - **Errors:** A batch with no address the API can look up raises a 422 `APIError`. ## lookup\_domain Returns the honeypot traffic Synthient has observed for a domain. ```ruby def lookup_domain: (String domain, ?limit: Integer?) -> DomainInfo ``` - `limit` `Integer?` — Caps the number of recent events returned, from 1 to 1000. `nil` uses the API default of 50. A value that is not a positive `Integer` raises `ArgumentError`. ```ruby domain = client.lookup_domain("example.com", limit: 10) puts "#{domain.status} #{domain.stats.events_24h}" domain.recent_events.each do |event| puts "#{event.timestamp} #{event.target_subdomain} #{event.port}" end ``` - **Cost:** Each lookup costs one credit. - **Input:** Encode internationalized names as Punycode. An empty domain, `"."`, or `".."` raises `ArgumentError` without a request. A domain that is not a `String` raises `TypeError`. - **Results:** A domain that has never been observed returns the status `inactive` and zero counts rather than an error. ## Account and health ### account Returns the account that owns the API key, including its scopes and remaining lookup credits. ```ruby def account: () -> Account ``` ```ruby account = client.account puts "#{account.email} #{account.organization.name} #{account.scopes.join(", ")}" quota = account.lookup_quota puts "#{quota.credits} credits, resetting in #{quota.resets_in} seconds" ``` - **Cost:** It costs no credits. ### health Returns `nil` if the API and its dependencies are healthy. ```ruby def health: () -> nil ``` ```ruby begin client.health rescue Synthient::APIError => e warn "unhealthy: #{e.status}" end ``` - **Errors:** An unhealthy API raises an `APIError` with status 503, which is not retried. The API does not check the key, but the edge in front of it answers a malformed key with a 401 `APIError`.