Most "what's my IP" sites are built for browsers. Call them from a script and you get a full HTML page back, or you're asked to sign up for an API key just to read one line of text.
I run checkip.me, a free IP lookup service, and I wanted it to work well from a terminal. So this post is partly a how-to and partly a plug. Everything below works without an account or a key.
Just the IP
curl checkip.me
203.0.113.42
You get plain text back, which is what you want in a script. On Windows 10/11 use curl.exe, because in PowerShell curl is an alias for Invoke-WebRequest.
If you have both IPv4 and IPv6, curl may pick either one. Add -4 or -6 to choose:
curl -4 checkip.me
curl -6 checkip.me
Details, or a single field
/json gives you the network and location info for your own IP. Put another address in the path to look that one up instead:
curl checkip.me/json
curl checkip.me/1.1.1.1
{
"ip": "203.0.113.42",
"asn": "AS45899",
"isp": "VNPT Corp",
"country": "Vietnam",
"city": "Hanoi",
"timezone": "Asia/Bangkok",
"vpn": false,
"proxy": false,
"tor": false,
"hosting": false,
...
}
If you only need one value, add the field name to the path. It comes back as plain text, so you don't need jq:
curl checkip.me/country # Vietnam
curl checkip.me/1.1.1.1/isp # Cloudflare, Inc.
curl checkip.me/1.1.1.1/hosting # true
curl checkip.me/vpn # false
curl checkip.me/help lists every field.
A tiny dynamic DNS script
This is the most common reason to fetch your IP from a script. It remembers the last address and only does something when it changes:
#!/usr/bin/env bash
set -euo pipefail
STATE_FILE="$HOME/.last_ip"
CURRENT=$(curl -fs -4 --max-time 10 checkip.me)
LAST=$(cat "$STATE_FILE" 2>/dev/null || true)
if [ "$CURRENT" != "$LAST" ]; then
echo "IP changed: ${LAST:-none} -> $CURRENT"
# call your DNS provider's API here
echo "$CURRENT" > "$STATE_FILE"
fi
Run it from cron every few minutes. -f makes curl fail on an HTTP error instead of saving the error message as your "IP", -s keeps the progress meter out of the variable, and --max-time stops a slow network from hanging the job.
Locking SSH to your current IP with Terraform
If you manage a bastion host with Terraform, you can pass your address in at apply time:
variable "my_ip" {
type = string
}
resource "aws_security_group_rule" "ssh_from_me" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${var.my_ip}/32"]
security_group_id = aws_security_group.bastion.id
}
terraform apply -var "my_ip=$(curl -s -4 checkip.me)"
Don't drop the -4. If curl picks IPv6, you end up with an IPv6 address followed by /32, and Terraform refuses it as an invalid CIDR block.
From code
CORS is enabled, so this works from a browser as well as from Node 18+. The JavaScript snippet uses top-level await, so run it in an ES module or the browser console.
import requests
ip = requests.get("https://checkip.me/ip", timeout=10).text.strip()
info = requests.get(f"https://checkip.me/{ip}", timeout=10).json()
print(ip, info["country"], info["isp"])
const info = await fetch("https://checkip.me/json").then(r => r.json());
console.log(info.ip, info.country, info.vpn);
Limits
- Looking up your own IP has no limit. Looking up other IPs is capped at 60 per hour per visitor. Past that you get HTTP 429 with a
Retry-Afterheader. - Results for other IPs are cached, so they can be up to 30 days old.
- Location comes from IP geolocation data. The country is usually right, but the city is often only approximate, like with every service of this kind.
- The VPN, proxy and Tor flags are good hints, but don't rely on them for anything security-critical.
Docs are at checkip.me/api.
What would you add? I'm deciding which fields and endpoints come next, so tell me what you'd actually use.
I run checkip.me. I wrote this post with help from an AI assistant and tested every command before publishing.
Top comments (0)