Get Your Public IP with curl

Copy a working command to print your public IP from curl, wget, HTTPie, PowerShell or Python, in plain text or JSON.

Try it, with your own connection

The outputs below are not samples. They are what these commands return for you right now, rendered on the server. Your address is 216.73.216.7, seen from Columbus, Ohio, United States on Anthropic, PBC (AS16509).

  1. curl whatsmyip.fyi
    216.73.216.7

    Content negotiation at the apex. curl gets text, a browser gets the page.

  2. curl -s https://whatsmyip.fyi/ip
    216.73.216.7

    The explicit endpoint. Use this one in scripts.

  3. curl -s https://whatsmyip.fyi/json | jq -r '.ip, .country, .isp'
    216.73.216.7
    US
    Anthropic, PBC

    The JSON record, three fields of it.

  4. curl -s https://whatsmyip.fyi/ip.md
    # Your public IP
    IP: 216.73.216.7 (IPv4)
    Location: Columbus, Ohio, US (approximate)
    ISP: Anthropic, PBC (AS16509)
    Source: https://whatsmyip.fyi/ip.md

    Small enough to paste into a prompt, and it carries its own source URL.

Nothing on this page was stored. The address above came from the request that fetched it and is gone when the response finished. See the privacy policy for what that means in detail.

The short version

curl whatsmyip.fyi

Prints your public address and a newline. Nothing else.

Plain text

# the apex, using content negotiation
curl whatsmyip.fyi

# the explicit endpoint, better in scripts
curl -s https://whatsmyip.fyi/ip

Use /ip in a script. It does not depend on your HTTP library sending a User-Agent the server recognises.

Forcing one address family

# IPv4 only
curl -4 v4.whatsmyip.fyi

# IPv6 only
curl -6 v6.whatsmyip.fyi

v4. has an A record only and v6. has an AAAA record only, so a success proves that family works end to end. Running curl -4 against the dual-stack apex only tells curl which family to prefer; it does not prove the other one is missing.

If curl -6 v6.whatsmyip.fyi fails with “Could not resolve host” or “Network is unreachable”, you have no working IPv6. See /ipv6-test for the diagnosis.

JSON

# the full record
curl -s https://whatsmyip.fyi/json

# just the address
curl -s https://whatsmyip.fyi/json | jq -r .ip

# a few fields
curl -s https://whatsmyip.fyi/json | jq -r '.ip, .country, .isp'

# with reverse DNS and privacy flags
curl -s 'https://whatsmyip.fyi/json?full=1' | jq .

# force JSON from any client
curl -s 'https://whatsmyip.fyi/?format=json' | jq .

# or ask for it with a header
curl -s -H 'Accept: application/json' https://whatsmyip.fyi

?full=1 adds hostname and the privacy object, both null otherwise. It costs a provider lookup, so it is slower and separately rate limited.

Markdown for agents

curl -s https://whatsmyip.fyi/ip.md
# Your public IP
IP: 203.0.113.45 (IPv4)
Location: Tallinn, Harju, EE (approximate)
ISP: Telia Eesti (AS3249)
Source: https://whatsmyip.fyi/ip.md

Short enough to paste into a prompt, and it carries its own source URL.

Other tools

# Wget
wget -qO- https://whatsmyip.fyi/ip

# Wget, IPv6 only
wget -6 -qO- https://v6.whatsmyip.fyi/ip

# HTTPie
http --body whatsmyip.fyi/ip
http --body whatsmyip.fyi/json

# Python
python3 -c "import urllib.request;print(urllib.request.urlopen('https://whatsmyip.fyi/ip').read().decode().strip())"

PowerShell, on Windows or anywhere else:

# plain text
(Invoke-RestMethod https://whatsmyip.fyi/ip).Trim()

# JSON, as an object
$r = Invoke-RestMethod https://whatsmyip.fyi/json
$r.ip
$r.isp

# IPv4 only
(Invoke-RestMethod https://v4.whatsmyip.fyi/ip).Trim()

Invoke-RestMethod parses JSON automatically. Use Invoke-WebRequest and read .Content if you want the raw body.

Using it in a script

The single-line version is fine interactively and bad in a cron job. This version sets timeouts, retries with backoff, fails loudly, and validates what it got back:

#!/usr/bin/env bash
set -euo pipefail

get_public_ip() {
  curl --silent --show-error --fail \
       --connect-timeout 5 --max-time 10 \
       --retry 3 --retry-delay 2 --retry-max-time 30 \
       --retry-all-errors \
       https://whatsmyip.fyi/ip
}

ip="$(get_public_ip | tr -d '[:space:]')"

if [[ ! "$ip" =~ ^[0-9a-fA-F:.]+$ ]]; then
  echo "unexpected response: $ip" >&2
  exit 1
fi

echo "$ip"

What each part is for:

  • --fail makes curl exit non-zero on an HTTP error instead of writing the error body to stdout. Without it, a 429 page ends up in your variable and your script carries on with garbage.
  • --connect-timeout and --max-time stop a hung connection from stalling a scheduled job indefinitely.
  • --retry with --retry-all-errors retries transient failures. curl honours a Retry-After header when it retries a 429, so it waits the time the server asked for rather than hammering.
  • The regex check catches the case where something in the path, a captive portal for instance, returned an HTML page with a 200 status.

For a dual-stack script, query both and treat a failure of either as informational rather than fatal:

v4="$(curl -s --max-time 5 https://v4.whatsmyip.fyi/ip || echo none)"
v6="$(curl -s --max-time 5 https://v6.whatsmyip.fyi/ip || echo none)"
printf 'IPv4: %s\nIPv6: %s\n' "$v4" "$v6"

Rate limits

Endpoint Limit Over the limit
/ip, /json, /ip.md, /api/v1/ip 1,000 per day, 60 per minute 429 with Retry-After
Lookup endpoints 200 per day, 20 per minute 429, a bot challenge after 50 per day

Limits are per source address and need no key. Every response carries the remaining quota in its headers, so a well-behaved client can slow down before it is refused.

When you get a 429, read Retry-After. It is a number of seconds, and it is not advisory:

resp="$(curl -s -D /tmp/h -o /tmp/b -w '%{http_code}' https://whatsmyip.fyi/ip)"
if [[ "$resp" == "429" ]]; then
  wait="$(grep -i '^retry-after:' /tmp/h | tr -d '\r' | awk '{print $2}')"
  echo "rate limited, waiting ${wait}s" >&2
  sleep "${wait:-60}"
fi

A script that checks once a minute makes 1,440 requests a day, which is over the daily limit. Cache the value and re-check when the network changes rather than on a timer. Your public address does not change every minute.

About this tool

One short command in a terminal returns your public address, and every command on this page works as written. It covers the plain text endpoint, JSON with and without enrichment, forcing IPv4 or IPv6, parsing with jq, the Markdown payload meant for agents, and the equivalents in wget, HTTPie, PowerShell, and Python. It ends with the rate limits and how to write a script that behaves when it reaches them.

How to read the result

curl whatsmyip.fyi
The short command. It returns your address and a trailing newline as text/plain, because the server reads the User-Agent and Accept headers and sends plain text to a known CLI tool.
/ip
The explicit plain text endpoint, and the one to use in scripts. Asking for it directly keeps the behaviour independent of whatever User-Agent your HTTP library sends.
/json and ?full=1
The full record as structured data, in the documented schema. Without full=1 the hostname and privacy fields are null and no external provider is called, and with it you get reverse DNS and privacy flags, and a slower response.
/ip.md
A small Markdown document with the address, location, ISP, and a source URL. It is meant for agents and language models that handle Markdown better than JSON, and it is small enough to paste into a prompt.
v4. and v6. hostnames
v4.whatsmyip.fyi publishes an A record only, and v6.whatsmyip.fyi an AAAA record only. Neither can fall back to the other family, so a successful request proves that family works, which curl -4 against a dual-stack host does not.
Rate limit headers and 429
Every response carries the quota you have left. Going over 1,000 per day or 60 per minute returns HTTP 429 with a Retry-After header in seconds, and a client that ignores it keeps getting 429.

Questions people ask

Why does curl get plain text while my browser gets a web page?
The server checks the User-Agent and the Accept header. Requests from curl, wget, HTTPie, PowerShell, python-requests, and Go's HTTP client, or any request whose Accept header does not include text/html, get text/plain. Everything else gets HTML. Add ?format=json to force JSON from any client.
How do I get only the IP in a shell variable?
Use the plain endpoint, which returns nothing else - IP=$(curl -s https://whatsmyip.fyi/ip). Add --fail so a 429 or 500 does not silently put an HTML error page into the variable, and add a timeout so a hung connection does not stall the script.
What are the rate limits?
1,000 requests per day and 60 per minute per source address for /ip, /json, /ip.md, and /api/v1/ip. Lookup endpoints are 200 per day and 20 per minute. No key is needed for any of it. Over the limit you get 429 with a Retry-After header.
Can I use this in production?
Yes, within the limits, with no key. Handle 429 by honouring Retry-After, set a timeout, and have a fallback, which is sensible against any free service including this one. If you need higher volume, the keyed tier exists for that.
Does it support IPv6?
Yes. The apex is dual-stack, so curl picks a family. v6.whatsmyip.fyi is IPv6-only and v4.whatsmyip.fyi is IPv4-only, which is how you test one family specifically rather than asking curl to prefer one.

Related

Last reviewed 2026-09-05.