Authentication
Send the Bearer key in the Authorization header of a server-side request.
Authorization: Bearer 17nas_live_xxxxxxxxx
Never put the key in browser scripts, app bundles or mini-program packages — all of those can be decompiled. When the front end needs the data, have it call your own server and let the server make the request. You can tighten this further with an IP/CIDR allowlist in the console.
GET/v1/ip
ipapi.17nas.com
Returns the IP and protocol actually used by this HTTP connection. If the request came over IPv4 you get IPv4; over IPv6 you get IPv6. Nothing to specify.
| Parameter | In | Description |
Authorization | Header | Required. Bearer <your API key> |
format | Query | Optional. json (default) or text. text returns the bare IP, handy for shell scripts |
# JSON (default)
curl https://ipapi.17nas.com/v1/ip \
-H "Authorization: Bearer 17nas_live_xxx"
# Bare IP as plain text — drop it straight into a shell variable
curl "https://ipapi.17nas.com/v1/ip?format=text" \
-H "Authorization: Bearer 17nas_live_xxx"
# Force IPv4 or IPv6 with curl's own flags
curl -4 https://ipapi.17nas.com/v1/ip -H "Authorization: Bearer 17nas_live_xxx"
curl -6 https://ipapi.17nas.com/v1/ip -H "Authorization: Bearer 17nas_live_xxx"
Response fields
200 OK
{
"ip": "203.0.113.10",
"protocol": "IPv4",
"country": { "code": "CN" },
"region": "Guangdong",
"city": "Shenzhen",
"timezone": "Asia/Shanghai",
"asn": { "number": 4134, "organization": "CHINANET" },
"edge": { "colo": "HKG" },
"request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"generated_at": "2026-07-26T09:00:00.000Z"
}
| Field | Type | Description |
ip | string | The public IP of this connection |
protocol | string | IPv4 or IPv6 |
country.code | string | Two-letter ISO 3166-1 country code; null when unknown |
region / city | string | Region/state and city, in English; accuracy depends on the edge database and may be null |
timezone | string | IANA time zone name, e.g. Asia/Shanghai |
asn.number | number | Autonomous system number; 0 when unknown |
asn.organization | string | Network operator name; may be null |
edge.colo | string | Three-letter code of the edge node that served this request |
request_id | string | Unique ID for this request — include it when reporting a problem |
Location fields come from the edge node's GeoIP data and can be wrong or empty on mobile networks, satellite links, corporate NAT egress and some proxies. Treat them as a hint, not a fact — don't use them for hard geographic blocking.
Quota headers
Every successful response carries the current quota state, so there is no separate endpoint to poll.
| Response header | Description |
X-RateLimit-Limit | Requests allowed per second (QPS) |
X-RateLimit-Remaining | How many are left in the current second |
X-RateLimit-Reset | Unix timestamp when the rate-limit window resets |
X-Quota-Limit | Total quota for this period |
X-Quota-Used | Used so far this period |
X-Quota-Remaining | Remaining this period |
X-Quota-Reset | When this period's quota resets |
X-Request-Id | ID of this request — include it when reporting a problem |
Watch X-Quota-Remaining in your code and alert below 10% — don't wait until calls start failing to notice.
Error statuses
401 Key invalid, revoked, or malformed
403 Source IP is not on the allowlist
429 Over the QPS limit, or this period's quota is spent
503 Temporarily unavailable — retry shortly
{
"error": {
"code": "quota_exhausted",
"message": "Monthly quota exhausted",
"request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"docs_url": "https://17nas.com/ip-api.php#documentation",
"upgrade_url": "https://17nas.com/ip-api.php?source=api-error&placement=quota-exhausted#pricing",
"reset_at": "2026-08-01T00:00:00+08:00"
}
}
429 has two causes, told apart by the headers. X-RateLimit-Remaining at 0 is a momentary rate limit — wait a second and carry on. X-Quota-Remaining at 0 means the period's quota is spent — wait for reset_at, or top up or upgrade.
Retries and fallbacks
This is a network call, so it will fail sometimes. A few practical rules:
- Set a timeout under 5 seconds so it can't drag down your main flow
- Retry on
503 or timeout with exponential backoff (1s, 2s, 4s), at most three times
- Never retry
401 / 403 — those are configuration problems, and retrying just burns quota
- If a
429 means the quota is spent, retrying is pointless — go straight to your fallback path
- On non-critical paths (logging, for instance) just skip it — don't block a legitimate user action because a location lookup failed
- Cache the answer locally for a few minutes — one server's egress IP doesn't change second by second