ipme.sh

Scalable IP address API

Multi vendor support in IPv4 or IPv6. Deploy anywhere.

Your IP loading...
IPv4 checking…
IPv6 checking…

Endpoints

The subdomain picks IPv4 or IPv6. The path picks the format.

HostForces
api.ipme.shIPv4 or IPv6 (whichever the client uses)
ipv4.api.ipme.shIPv4 only (DNS has no AAAA record)
ipv6.api.ipme.shIPv6 only (DNS has no A record)
PathContent-TypeExample body
/ or /ip text/plain 203.0.113.42
/json application/json {"ip":"203.0.113.42"}
/jsonp?callback=cb application/javascript cb({"ip":"203.0.113.42"})
/healthz (none) 200 OK

JSONP callbacks should be valid JavaScript identifiers. Anything else comes back as a 400.

Examples

cURL
# Default (whichever family resolves first)
curl https://api.ipme.sh

# Force IPv4 / IPv6 via DNS-only subdomains
curl https://ipv4.api.ipme.sh
curl https://ipv6.api.ipme.sh

# JSON
curl https://api.ipme.sh/json
JavaScript (browser fetch)
const r = await fetch('https://api.ipme.sh/json');
const { ip } = await r.json();
console.log(ip);
Python
import urllib.request, json

with urllib.request.urlopen('https://api.ipme.sh/json') as r:
    print(json.load(r)['ip'])

# or with requests
import requests
print(requests.get('https://api.ipme.sh').text.strip())
Go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    r, _ := http.Get("https://api.ipme.sh/json")
    defer r.Body.Close()
    var out struct{ IP string }
    json.NewDecoder(r.Body).Decode(&out)
    fmt.Println(out.IP)
}
Node.js
const r = await fetch('https://api.ipme.sh/json');
const { ip } = await r.json();
console.log(ip);
Bash (dynamic DNS pattern)
#!/usr/bin/env bash
# Update record only when public IP changes.
set -euo pipefail

STATE=/var/lib/ipme/last-ip
mkdir -p "$(dirname "$STATE")"
NOW=$(curl -fsS https://api.ipme.sh)
LAST=$(cat "$STATE" 2>/dev/null || true)

if [[ "$NOW" != "$LAST" ]]; then
  echo "IP changed: $LAST -> $NOW"
  # update_dns_record "$NOW"
  echo "$NOW" > "$STATE"
fi
PHP
$ip = trim(file_get_contents('https://api.ipme.sh'));
echo $ip;

// JSON
$data = json_decode(file_get_contents('https://api.ipme.sh/json'), true);
echo $data['ip'];
Ruby
require 'net/http'
require 'json'

ip = Net::HTTP.get(URI('https://api.ipme.sh')).strip
puts ip

data = JSON.parse(Net::HTTP.get(URI('https://api.ipme.sh/json')))
puts data['ip']
Rust (ureq)
let ip: String = ureq::get("https://api.ipme.sh")
    .call()?
    .into_string()?
    .trim()
    .to_string();
println!("{ip}");
Java
// Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class IpMe {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder(URI.create("https://api.ipme.sh")).build();
        var res = client.send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println(res.body().trim());
    }
}
Kotlin (OkHttp)
// build.gradle.kts: implementation("com.squareup.okhttp3:okhttp:4.12.0")
import okhttp3.OkHttpClient
import okhttp3.Request

val client = OkHttpClient()
val req = Request.Builder().url("https://api.ipme.sh").build()
client.newCall(req).execute().use { res ->
    println(res.body!!.string().trim())
}

For server-side Kotlin on JVM 11+, the stdlib java.net.http.HttpClient works without OkHttp. See the Java example above.

Swift (URLSession)
// iOS 15+ / macOS 12+, Swift 5.5+ (async/await)
import Foundation

struct IpResponse: Decodable { let ip: String }

let url = URL(string: "https://api.ipme.sh/json")!
let (data, _) = try await URLSession.shared.data(from: url)
let ip = try JSONDecoder().decode(IpResponse.self, from: data).ip
print(ip)
C# / .NET
// .NET 6+ (top-level statements)
using System.Text.Json;

using var http = new HttpClient();

// Text
var ip = (await http.GetStringAsync("https://api.ipme.sh")).Trim();
Console.WriteLine(ip);

// JSON
using var stream = await http.GetStreamAsync("https://api.ipme.sh/json");
var doc = await JsonDocument.ParseAsync(stream);
Console.WriteLine(doc.RootElement.GetProperty("ip").GetString());
PowerShell
# Text
(Invoke-WebRequest https://api.ipme.sh).Content.Trim()

# JSON (Invoke-RestMethod auto-parses)
(Invoke-RestMethod https://api.ipme.sh/json).ip

# Force IPv4 / IPv6 via DNS-only subdomains
(Invoke-RestMethod https://ipv4.api.ipme.sh/json).ip
(Invoke-RestMethod https://ipv6.api.ipme.sh/json).ip
Elixir (Req)
# mix.exs: {:req, "~> 0.5"}
# or one-off: Mix.install([:req])

# Text
Req.get!("https://api.ipme.sh").body |> String.trim() |> IO.puts()

# JSON (Req auto-decodes application/json)
Req.get!("https://api.ipme.sh/json").body["ip"] |> IO.puts()
JSONP (legacy cross-origin)
<script src="https://api.ipme.sh/jsonp?callback=showIP"></script>
<script>
  function showIP(data) { console.log(data.ip); }
</script>

For anything new, fetch is a better fit. JSONP is mostly for older clients.