Your public IP from the command line or in code
Get your public IP address in bash, fish, PowerShell, Python, Node, Go and PHP: IPv4 or IPv6, with timeouts and a check that the answer is an IP.
Your computer knows its own network address, but not the public address the internet sees: that one belongs to your router or your provider, and only a server you talk to can tell you. This guide shows how to ask one from a shell or a program, how to get the IPv4 or the IPv6 address on purpose, and how to make the answer safe to use in a script. All the examples call host.sh and need no key, and the shell, Python, Node, Go and PHP ones run in host.sh's CI on every change.
The endpoints
| URL | Answer |
|---|---|
https://host.sh/ip | Your address as plain text, for terminals (the curl page has more fields) |
https://api.host.sh/v2/ip/ | Your address as plain text; ?format=json gives {"ip": "…"} |
https://ipv4.host.sh/v2/ip/ | Your IPv4 address, even on a connection that prefers IPv6 |
The API endpoints are described in Lookup. On a dual-stack connection, host.sh and api.host.sh usually answer with your IPv6 address, because systems prefer IPv6 by default when it works; the IPv4 vs IPv6 guide explains why.
In a shell
ip=$(curl -sS --fail-with-body --max-time 5 https://host.sh/ip) || exit 1 case "$ip" in ''|*[!0-9a-fA-F.:]*) echo "not an IP address: $ip" >&2; exit 1;; esac echo "My public IP is $ip"
The same in other shells and tools:
set ip (curl -fsS --max-time 5 https://host.sh/ip) # fish $ip = (Invoke-RestMethod -TimeoutSec 5 https://host.sh/ip).Trim() # PowerShell wget -qO- --timeout=5 https://host.sh/ip # wget http --body https://host.sh/ip # HTTPie curl -4 -fsS https://host.sh/ip # only IPv4 curl -6 -fsS https://host.sh/ip # only IPv6, fails without it
Two curl flags make the difference in a script: --fail-with-body turns an HTTP error into a failing exit status (curl 7.76 and later; -f does the same on older versions, without printing the error's body), and --max-time keeps a broken network from hanging the script. The || exit 1 stops the script when curl fails, instead of carrying on with an empty variable, and the case line is a quick sanity check: it stops the script when the answer has anything but the digits, letters, dots and colons of an address, such as an error page. It can't tell 999.999.999.999 from a real address; for a full check, parse the answer in one of the languages below.
In code
Each example sets a timeout and parses the answer as an IP address before using it:
import ipaddress
import urllib.request
# your public address as websites see it; ipv4.host.sh for the IPv4 one
with urllib.request.urlopen("https://api.host.sh/v2/ip/", timeout=5) as r:
ip = ipaddress.ip_address(r.read().decode().strip()) # ValueError if the answer isn't an address
print(ip, "IPv" + str(ip.version))import { isIP } from 'node:net';
// Node 18 or later: fetch is built in
const res = await fetch('https://api.host.sh/v2/ip/?format=json', { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error('HTTP ' + res.status);
const { ip } = await res.json();
if (!isIP(ip)) throw new Error('not an IP address: ' + ip);
console.log(ip);package main
import (
"fmt"
"io"
"net/http"
"net/netip"
"strings"
"time"
)
func main() {
client := &http.Client{Timeout: 5 * time.Second}
res, err := client.Get("https://api.host.sh/v2/ip/")
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
panic(res.Status)
}
body, err := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
ip, err := netip.ParseAddr(strings.TrimSpace(string(body)))
if err != nil {
panic(err)
}
fmt.Println(ip)
}<?php
// your public IPv4 address; api.host.sh answers with whichever family the connection used
$ctx = stream_context_create(['http' => ['timeout' => 5]]);
$body = file_get_contents('https://ipv4.host.sh/v2/ip/', false, $ctx); // false on a network or HTTP error
$ip = $body === false ? '' : trim($body);
if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
fwrite(STDERR, "no IP address in the answer\n");
exit(1);
}
echo $ip, "\n";The Python, Node and Go examples ask api.host.sh, so they usually print your IPv6 address when your connection has one; the PHP one asks ipv4.host.sh for the IPv4 address. ipv4.host.sh always answers with IPv4, but api.host.sh answers with whichever family the connection used, which is IPv4 when IPv6 isn't working. If you need an IPv6 address, check the parsed address's version (or use curl -6, which fails without IPv6).
Make it safe to use
- Validate the answer. Parse it as an address, as the code examples do (the shell
caseline is only a sanity check), before putting it in a firewall rule, a DNS record or a log. A proxy error page or an empty response should stop your script, not become your "IP". - Set a timeout and handle failure: a script that runs at boot may start before the network is up.
- Ask once, not in a loop.
curl host.shanswers from a copy kept for an hour for your address, and tokenless lookups through the API are limited to 10 a minute per IP address (rate limits). A dynamic-DNS script that checks every few minutes is fine. - Pick the family you need. A DNS
Arecord or an IPv4 allow-list needs your IPv4 address: askipv4.host.shor usecurl -4. AnAAAArecord needscurl -6, and note that many systems use temporary IPv6 addresses that change daily (public vs private IP).
More than the address
The same request can tell you more: curl host.sh prints your address with its location, network and time zone as JSON, and curl host.sh/country_code prints a single field.
In code, https://api.host.sh/v2/lookup/ returns the full answer for your own address without a token; looking up other addresses needs one. See the curl page and the API docs.
FAQ
How do I get my public IP address from the command line?
Run curl https://host.sh/ip. It prints your public IP address as plain text, with no key or sign-up. Add -4 or -6 to get your IPv4 or IPv6 address.
Why does my script get an IPv6 address when I wanted IPv4?
Your connection has both, and systems prefer IPv6 by default. Ask https://ipv4.host.sh/v2/ip/, which can only be reached over IPv4, or use curl -4.
How do I get my public IP in Python?
Read https://api.host.sh/v2/ip/ with urllib.request and a timeout, strip the newline, and check it with ipaddress.ip_address. The example above does all three with the standard library only.
Is there a limit on how often I can ask?
Tokenless lookups through the API are limited to 10 a minute per IP address, and curl host.sh keeps your answer for an hour. Asking every few minutes, as a dynamic-DNS script does, is fine.
This page as Markdown: /guides/public-ip-command-line/index.md