WHOIS Lookup
One endpoint for four kinds of query. Send a domain, an IP address, a CIDR range or an AS number in query and the response's type (domain, ip or asn) says which shape came back. For a domain: who sold it, when it was created and when it lapses, which name servers it points at, and the derived domain_age_days that most risk rules actually want. Answers come from the registry's RDAP server, with port-43 WHOIS as the fallback for TLDs that have none.
/api/whois-lookupCosts 1 creditAPI keyWHOIS lookup for a domain, IP address, CIDR range or AS number
What to expect for domains
sourcesays which protocol answered:rdapfor every gTLD and the country-code TLDs that run an RDAP server,whoisfor the rest (.de,.ru,.ioand others) and whenever a registry's RDAP server is failing. The fields are the same either way.lookup_statusseparates the three outcomes:ok,not_found(the registry says the domain is not registered) anderror(no answer could be obtained, so the empty fields say nothing about the domain).terms_of_service_urllinks the terms the registry attaches to its RDAP data. Keep it with any record you store.- Dates are ISO-8601 in UTC. A host name below a registered domain (
www.example.co.uk) returns the registration it belongs to. - Since GDPR most registrant details are redacted or replaced by a proxy service. The
registrant_*fields carry what the registry still exposes, andprivacy_protectedsays when the registrant shown is a privacy service or was redacted rather than the real holder. registrar_abuse_emailis where to report the domain. It and the registrar's IANA ID are present for nearly every gTLD.dnssecis the registry's record of the delegation:signedDelegationwhen it holds a DS record, otherwiseunsigned.dnssec_statusis what a validating resolver actually sees:secure,unsigned,zone_signed_no_ds(the zone signs its records but no DS record links it to the registry, so resolvers treat it as unsigned) orbroken(a DS record exists but validation fails, so validating resolvers cannot reach the domain). It isnullwhen the check could not be completed.dnssec_checked_atis when the state was last checked, andds_recordslists the DS records the registry holds.registered_domainis the registration the record describes. It differs fromdomainwhen you query a host name such aswww.example.co.uk.- Age is counted on UTC calendar dates;
domain_age_monthsanddomain_age_yearstick over on the anniversary day. statuscarries the EPP status codes (e.g.clientTransferProhibited). Registries with their own vocabulary are passed through as published.
Request
| Field | Type | Description |
|---|---|---|
| query | string | null | What to look up: a domain (example.com), an IP address, a CIDR range (8.8.8.0/24) or an AS number (AS15169) |
| domain | string | null | Domain name to look up. Older spelling of `query`. |
Authenticate with API key. Send it as the X-API-Key header.
curl -X POST "https://api.senapati.io/api/whois-lookup" \
-H "X-API-Key: $SENAPATI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"example.org"}'Response
Every success is wrapped in { "data": … } with a request_id; the fields below describe data. See Errors for the failure shape.
{"request_id": "req_8f2c1a7e","timestamp": "2026-10-09T09:53:42.094342+00:00","data": {"type": "domain","query": "example.org","domain": "example.org","registered_domain": "example.org","registered": true,"registrar": "ICANN","registrar_iana_id": "376","registrar_url": null,"registrar_abuse_phone": "+1.1234567890","creation_date": "1995-08-31T04:00:00+00:00","expiration_date": "2027-08-30T04:00:00+00:00","updated_date": "2026-08-30T04:00:10.519000+00:00","domain_age_days": 11362,"domain_age_months": 373,"domain_age_years": 31,"name_servers": ["katelyn.ns.cloudflare.com","mitch.ns.cloudflare.com"],"status": ["serverDeleteProhibited","serverTransferProhibited","serverUpdateProhibited","autoRenewPeriod"],"registrant_name": null,"registrant_organization": null,"registrant_state": null,"registrant_country": null,"registrant_email_domain": null,"registrant_email_is_free": null,"privacy_protected": true,"dnssec": "signedDelegation","dnssec_status": "secure","dnssec_zone_signed": true,"dnssec_checked_at": "2026-10-09T09:53:42.088174+00:00","ds_records": [{"key_tag": 2371,"algorithm": 13,"digest_type": 2,"digest": "169521AB547DF3A14C1719A0F0D5289278E22682B53CE9F22D379B922125BF98"}],"emails": [],"terms_of_service_url": "https://thenew.org/org-people/about-pir/policies/","source": "rdap","lookup_status": "ok","fetched_at": "2026-10-09T09:53:42.040845+00:00","elapsed_ms": 743}}
This is the domain shape. The ip and asn shapes are shown below.
| Field | Type | Description |
|---|---|---|
| type | "domain" | default "domain" |
| query | string | null | The normalised query |
| domain* | string | |
| registered_domain | string | null | The registration this record describes. Differs from `domain` when a host name below it was queried |
| registered | boolean | null | False when the registry says the domain is not registered |
| registrar | string | null | |
| registrar_iana_id | string | null | |
| registrar_url | string | null | |
| registrar_abuse_email | string | null | |
| registrar_abuse_phone | string | null | |
| creation_date | string | null | |
| expiration_date | string | null | |
| updated_date | string | null | |
| domain_age_days | integer | null | |
| domain_age_months | integer | null | Whole calendar months since creation |
| domain_age_years | integer | null | |
| name_servers | string[] | |
| status | string[] | |
| registrant_name | string | null | |
| registrant_organization | string | null | |
| registrant_state | string | null | |
| registrant_country | string | null | |
| registrant_email_domain | string | null | |
| registrant_email_is_free | boolean | null | The registrant address is at a free mailbox provider |
| privacy_protected | boolean | null | The registrant shown is a privacy or proxy service, or the registry redacted it |
| dnssec | string | null | Delegation state as the registry records it: signedDelegation when it holds a DS record, else unsigned |
| dnssec_status | "secure" | "unsigned" | "zone_signed_no_ds" | "broken" | null | What a validating resolver sees. secure: DS record present and the zone validates; unsigned: DNSSEC not in use; zone_signed_no_ds: the zone signs its records but no DS record links it to the registry, so it is not protected; broken: a DS record exists but validation fails, so validating resolvers cannot reach the domain; null: could not be determined |
| dnssec_zone_signed | boolean | null | The zone publishes DNSKEY records |
| dnssec_checked_at | string | null | When dnssec_status was last checked (refreshed hourly) |
| ds_records | object[] | DS records the registry holds: key_tag, algorithm, digest_type, digest |
| emails | string[] | |
| terms_of_service_url | string | null | Terms the registry attaches to this data, when it links them |
| source | "rdap" | "whois" | null | Protocol that answered the lookup |
| lookup_status | "ok" | "not_found" | "error" | null | ok: the registry answered; not_found: the domain is not registered; error: no answer could be obtained |
| fetched_at | string | null | When the registry was queried (ISO-8601) |
| elapsed_ms | integer | default 0 |
Errors
- 401missing or invalid API key / token
- 402not enough credits on the account
- 422the request body or a parameter failed validation
- 429plan rate limit exceeded
IP addresses and CIDR ranges
An address or range returns the most specific registered network that contains it, from the regional registry that holds the space (ARIN, RIPE NCC, APNIC, LACNIC or AFRINIC): network, the organization it is registered to, and the abuse_email to report it to. Private and reserved addresses are rejected with a 422 and no charge, since no registry holds a record for them.
The answer is for the address you asked about. A neighbouring address in the same network can sit in a smaller block the registry has reassigned to someone else, so look each address up rather than reusing a result for the whole range.
{"request_id": "req_8f2c1a7e","timestamp": "2026-10-09T09:22:25.147050+00:00","data": {"query": "8.8.8.8","handle": "NET-8-8-8-0-2","organization": "Google LLC","country": null,"registry": "arin","emails": [],"registration_date": "2023-12-28T22:24:33+00:00","updated_date": "2023-12-28T22:24:56+00:00","status": ["active"],"terms_of_service_url": "https://www.arin.net/resources/registry/whois/tou/","source": "rdap","lookup_status": "ok","fetched_at": "2026-10-09T09:22:25.147050+00:00","elapsed_ms": 286,"type": "ip","network": "8.8.8.0/24","start_address": "8.8.8.0","end_address": "8.8.8.255","ip_version": 4,"network_name": "GOGL","parent_handle": "NET-8-0-0-0-0","network_type": "DIRECT ALLOCATION"}}
| Field | Type | Description |
|---|---|---|
| query* | string | |
| handle | string | null | Registry handle |
| organization | string | null | Organisation the resource is registered to |
| country | string | null | |
| registry | string | null | Regional registry: arin, ripe, apnic, lacnic or afrinic |
| abuse_email | string | null | Where the holder accepts abuse reports |
| emails | string[] | |
| registration_date | string | null | |
| updated_date | string | null | |
| status | string[] | |
| terms_of_service_url | string | null | |
| source | "rdap" | null | |
| lookup_status | "ok" | "not_found" | "error" | null | |
| fetched_at | string | null | |
| elapsed_ms | integer | default 0 |
| type | "ip" | default "ip" |
| network | string | null | Most specific registered network containing the query |
| start_address | string | null | |
| end_address | string | null | |
| ip_version | integer | null | |
| network_name | string | null | |
| parent_handle | string | null | |
| network_type | string | null | Allocation type as the registry words it |
AS numbers
AS15169 and 15169 are both accepted. The record names the network operator and its abuse contact. It does not list the prefixes the AS announces; that is routing data, which registries do not hold. Private-use and unallocated numbers return lookup_status: "not_found".
{"request_id": "req_8f2c1a7e","timestamp": "2026-10-09T09:22:25.485915+00:00","data": {"query": "AS15169","handle": "AS15169","organization": "Google LLC","country": null,"registry": "arin","emails": [],"registration_date": "2000-03-30T05:00:00+00:00","updated_date": "2012-02-24T14:44:34+00:00","status": ["active"],"terms_of_service_url": "https://www.arin.net/resources/registry/whois/tou/","source": "rdap","lookup_status": "ok","fetched_at": "2026-10-09T09:22:25.485915+00:00","elapsed_ms": 301,"type": "asn","asn": 15169,"asn_name": "GOOGLE","asn_range": null}}
| Field | Type | Description |
|---|---|---|
| query* | string | |
| handle | string | null | Registry handle |
| organization | string | null | Organisation the resource is registered to |
| country | string | null | |
| registry | string | null | Regional registry: arin, ripe, apnic, lacnic or afrinic |
| abuse_email | string | null | Where the holder accepts abuse reports |
| emails | string[] | |
| registration_date | string | null | |
| updated_date | string | null | |
| status | string[] | |
| terms_of_service_url | string | null | |
| source | "rdap" | null | |
| lookup_status | "ok" | "not_found" | "error" | null | |
| fetched_at | string | null | |
| elapsed_ms | integer | default 0 |
| type | "asn" | default "asn" |
| asn | integer | null | |
| asn_name | string | null | |
| asn_range | string | null | Set when the registry answered with a block of AS numbers |
Notes
fetched_at is when the registry was asked. A lookup that fails is not kept, so a retry a few seconds later usually succeeds. When a registry's RDAP server rate-limits us, lookups for its TLDs are answered over WHOIS until it lifts the limit. IP and AS lookups have no WHOIS fallback and return error instead. The older {"domain": "example.com"} request body still works. A registry that refuses the query returns an empty shape with lookup_status: "error" rather than an HTTP error, so the call still costs one credit.