{"openapi":"3.1.0","info":{"title":"IP Almanac","summary":"Who else is on this IP, and since when: observed DNS for investigations.","description":"Which domains were seen resolving to an IP and when (reverse IP), what a domain\nresolved to over time (DNS history), and each IP's mapped ASN, published cloud,\nhosting or CDN ranges and PTR names. Built for shared-hosting, phishing and\nbrand-abuse investigations and infrastructure mapping. Coverage is strongest on\nshared-hosting platforms and CDNs serving popular domains; residential and ISP\naddresses usually return the ASN only.\n\nSend an API key as `Authorization: Bearer <key>`; create one at\nhttps://app.ipalmanac.com/dashboard/api-keys. A lookup that returns an answer counts one\nrequest against the workspace's monthly quota; refused input is not counted. Errors\nare `{\"error\": \"<code>\", \"message\": \"<text>\"}`.\n\nHow to read the answers, coverage limits and pagination:\nhttps://skeleton-api-ouax.onrender.com/llms-full.txt. The same lookups are MCP tools at\nhttps://skeleton-api-ouax.onrender.com/mcp.\n\nAlso, per domain: the networks its addresses moved through, at GET /v1/domain/{domain}/hosting.\n\nAlso, per IP: whether it is listed as one of Tor exits, iCloud Private Relay egress and DuckDuckGo and Google crawlers, from the operators' own lists.\n","version":"1.0.0"},"servers":[{"url":"https://skeleton-api-ouax.onrender.com"}],"paths":{"/v1/ping":{"get":{"tags":["v1"],"summary":"Check an API key","description":"Check that your API key works.\n\nAnswers with the server's time. Counts one request against the rate\nlimit and the monthly quota, like a lookup, and carries the same limit\nheaders.","operationId":"ping_v1_ping_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResponse"}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKey":[]}]}},"/v1/ip/{ip}":{"get":{"tags":["v1"],"summary":"Look up a public IP","description":"What was observed about one public IP: its mapped ASN, provider ranges\n(cloud, hosting, CDN), PTR name and the domains seen resolving to it.\nStart here for who else is on an IP and which network hosts it.\n\nCounts one request. Unknown is null, never false; an empty domain list\nmeans none were observed here, not that none exist. provider_ranges also says whether the IP is listed as one of Tor exits, iCloud Private Relay egress and DuckDuckGo and Google crawlers (tor_exit, relay and crawler): how the IP is used, from the operators' own published lists, never who hosts it.","operationId":"lookup_ip","security":[{"APIKey":[]}],"parameters":[{"name":"ip","in":"path","required":true,"schema":{"type":"string","description":"A public IPv4 or IPv6 address. Private, reserved, loopback, link-local, documentation, CGNAT and multicast addresses are refused.","examples":["54.10.20.30"],"title":"Ip"},"description":"A public IPv4 or IPv6 address. A private, reserved, loopback, link-local, documentation, CGNAT or multicast address answers is_bogon true and nothing else, not counted."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IpLookup"},"example":{"ip":"54.10.20.30","asn":{"asn":64500,"route":"54.10.0.0/16","descr":"EXAMPLE-CLOUD - Example Cloud, Inc.","country":"US","source":"IPtoASN"},"is_datacenter":true,"is_cdn":null,"provider":"examplecloud","prefix":"54.10.20.0/24","provider_ranges":[{"prefix":"54.10.20.0/24","provider":"examplecloud","category":"cloud","metadata":{"region":"xx-test-1","service":"COMPUTE"},"first_seen":"2026-08-07T00:00:00+00:00","last_seen":"2026-10-05T00:00:00+00:00"}],"ptr":{"names":["host-54-10-20-30.examplecloud.example"],"status":"positive","checked_at":"2026-10-04T12:00:00+00:00","last_confirmed":"2026-10-04T12:00:00+00:00"},"domains":{"current_count":2,"sample":["alpha.example","gamma.example"],"first_observed":"2026-10-01T03:10:00+00:00","last_confirmed":"2026-10-06T01:40:00+00:00"},"coverage":{"observation_window":{"first":"2026-10-01T00:00:00+00:00","last":"2026-10-06T01:55:00+00:00"},"vantage_count":1,"record_types":["A","AAAA"],"seed":{"source":"Majestic Million","names":1000000},"ptr":{"checked_ips":87000,"observed_ips":588000},"semantics":["Coverage is the seed list, not the whole Internet. Empty means unobserved here, not absent.","is_datacenter and is_cdn are true only when the IP is inside a published provider range of that category; null means no range matched, not that the IP is not hosted.","provider and prefix come only from cloud, hosting and CDN ranges. A range of any other category in provider_ranges describes how the IP is used, not who hosts it."]},"attribution":[{"source":"Majestic Million","by":"Majestic","license":"CC BY 3.0"},{"source":"IPtoASN","by":"Frank Denis","license":"PDDL 1.0"}],"release":{"id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00"},"is_bogon":false,"bogon":null}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized","message":"Invalid API key"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"quota_exceeded","message":"Monthly quota exceeded. Limit: 10000, Used: 10000. Upgrade your plan at https://console.example/dashboard/billing."}}}},"422":{"description":"`invalid_ip`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid_ip","message":"Expected an IPv4 or IPv6 address. No request was counted."}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate_limited","message":"Rate limit exceeded"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"release_unavailable","message":"Lookup data is not available right now. No request was counted."}}}}}}},"/v1/ip/{ip}/domains":{"get":{"tags":["v1"],"summary":"List the domains seen on an IP","description":"Reverse IP: the domains observed resolving to an IP (who else is on it),\none item per answer interval, oldest first. Ended intervals are included\nwith `include_ended=true`. Pass `next_cursor` back as `cursor` for the\nnext page; each page counts one request.","operationId":"ip_domains","security":[{"APIKey":[]}],"parameters":[{"name":"ip","in":"path","required":true,"schema":{"type":"string","description":"A public IPv4 or IPv6 address. Private, reserved, loopback, link-local, documentation, CGNAT and multicast addresses are refused.","examples":["54.10.20.30"],"title":"Ip"},"description":"A public IPv4 or IPv6 address. Private, reserved, loopback, link-local, documentation, CGNAT and multicast addresses are refused."},{"name":"include_ended","in":"query","required":false,"schema":{"type":"boolean","description":"Also list intervals that ended (the domain stopped resolving to the IP).","default":false,"title":"Include Ended"},"description":"Also list intervals that ended (the domain stopped resolving to the IP)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues.","default":100,"title":"Limit"},"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page.","title":"Cursor"},"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IpDomainsPage"},"example":{"ip":"54.10.20.30","include_ended":true,"items":[{"domain":"alpha.example","record_type":"A","first_observed":"2026-10-01T03:10:00+00:00","last_confirmed":"2026-10-06T01:40:00+00:00","ended_at":null,"current":true},{"domain":"beta.example","record_type":"A","first_observed":"2026-10-01T04:00:00+00:00","last_confirmed":"2026-10-03T04:00:00+00:00","ended_at":"2026-10-04T04:00:00+00:00","current":false}],"next_cursor":"eyJ...opaque","coverage":{"observation_window":{"first":"2026-10-01T00:00:00+00:00","last":"2026-10-06T01:55:00+00:00"},"vantage_count":1,"record_types":["A","AAAA"],"seed":{"source":"Majestic Million","names":1000000},"ptr":{"checked_ips":87000,"observed_ips":588000},"semantics":["Coverage is the seed list, not the whole Internet. Empty means unobserved here, not absent.","is_datacenter and is_cdn are true only when the IP is inside a published provider range of that category; null means no range matched, not that the IP is not hosted.","provider and prefix come only from cloud, hosting and CDN ranges. A range of any other category in provider_ranges describes how the IP is used, not who hosts it."]},"attribution":[{"source":"Majestic Million","by":"Majestic","license":"CC BY 3.0"},{"source":"IPtoASN","by":"Frank Denis","license":"PDDL 1.0"}],"release":{"id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00"}}}}},"400":{"description":"`invalid_cursor`: the cursor was not issued for this query. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid_cursor","message":"The cursor does not belong to this query. No request was counted."}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized","message":"Invalid API key"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"quota_exceeded","message":"Monthly quota exceeded. Limit: 10000, Used: 10000. Upgrade your plan at https://console.example/dashboard/billing."}}}},"410":{"description":"`cursor_expired`: the data was updated since the cursor was issued; start again without a cursor. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"cursor_expired","message":"The data was updated since this cursor was issued; start again without a cursor. No request was counted."}}}},"422":{"description":"`invalid_ip` or `non_public_ip`; an out-of-range `limit` or too long `cursor` gets FastAPI's `detail` list. Not counted.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/ValidationErrorResponse"}],"title":"Response 422 Ip Domains"},"example":{"error":"non_public_ip","message":"Private, reserved and multicast addresses have no public data. No request was counted."}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate_limited","message":"Rate limit exceeded"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"release_unavailable","message":"Lookup data is not available right now. No request was counted."}}}}}}},"/v1/domain/{domain}/history":{"get":{"tags":["v1"],"summary":"Show a domain's DNS history","description":"A domain's current A/AAAA state, with staleness, and its answer\nintervals, oldest first. Covers popular registered domains; a new or\nlong-tail domain is usually empty. Each page counts one request.","operationId":"domain_history","security":[{"APIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"A hostname such as example.com. It is lowercased, a trailing dot is dropped and Unicode labels are punycoded.","examples":["example.com"],"title":"Domain"},"description":"A hostname such as example.com. It is lowercased, a trailing dot is dropped and Unicode labels are punycoded."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues.","default":100,"title":"Limit"},"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page.","title":"Cursor"},"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainHistoryPage"},"example":{"domain":"alpha.example","observed":true,"current":[{"record_type":"A","outcome":"positive","outcome_class":"positive","values":["54.10.20.30"],"cnames":[],"last_observed":"2026-10-06T01:40:00+00:00","last_confirmed":"2026-10-06T01:40:00+00:00","last_positive":"2026-10-06T01:40:00+00:00","age_seconds":3600.0,"stale":false,"stale_reason":null},{"record_type":"AAAA","outcome":"timeout","outcome_class":"transient","values":["2600:1f18::10"],"cnames":[],"last_observed":"2026-10-06T01:40:00+00:00","last_confirmed":"2026-10-05T01:40:00+00:00","last_positive":"2026-10-05T01:40:00+00:00","age_seconds":90000.0,"stale":true,"stale_reason":"transient_failure"}],"intervals":[{"record_type":"A","value":"54.10.20.30","first_observed":"2026-10-01T03:10:00+00:00","last_confirmed":"2026-10-06T01:40:00+00:00","ended_at":null,"current":true},{"record_type":"AAAA","value":"2600:1f18::10","first_observed":"2026-10-01T03:10:00+00:00","last_confirmed":"2026-10-05T01:40:00+00:00","ended_at":null,"current":true}],"next_cursor":null,"coverage":{"observation_window":{"first":"2026-10-01T00:00:00+00:00","last":"2026-10-06T01:55:00+00:00"},"vantage_count":1,"record_types":["A","AAAA"],"seed":{"source":"Majestic Million","names":1000000},"ptr":{"checked_ips":87000,"observed_ips":588000},"semantics":["Coverage is the seed list, not the whole Internet. Empty means unobserved here, not absent.","is_datacenter and is_cdn are true only when the IP is inside a published provider range of that category; null means no range matched, not that the IP is not hosted.","provider and prefix come only from cloud, hosting and CDN ranges. A range of any other category in provider_ranges describes how the IP is used, not who hosts it."]},"attribution":[{"source":"Majestic Million","by":"Majestic","license":"CC BY 3.0"},{"source":"IPtoASN","by":"Frank Denis","license":"PDDL 1.0"}],"release":{"id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00"}}}}},"400":{"description":"`invalid_cursor`: the cursor was not issued for this query. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid_cursor","message":"The cursor does not belong to this query. No request was counted."}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized","message":"Invalid API key"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"quota_exceeded","message":"Monthly quota exceeded. Limit: 10000, Used: 10000. Upgrade your plan at https://console.example/dashboard/billing."}}}},"410":{"description":"`cursor_expired`: the data was updated since the cursor was issued; start again without a cursor. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"cursor_expired","message":"The data was updated since this cursor was issued; start again without a cursor. No request was counted."}}}},"422":{"description":"`invalid_domain`; an out-of-range `limit` or too long `cursor` gets FastAPI's `detail` list. Not counted.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/ValidationErrorResponse"}],"title":"Response 422 Domain History"},"example":{"error":"invalid_domain","message":"Expected a hostname such as example.com. No request was counted."}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate_limited","message":"Rate limit exceeded"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"release_unavailable","message":"Lookup data is not available right now. No request was counted."}}}}}}},"/v1/asn/{asn}":{"get":{"tags":["v1"],"summary":"Look up an ASN","description":"One network: the IP ranges the IPtoASN mapping assigns to the ASN, its\nname and registry country, the published cloud, hosting and CDN ranges\ninside them, and how many IPs and domains were observed there. Ranges\nare paged: pass `next_cursor` back as `cursor`; each page counts one\nrequest. An ASN the mapping does not list answers with no ranges.","operationId":"lookup_asn","security":[{"APIKey":[]}],"parameters":[{"name":"asn","in":"path","required":true,"schema":{"type":"string","description":"An AS number, as 64500 or AS64500.","examples":["AS64500"],"title":"Asn"},"description":"An AS number, as 64500 or AS64500."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues.","default":100,"title":"Limit"},"description":"Items per page, 1 to 1000. Free keys get at most 100 a page; a larger limit is lowered and `next_cursor` continues."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page.","title":"Cursor"},"description":"The previous page's `next_cursor`, unchanged. Omit it for the first page."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsnLookup"},"example":{"asn":64500,"is_bogon":true,"bogon":{"first":64496,"last":64511,"name":"Documentation","rfc":"RFC 5398"},"name":"EXAMPLE-CLOUD - Example Cloud, Inc.","country":"US","source":"IPtoASN","routes":{"ipv4":1,"ipv6":1},"ipv4_addresses":65536,"providers":[{"provider":"examplecloud","category":"cloud","ranges":5}],"observed":{"ips":2,"domains":2},"ranges":[{"first":"54.10.0.0","last":"54.10.255.255","prefixes":["54.10.0.0/16"]}],"next_cursor":"eyJ...opaque","coverage":{"observation_window":{"first":"2026-10-01T00:00:00+00:00","last":"2026-10-06T01:55:00+00:00"},"vantage_count":1,"record_types":["A","AAAA"],"seed":{"source":"Majestic Million","names":1000000},"ptr":{"checked_ips":87000,"observed_ips":588000},"semantics":["Coverage is the seed list, not the whole Internet. Empty means unobserved here, not absent.","is_datacenter and is_cdn are true only when the IP is inside a published provider range of that category; null means no range matched, not that the IP is not hosted.","provider and prefix come only from cloud, hosting and CDN ranges. A range of any other category in provider_ranges describes how the IP is used, not who hosts it."]},"attribution":[{"source":"Majestic Million","by":"Majestic","license":"CC BY 3.0"},{"source":"IPtoASN","by":"Frank Denis","license":"PDDL 1.0"}],"release":{"id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00"}}}}},"400":{"description":"`invalid_cursor`: the cursor was not issued for this query. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid_cursor","message":"The cursor does not belong to this query. No request was counted."}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized","message":"Invalid API key"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"quota_exceeded","message":"Monthly quota exceeded. Limit: 10000, Used: 10000. Upgrade your plan at https://console.example/dashboard/billing."}}}},"410":{"description":"`cursor_expired`: the data was updated since the cursor was issued; start again without a cursor. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"cursor_expired","message":"The data was updated since this cursor was issued; start again without a cursor. No request was counted."}}}},"422":{"description":"`invalid_asn`: not an AS number from 1 to 4294967295; an out-of-range `limit` or too long `cursor` gets FastAPI's `detail` list. Not counted.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"$ref":"#/components/schemas/ValidationErrorResponse"}],"title":"Response 422 Lookup Asn"},"example":{"error":"invalid_asn","message":"Expected an AS number such as AS64500. No request was counted."}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate_limited","message":"Rate limit exceeded"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"release_unavailable","message":"Lookup data is not available right now. No request was counted."}}}}}}},"/v1/domain/{domain}/hosting":{"get":{"tags":["v1"],"summary":"Show the networks a domain moved through","description":"Hosting history: the networks a domain's A/AAAA answers moved through,\none run per stretch on one network (routing ASN and cloud, hosting or CDN\nprovider), oldest first, with when it was first and last seen there and\nwhen it left. A move happened between one run's `last_confirmed` and its\n`ended_at`, never at an instant. Covers popular registered domains;\nhistory starts with the first observation (`coverage.observation_window`).\nEach run's network is the current release's mapping and ranges applied\nto its addresses, not the routing at the time. Counts one request.","operationId":"domain_hosting","security":[{"APIKey":[]}],"parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","description":"A hostname such as example.com. It is lowercased, a trailing dot is dropped and Unicode labels are punycoded.","examples":["example.com"],"title":"Domain"},"description":"A hostname such as example.com. It is lowercased, a trailing dot is dropped and Unicode labels are punycoded."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainHosting"},"example":{"domain":"beta.example","hosting":[{"public":true,"asn":64500,"as_name":"EXAMPLE-CLOUD - Example Cloud, Inc.","provider":"examplecloud","category":"cloud","addresses":1,"record_types":["A"],"first_observed":"2026-10-01T04:00:00+00:00","last_confirmed":"2026-10-03T04:00:00+00:00","ended_at":"2026-10-04T04:00:00+00:00","current":false},{"public":true,"asn":64501,"as_name":"EXAMPLE-CDN - Example CDN, Inc.","provider":"examplecdn","category":"cdn","addresses":1,"record_types":["A"],"first_observed":"2026-10-04T04:00:00+00:00","last_confirmed":"2026-10-06T00:00:00+00:00","ended_at":null,"current":true}],"truncated":false,"coverage":{"observation_window":{"first":"2026-10-01T00:00:00+00:00","last":"2026-10-06T01:55:00+00:00"},"vantage_count":1,"record_types":["A","AAAA"],"seed":{"source":"Majestic Million","names":1000000},"ptr":{"checked_ips":87000,"observed_ips":588000},"semantics":["Coverage is the seed list, not the whole Internet. Empty means unobserved here, not absent.","is_datacenter and is_cdn are true only when the IP is inside a published provider range of that category; null means no range matched, not that the IP is not hosted.","provider and prefix come only from cloud, hosting and CDN ranges. A range of any other category in provider_ranges describes how the IP is used, not who hosts it."]},"attribution":[{"source":"Majestic Million","by":"Majestic","license":"CC BY 3.0"},{"source":"IPtoASN","by":"Frank Denis","license":"PDDL 1.0"}],"release":{"id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00"}}}}},"401":{"description":"`unauthorized`: the API key is missing, malformed, revoked or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized","message":"Invalid API key"}}}},"402":{"description":"`quota_exceeded` or `project_cap_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"quota_exceeded","message":"Monthly quota exceeded. Limit: 10000, Used: 10000. Upgrade your plan at https://console.example/dashboard/billing."}}}},"422":{"description":"`invalid_domain`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid_domain","message":"Expected a hostname such as example.com. No request was counted."}}}},"429":{"description":"`rate_limited`: over the plan's requests per minute, or more than 100 refused keys from your address in a minute. Retry after the `Retry-After` header's seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate_limited","message":"Rate limit exceeded"}}}},"503":{"description":"`release_unavailable` (no lookup data loaded) or `service_unavailable`. Not counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"release_unavailable","message":"Lookup data is not available right now. No request was counted."}}}}}}},"/health":{"get":{"summary":"Check service health","description":"Whether the service is up, and the data release it serves.\n\nNeeds no key and counts nothing. Answers 503 with `status: unhealthy`\nwhen the database is unreachable. `release` names the served data\nrelease (`id`, `built_at`, `age_seconds`). Without one only the lookups\nfail (503, not counted), so the service stays healthy.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{},"example":{"status":"healthy","checks":{"postgres":"ok","redis":"ok","release":"ok"},"release":{"status":"ok","id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00","age_seconds":3600.0}}}}},"503":{"description":"The database is unreachable.","content":{"application/json":{"example":{"status":"unhealthy","checks":{"postgres":"error","redis":"ok","release":"ok"},"release":{"status":"ok","id":"20261006T020000Z","built_at":"2026-10-06T02:00:00+00:00","age_seconds":3600.0}}}}}}}}},"components":{"schemas":{"AnswerInterval":{"properties":{"record_type":{"type":"string","title":"Record Type","description":"A or AAAA."},"value":{"type":"string","title":"Value","description":"The address answered; may be private or reserved."},"first_observed":{"type":"string","title":"First Observed","description":"When this answer was first seen."},"last_confirmed":{"type":"string","title":"Last Confirmed","description":"When this answer was last seen."},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ended At","description":"When the domain stopped giving this answer; null while current."},"current":{"type":"boolean","title":"Current","description":"Part of the domain's latest answer."}},"type":"object","required":["record_type","value","first_observed","last_confirmed","ended_at","current"],"title":"AnswerInterval"},"AsnBogonBlock":{"properties":{"first":{"type":"integer","title":"First","description":"The block's first AS number."},"last":{"type":"integer","title":"Last","description":"The block's last AS number."},"name":{"type":"string","title":"Name","description":"Its name in the IANA registry, e.g. Private Use."},"rfc":{"type":"string","title":"Rfc","description":"The RFC that defines it."}},"type":"object","required":["first","last","name","rfc"],"title":"AsnBogonBlock"},"AsnInfo":{"properties":{"asn":{"type":"integer","title":"Asn","description":"The routing ASN in the mapping (never 0)."},"route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Route","description":"The CIDR block of the mapped range that holds the IP."},"descr":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descr","description":"The ASN's name as IPtoASN gives it."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"Registry country of the ASN."},"source":{"type":"string","title":"Source","description":"An imported mapping (IPtoASN), not a BGP observation."},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"The routing ASN's primary function: isp, hosting, cdn, business, banking, education_research or government_admin; null when unclassified. It is this API's own classification where it has one and ipverse as-metadata's where it has none (category_source says which). It describes the network that routes the IP, not the IP's tenant: a hosting ASN can route corporate gateway egress, and an ISP's network can be classified hosting. It never sets is_datacenter, which stays true only inside a published range."},"category_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category Source","description":"Where category comes from: own (this API's classification of the network) or ipverse (ipverse as-metadata, used only where there is no own classification); null when category is null."},"network_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network Role","description":"The routing ASN's connectivity role from its BGP neighbours (ipverse as-metadata): tier1_transit, major_transit, midsize_transit, access_provider, content_network or stub; null when unknown."}},"type":"object","required":["asn","route","descr","country","source"],"title":"AsnInfo"},"AsnLookup":{"properties":{"asn":{"type":"integer","title":"Asn"},"is_bogon":{"type":"boolean","title":"Is Bogon","description":"true for an AS number IANA reserves for private use, documentation or another special purpose (AS_TRANS, RFC 6996, 5398, 7300). Such a number is not a public network: the public routing tables the mapping comes from carry none, so expect no ranges. false does not mean the number is allocated."},"bogon":{"anyOf":[{"$ref":"#/components/schemas/AsnBogonBlock"},{"type":"null"}],"description":"The block that makes the AS number a bogon; null if not."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"The ASN's name as IPtoASN gives it; null when unmapped."},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","description":"Registry country of the ASN."},"source":{"type":"string","title":"Source","description":"An imported mapping (IPtoASN), not a BGP observation."},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"The routing ASN's primary function: isp, hosting, cdn, business, banking, education_research or government_admin; null when unclassified. It is this API's own classification where it has one and ipverse as-metadata's where it has none (category_source says which). It describes the network that routes the IP, not the IP's tenant: a hosting ASN can route corporate gateway egress, and an ISP's network can be classified hosting. It never sets is_datacenter, which stays true only inside a published range."},"category_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category Source","description":"Where category comes from: own (this API's classification of the network) or ipverse (ipverse as-metadata, used only where there is no own classification); null when category is null."},"network_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Network Role","description":"The routing ASN's connectivity role from its BGP neighbours (ipverse as-metadata): tier1_transit, major_transit, midsize_transit, access_provider, content_network or stub; null when unknown."},"routes":{"$ref":"#/components/schemas/AsnRoutes"},"ipv4_addresses":{"type":"integer","title":"Ipv4 Addresses","description":"IPv4 addresses in those ranges."},"providers":{"items":{"$ref":"#/components/schemas/AsnProvider"},"type":"array","title":"Providers","description":"Published cloud, hosting and CDN ranges inside the ASN's ranges, per feed, most first. Empty means none matched, not that the network hosts nothing."},"observed":{"anyOf":[{"$ref":"#/components/schemas/AsnObserved"},{"type":"null"}],"description":"What was observed in the ASN's ranges; null when this release does not count it (unknown, never zero)."},"ranges":{"items":{"$ref":"#/components/schemas/AsnRange"},"type":"array","title":"Ranges","description":"The mapped ranges, IPv4 then IPv6, in address order."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass back as `cursor` for the next page of ranges; null on the last."},"coverage":{"additionalProperties":true,"type":"object","title":"Coverage","description":"What was observed, from where, and how to read it (`semantics`). Empty means unobserved here, not absent."},"attribution":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attribution","description":"Required source attribution."},"release":{"$ref":"#/components/schemas/ReleaseInfo"}},"type":"object","required":["asn","is_bogon","bogon","name","country","source","routes","ipv4_addresses","providers","observed","ranges","next_cursor","coverage","attribution","release"],"title":"AsnLookup"},"AsnObserved":{"properties":{"ips":{"type":"integer","title":"Ips","description":"IPs in the ASN's ranges with at least one current domain."},"domains":{"type":"integer","title":"Domains","description":"Distinct domains whose current A/AAAA answer is one of them."}},"type":"object","required":["ips","domains"],"title":"AsnObserved"},"AsnProvider":{"properties":{"provider":{"type":"string","title":"Provider","description":"The feed that lists the ranges."},"category":{"type":"string","title":"Category","description":"cloud, hosting or cdn."},"ranges":{"type":"integer","title":"Ranges","description":"That feed's ranges lying inside the ASN's mapped ranges."}},"type":"object","required":["provider","category","ranges"],"title":"AsnProvider"},"AsnRange":{"properties":{"first":{"type":"string","title":"First","description":"First address of the mapped range."},"last":{"type":"string","title":"Last","description":"Last address of the mapped range."},"prefixes":{"items":{"type":"string"},"type":"array","title":"Prefixes","description":"The range as CIDR blocks (a mapped range need not be one)."}},"type":"object","required":["first","last","prefixes"],"title":"AsnRange"},"AsnRoutes":{"properties":{"ipv4":{"type":"integer","title":"Ipv4","description":"IPv4 ranges the mapping assigns to the ASN."},"ipv6":{"type":"integer","title":"Ipv6","description":"IPv6 ranges the mapping assigns to the ASN."}},"type":"object","required":["ipv4","ipv6"],"title":"AsnRoutes"},"BogonBlock":{"properties":{"prefix":{"type":"string","title":"Prefix","description":"The IANA special-purpose block."},"name":{"type":"string","title":"Name","description":"Its name in the IANA registry, e.g. Private-Use."},"rfc":{"type":"string","title":"Rfc","description":"The RFC that defines it."}},"type":"object","required":["prefix","name","rfc"],"title":"BogonBlock"},"CurrentRecord":{"properties":{"record_type":{"type":"string","title":"Record Type","description":"A or AAAA."},"outcome":{"type":"string","title":"Outcome","description":"The last query's result: positive, nodata, nxdomain, timeout, servfail or malformed."},"outcome_class":{"type":"string","title":"Outcome Class","description":"positive, negative or transient."},"values":{"items":{"type":"string"},"type":"array","title":"Values","description":"Addresses in the current state; a transient failure keeps the earlier answer."},"cnames":{"items":{"type":"string"},"type":"array","title":"Cnames","description":"The CNAME chain in the current state."},"last_observed":{"type":"string","title":"Last Observed","description":"When the name was last queried."},"last_confirmed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Confirmed","description":"When the current state was last confirmed."},"last_positive":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Positive","description":"When the name last answered positively."},"age_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Age Seconds","description":"Seconds since last_confirmed."},"stale":{"type":"boolean","title":"Stale"},"stale_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stale Reason","description":"transient_failure, never_confirmed or older_than_seven_days."}},"type":"object","required":["record_type","outcome","outcome_class","values","cnames","last_observed","last_confirmed","last_positive","age_seconds","stale","stale_reason"],"title":"CurrentRecord"},"DomainHistoryPage":{"properties":{"domain":{"type":"string","title":"Domain"},"observed":{"type":"boolean","title":"Observed","description":"Whether this name has DNS state here. False means it was never queried (it is outside the seed list), so the empty lists mean not covered, not that the domain has no DNS."},"current":{"items":{"$ref":"#/components/schemas/CurrentRecord"},"type":"array","title":"Current","description":"Latest A/AAAA state."},"intervals":{"items":{"$ref":"#/components/schemas/AnswerInterval"},"type":"array","title":"Intervals","description":"Oldest first."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass back as `cursor` for the next page; null on the last."},"coverage":{"additionalProperties":true,"type":"object","title":"Coverage","description":"What was observed, from where, and how to read it (`semantics`). Empty means unobserved here, not absent."},"attribution":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attribution","description":"Required source attribution."},"release":{"$ref":"#/components/schemas/ReleaseInfo"}},"type":"object","required":["domain","observed","current","intervals","next_cursor","coverage","attribution","release"],"title":"DomainHistoryPage"},"DomainHosting":{"properties":{"domain":{"type":"string","title":"Domain"},"hosting":{"items":{"$ref":"#/components/schemas/HostingRun"},"type":"array","title":"Hosting","description":"One run per stretch of time the domain answered on one network (ASN and provider), oldest first. A domain that leaves a network and comes back gets a new run. Networks are this release's mapping and ranges applied to every address, not as of the run's dates."},"truncated":{"type":"boolean","title":"Truncated","description":"More than 5000 answer intervals: the runs cover the current ones and the most recent, and the oldest are left out: the history goes back further than the first run's first_observed."},"coverage":{"additionalProperties":true,"type":"object","title":"Coverage","description":"What was observed, from where, and how to read it (`semantics`). Empty means unobserved here, not absent."},"attribution":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attribution","description":"Required source attribution."},"release":{"$ref":"#/components/schemas/ReleaseInfo"}},"type":"object","required":["domain","hosting","truncated","coverage","attribution","release"],"title":"DomainHosting"},"DomainSummary":{"properties":{"current_count":{"type":"integer","title":"Current Count","description":"Domains whose latest A/AAAA answer includes this IP."},"sample":{"items":{"type":"string"},"type":"array","title":"Sample","description":"Up to 10, oldest observation first."},"first_observed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Observed","description":"Earliest observation of any domain on this IP, ended or not."},"last_confirmed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Confirmed","description":"Latest confirmation of any domain on this IP."}},"type":"object","required":["current_count","sample","first_observed","last_confirmed"],"title":"DomainSummary"},"ErrorResponse":{"properties":{"error":{"type":"string","title":"Error"},"message":{"type":"string","title":"Message"}},"type":"object","required":["error","message"],"title":"ErrorResponse"},"HostingRun":{"properties":{"public":{"type":"boolean","title":"Public","description":"false for answers that are not public addresses (private, reserved); those have no network."},"asn":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Asn","description":"The routing ASN this release's IPtoASN mapping gives the addresses; null when unmapped. Not the address holder or the tenant."},"as_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"As Name","description":"The ASN's name as IPtoASN gives it."},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Provider of the most specific published cloud, hosting or CDN range holding the addresses; null outside every range."},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"cloud, hosting or cdn."},"addresses":{"type":"integer","title":"Addresses","description":"Distinct addresses answered in this run."},"record_types":{"items":{"type":"string"},"type":"array","title":"Record Types","description":"A, AAAA or both."},"first_observed":{"type":"string","title":"First Observed","description":"When an answer on this network was first seen: the domain arrived by then."},"last_confirmed":{"type":"string","title":"Last Confirmed","description":"When an answer on this network was last seen."},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ended At","description":"When the domain was seen no longer answering on this network; it left between last_confirmed and ended_at. null while current."},"current":{"type":"boolean","title":"Current","description":"The domain still answers on this network."}},"type":"object","required":["public","asn","as_name","provider","category","addresses","record_types","first_observed","last_confirmed","ended_at","current"],"title":"HostingRun"},"IpDomain":{"properties":{"domain":{"type":"string","title":"Domain"},"record_type":{"type":"string","title":"Record Type","description":"A or AAAA."},"first_observed":{"type":"string","title":"First Observed","description":"When this answer was first seen."},"last_confirmed":{"type":"string","title":"Last Confirmed","description":"When this answer was last seen."},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ended At","description":"When the domain stopped resolving to the IP; null while current."},"current":{"type":"boolean","title":"Current","description":"The domain's latest answer includes the IP."}},"type":"object","required":["domain","record_type","first_observed","last_confirmed","ended_at","current"],"title":"IpDomain"},"IpDomainsPage":{"properties":{"ip":{"type":"string","title":"Ip"},"include_ended":{"type":"boolean","title":"Include Ended"},"items":{"items":{"$ref":"#/components/schemas/IpDomain"},"type":"array","title":"Items","description":"Answer intervals, oldest first."},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Pass back as `cursor` for the next page; null on the last."},"coverage":{"additionalProperties":true,"type":"object","title":"Coverage","description":"What was observed, from where, and how to read it (`semantics`). Empty means unobserved here, not absent."},"attribution":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attribution","description":"Required source attribution."},"release":{"$ref":"#/components/schemas/ReleaseInfo"}},"type":"object","required":["ip","include_ended","items","next_cursor","coverage","attribution","release"],"title":"IpDomainsPage"},"IpLookup":{"properties":{"ip":{"type":"string","title":"Ip"},"asn":{"anyOf":[{"$ref":"#/components/schemas/AsnInfo"},{"type":"null"}],"description":"null when no mapped range holds the IP."},"is_datacenter":{"anyOf":[{"type":"boolean","const":true},{"type":"null"}],"title":"Is Datacenter","description":"true inside a published cloud or hosting range; null is unknown, never false."},"is_cdn":{"anyOf":[{"type":"boolean","const":true},{"type":"null"}],"title":"Is Cdn","description":"true inside a published CDN range (an edge, not a customer origin); null is unknown, never false."},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider","description":"Provider of the most specific cloud, hosting or CDN range."},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix","description":"The most specific cloud, hosting or CDN range."},"provider_ranges":{"items":{"$ref":"#/components/schemas/ProviderRange"},"type":"array","title":"Provider Ranges","description":"Every provider range holding the IP, most specific first."},"ptr":{"anyOf":[{"$ref":"#/components/schemas/PtrInfo"},{"type":"null"}],"description":"null when the IP was never checked."},"domains":{"$ref":"#/components/schemas/DomainSummary"},"is_bogon":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Bogon","description":"true for an address in an IANA special-purpose block that is not globally reachable (private, loopback, link-local, CGNAT, documentation, reserved) or multicast. Such an address has no public data: everything else in the answer is empty, and it is not counted. false does not mean the address is allocated or announced."},"bogon":{"anyOf":[{"$ref":"#/components/schemas/BogonBlock"},{"type":"null"}],"description":"The block that makes the IP a bogon; null if not."},"coverage":{"additionalProperties":true,"type":"object","title":"Coverage","description":"What was observed, from where, and how to read it (`semantics`). Empty means unobserved here, not absent."},"attribution":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attribution","description":"Required source attribution."},"release":{"$ref":"#/components/schemas/ReleaseInfo"}},"type":"object","required":["ip","asn","is_datacenter","is_cdn","provider","prefix","provider_ranges","ptr","domains","coverage","attribution","release"],"title":"IpLookup"},"PingResponse":{"properties":{"ok":{"type":"boolean","title":"Ok"},"timestamp":{"type":"string","title":"Timestamp"}},"type":"object","required":["ok","timestamp"],"title":"PingResponse"},"ProviderRange":{"properties":{"prefix":{"type":"string","title":"Prefix"},"provider":{"type":"string","title":"Provider","description":"The feed that lists the range."},"category":{"type":"string","title":"Category","description":"The published range's kind: cloud, hosting or cdn. How it is used: tor_exit, relay and crawler (Tor exits, iCloud Private Relay egress and DuckDuckGo and Google crawlers). Only cloud, hosting and cdn ranges set provider and prefix."},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"Fields the provider's feed publishes, e.g. region, service."},"first_seen":{"type":"string","title":"First Seen","description":"When the provider's feed first listed it."},"last_seen":{"type":"string","title":"Last Seen","description":"When the provider's feed last listed it."}},"type":"object","required":["prefix","provider","category","metadata","first_seen","last_seen"],"title":"ProviderRange","examples":[{"prefix":"171.25.193.25/32","provider":"tor-exit","category":"tor_exit","metadata":{"kind":"exit_observed"},"first_seen":"2026-10-07T00:42:05+00:00","last_seen":"2026-10-07T00:42:05+00:00"},{"prefix":"172.224.226.0/27","provider":"apple-private-relay","category":"relay","metadata":{},"first_seen":"2026-10-07T00:00:00+00:00","last_seen":"2026-10-07T00:00:00+00:00"},{"prefix":"66.249.64.0/27","provider":"google-common-crawlers","category":"crawler","metadata":{"bot":"google-common-crawlers","operator":"Google"},"first_seen":"2026-10-06T14:47:46+00:00","last_seen":"2026-10-06T14:47:46+00:00"},{"prefix":"20.191.45.212/32","provider":"duckduckbot","category":"crawler","metadata":{"bot":"duckduckbot","operator":"DuckDuckGo"},"first_seen":"2026-10-06T23:00:00+00:00","last_seen":"2026-10-06T23:00:00+00:00"}]},"PtrInfo":{"properties":{"names":{"items":{"type":"string"},"type":"array","title":"Names","description":"Current PTR names, usually one; empty when none answered."},"status":{"type":"string","title":"Status","description":"Outcome of the last check: positive, nxdomain, nodata, timeout or servfail. A transient failure keeps the earlier names."},"checked_at":{"type":"string","title":"Checked At","description":"When the last check ran."},"last_confirmed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Confirmed","description":"When the names were last confirmed; null if never."}},"type":"object","required":["names","status","checked_at","last_confirmed"],"title":"PtrInfo"},"ReleaseInfo":{"properties":{"id":{"type":"string","title":"Id","description":"The data release (snapshot) this answer comes from."},"built_at":{"type":"string","title":"Built At","description":"When the release was built (UTC, ISO 8601)."}},"type":"object","required":["id","built_at"],"title":"ReleaseInfo"},"ValidationErrorResponse":{"properties":{"detail":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Detail","description":"FastAPI's validation errors, e.g. for `limit` out of range."}},"type":"object","required":["detail"],"title":"ValidationErrorResponse"}},"securitySchemes":{"APIKey":{"type":"http","description":"Your API key, which starts with sk_. Create one in the console.","scheme":"bearer"}}}}