I was building upsonar.io - it monitors uptime, SSL certificates, and domain expiration. When I got to domain expiry alerts, I hit a wall.
Checked .com, .org, .net - worked fine. Then I tried google.dev.
Connection refused.
The Investigation
First thought: maybe Google blocks WHOIS queries for their domains? Let me check the WHOIS server for .dev:
$ whois -h whois.iana.org dev
Output (trimmed):
domain: DEV
organisation: Charleston Road Registry Inc.
...
whois:
status: ACTIVE
remarks: Registration information: https://www.registry.google
See that empty whois: field? There's no WHOIS server for .dev at all.
Turns out, Google owns 40+ TLDs and none of them have a WHOIS server:
.app, .page, .new, .google, .youtube, .android, .chrome, .zip, .mov, .dad, .phd, .prof, .meme, .foo, .nexus, .rsvp, .fly, .ing, .boo, .channel...
You can check any TLD on IANA's root database - look for the "WHOIS Server" field.
This isn't a bug. It's by design.
Why No WHOIS?
Google launched these TLDs after 2012, when ICANN started pushing a new protocol called RDAP (Registration Data Access Protocol).
The main problem with WHOIS? No standard format. Every registrar returns different text, different field names, different date formats.
RDAP fixes this:
-
JSON responses - no more parsing random text formats, just
json.Unmarshal -
Standard field names - expiration date is always
eventAction: "expiration", not "Expiry Date" vs "Registry Expiry Date" vs "paid-till" - RESTful API - just a GET request, works with any HTTP client
In January 2025, ICANN officially sunset WHOIS for gTLDs. RDAP is now the standard.
How to Check Domain Expiration with RDAP
If you want to monitor domain expiration or build a domain expiration lookup tool, RDAP is the way to go. Here's working Go code:
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
)
func main() {
domain := "google.dev"
if len(os.Args) > 1 {
domain = os.Args[1]
}
expiry, err := getExpiry(domain)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
days := int(time.Until(expiry).Hours() / 24)
fmt.Printf("%s expires %s (%d days)\n", domain, expiry.Format("2006-01-02"), days)
}
func getExpiry(domain string) (time.Time, error) {
tld := domain[strings.LastIndex(domain, ".")+1:]
// RDAP endpoints (from IANA bootstrap file)
endpoints := map[string]string{
"com": "https://rdap.verisign.com/com/v1/domain/",
"net": "https://rdap.verisign.com/net/v1/domain/",
"org": "https://rdap.publicinterestregistry.org/rdap/domain/",
"dev": "https://pubapi.registry.google/rdap/domain/",
"app": "https://pubapi.registry.google/rdap/domain/",
"io": "https://rdap.nic.io/domain/",
}
endpoint, ok := endpoints[tld]
if !ok {
return time.Time{}, fmt.Errorf("unknown TLD: %s", tld)
}
resp, err := http.Get(endpoint + domain)
if err != nil {
return time.Time{}, err
}
defer resp.Body.Close()
var data struct {
Events []struct {
Action string `json:"eventAction"`
Date string `json:"eventDate"`
} `json:"events"`
}
json.NewDecoder(resp.Body).Decode(&data)
for _, e := range data.Events {
if e.Action == "expiration" {
return time.Parse(time.RFC3339, e.Date)
}
}
return time.Time{}, fmt.Errorf("no expiration date found")
}
Run it:
$ go run main.go google.dev
google.dev expires 2025-09-14 (252 days)
$ go run main.go github.com
github.com expires 2026-10-09 (642 days)
Clean JSON, standard fields, no parsing nightmares.
RDAP Coverage
All gTLDs (.com, .org, .dev, etc.) support RDAP. Most ccTLDs (.uk, .de, .ru, etc.) still rely on WHOIS, but coverage is growing every year.
The full list of RDAP endpoints is in IANA's bootstrap file - this is where you get the correct endpoint for any TLD.
Key Takeaways
- New TLDs (especially Google's) have no WHOIS server - RDAP only
- ICANN deprecated WHOIS in 2025 - RDAP is the future
- RDAP returns JSON - no more parsing 50 different date formats
Don't want to deal with RDAP yourself? upsonar.io/docs/domain-expiry - monitors domain expiration and alerts before they expire. Free tier available.
This is part of the Building a Monitoring Tool in Go series. Follow for more.
What TLDs are you monitoring? Hit any weird edge cases?
Top comments (0)