Docs

API Documentation · Bulk Endpoint

REST API for IP intelligence — geolocation, network, timezone, currency and threat data, returned as JSON, XML or CSV.

Overview

This API is built for developers who require high performance, low latency, and production-grade reliability at scale.
Our platform runs on a proprietary Anycast network with globally distributed, redundant, and self-scaling infrastructure, ensuring low-latency responses worldwide. The core is built with a highly optimized stack combining OpenResty and Rust, designed for maximum throughput and minimal overhead.
IP geolocation databases are stored in a compact binary format and loaded directly into high-speed memory, enabling constant-time lookups without disk access. This in-memory architecture allows the API to process over 1 billion requests per day with consistently stable performance.
Our database is updated multiple times per day and enhanced using AI-driven data aggregation and validation techniques, improving accuracy by continuously analyzing signals from multiple trusted sources.
With a clean and developer-friendly interface, flexible integration options, and predictable performance under heavy load, this API is designed to integrate seamlessly into modern production environments.


API Endpoints

Quickstart · Bulk IP lookup

API key requiredFor production workloads and business-critical applications that need higher limits, reliable scaling, and an uptime SLA.

Available in: Business and Unlimited Plan

The Bulk IP Lookup endpoint lets you look up up to 100 IP addresses in a single request. You can mix IPv4 and IPv6 addresses.

This is useful for processing large IP lists faster and reducing the number of API calls.

We support two ways to pass IP addresses in a batch request: comma-separated values and a JSON array.

Comma-separated values

To process multiple IP addresses with a GET request, append a comma-separated list of IP addresses to the Bulk endpoint URL:

$ curl "http://ipwhois.pro/bulk/8.8.4.4,1.1.1.1,2c0f:fb50:4003::?key=YOUR_API_KEY"

JSON array in POST body

You can also send a JSON array of IP addresses in the POST request body. The example below looks up 3 IP addresses with a single API call:

$ curl --data '["8.8.4.4", "1.1.1.1", "2c0f:fb50:4003::"]' \
     --header "Content-Type: application/json" \
     --request POST "http://ipwhois.pro/bulk?key=YOUR_API_KEY"

Please note: A Bulk IP Lookup request costs one credit per IP address. For example, the request above costs 3 credits.

HTTPS is also supported. Use https:// when you need an encrypted connection, especially for browser-based requests from secure websites.

Get Your API Key Basic code examples

Try it live

Edit the request URL and see the API response instantly:

GET
Actual response
{
    "ip": "8.8.4.4",
    "success": true,
    "type": "IPv4",
    "continent": "North America",
    "continent_code": "NA",
    "country": "United States",
    "country_code": "US",
    "region": "California",
    "region_code": "CA",
    "city": "Mountain View",
    "latitude": 37.3860517,
    "longitude": -122.0838511,
    "is_eu": false,
    "postal": "94039",
    "calling_code": "1",
    "capital": "Washington D.C.",
    "borders": "CA,MX",
    "flag": {
        "img": "https://cdn.ipwhois.io/flags/us.svg",
        "emoji": "🇺🇸",
        "emoji_unicode": "U+1F1FA U+1F1F8"
    },
    "connection": {
        "asn": 15169,
        "org": "Google LLC",
        "isp": "Google LLC",
        "domain": "google.com"
    },
    "timezone": {
        "id": "America/Los_Angeles",
        "abbr": "PDT",
        "is_dst": true,
        "offset": -25200,
        "utc": "-07:00",
        "current_time": "2022-04-22T14:31:48-07:00"
    },
    "currency": {
        "name": "US Dollar",
        "code": "USD",
        "symbol": "$",
        "plural": "US dollars",
        "exchange_rate": 1
    },
    "security": {
        "anonymous": false,
        "proxy": false,
        "vpn": false,
        "tor": false,
        "hosting": false
    },
    "rate": {
        "limit": 250000,
        "remaining": 50155
    }
}

Please note: The example response above includes all available fields for illustration. Actual fields may vary depending on your plan and request parameters.


Response fields

The API response contains fields based on your plan and request options. The table below lists all available response fields:

name description
ip IP used for the query (e.g. 8.8.4.4)
success If the query is successful, true will be returned, and false if it fails.
message Included only when success is false
Can be one of the following: See error codes
type IP address type (IPv4 or IPv6)
continent The name of the continent (e.g. North America)
continent_code Two-letter (ISO 3166-1) continent code (e.g. NA)
country The name of the country (e.g. United States)
country_code Two-letter (ISO 3166-1) country code (e.g. US)
region The name of the region/state (e.g. California)
region_code The state/region code (ISO 3166-2), when available (e.g. CA)
city The name of the city (e.g. Mountain View)
latitude The approximate (WGS84) latitude of the location associated with the IP (e.g. 37.3860517)
longitude The approximate (WGS84) longitude of the location associated with the IP (e.g. -122.0838511)
is_eu Returns true or false depending on whether the country associated with the IP is in the European Union.
postal The ZIP code associated with location.
calling_code The calling/dial code of the country (e.g. 1)
capital The capital city of the country (e.g. Washington)
borders Two-letter (ISO 3166-1) code of the countries that border the country associated with this IP (e.g. CA,MX)
flag > img Returns an HTTP URL leading to an SVG-flag icon for the country.
flag > emoji An emoji version of the flag of the country (e.g. 🇺🇸)
flag > emoji_unicode The unicode value of the emoji icon for the flag.
connection > asn The Autonomous System (AS) Number (e.g. 15169)
connection > org The name of the organization that owns the Autonomous System for the IP address that is analyzed (e.g. Google LLC)
connection > isp The name of the ISP associated with the IP (e.g. Google LLC)
connection > domain The domain name associated with the organization that owns the connection IP (e.g. google.com)
timezone > id The ID of the time zone associated with location (e.g. America/Los_Angeles)
timezone > abbr The Abbreviation of the Timezone (e.g. PDT)
timezone > is_dst true or false depending on whether or not Daylight Savings have been accounted for.
timezone > offset The offset from UTC (in seconds) for the given location (e.g. -25200 for PDT's -7h UTC offset)
timezone > utc The UTC offset of the Timezone (e.g. -07:00)
timezone > current_time The exact current date and time (ISO 8601 format) associated with location (e.g. 2022-04-22T14:31:48-07:00)
currency > name The name of the currency (e.g. US Dollar)
currency > code The 3-letter (ISO 4217) currency code (e.g. USD)
currency > symbol The native (local) symbol of the given currency (e.g. $)
currency > plural The plural version of the currency (e.g. US dollars)
currency > exchange_rate The current exchange rate against the US dollar.
security > anonymous Boolean with true value if proxy, vpn OR tor is satisfied.
security > proxy true or false depending on whether the IP Address is a known proxy or any type.
security > vpn Returns true when the IP address under search is used by a VPN, false otherwise.
security > tor true or false depending on whether the IP Address is a known Tor exit node or relay.
security > hosting true or false depending on whether the IP address is used for hosting purposes.
rate > limit The total number of API requests that your account is limited to over the given time period (month).
rate > remaining The number of API requests remaining in the given time period (month).

Specify response fields

You can limit the API response to specific data fields by adding the fields GET parameter to the request URL. Use a comma-separated list to request multiple fields.

For example:
fields=country,city,flag.emoji
fields=city,timezone
fields=ip,timezone.current_time

JSONP Callback

The API supports JSONP callbacks. Add the callback GET parameter to your request URL and set it to your callback function name. The JSON response will be wrapped in the specified function.

For example:
callback=getIPinfo

Localized responses

Localized city, region, country, capital and continent fields can be returned by setting the GET parameter lang to one of the following:

lang description demo
en English (default)
ru Русский (Russian)
de Deutsch (German)
es Español (Spanish)
pt-BR Português - Brasil (Portuguese)
fr Français (French)
zh-CN 中国 (Chinese)
ja 日本語 (Japanese)

Enable Threat Detection

Available in: Business and Unlimited Plan

Add security=1 to your request URL to include security signals for abuse prevention, fraud checks, and risk scoring. The response includes detection fields for anonymous traffic, proxies, VPNs, Tor, and hosting providers.

{
    [...],
    "security": {
        "anonymous": true,
        "proxy": false,
        "vpn": true,
        "tor": false,
        "hosting": false
    }
}
For example:
security=1

Usage quota in the API response

Available in: Basic and Business Plan

Add rate=1 to your request URL to include quota details in the JSON response, including your total limit and remaining requests.
Example JSON response:

{
    [...],
    "rate": {
        "limit": 250000,
        "remaining": 50155
    }
}
For example:
rate=1

Error codes

The API may return the following standard HTTP 4xx client error codes:

CodeReasonResponse
400Bad Request{"success":false,"message":"Bad Request"}
400Invalid callback parameter{"success":false,"message":"Invalid callback parameter"}
400Request size limit{"success":false,"message":"Bulk lookup accepts up to 100 IP addresses per request"}
401Unauthorized{"success":false,"message":"Invalid API key or subscription expired"}
403Access denied{"success":false,"message":"The current subscription plan does not support this API endpoint"}
403Source not allowed{"success":false,"message":"API key not allowed access from this source. Please check your API key security rules: https://ipwhois.io/dashboard/settings"}
404URL Not Found{"success":false,"message":"404 not found"}
405HTTP method is not allowed{"success":false,"message":"HTTP method is not allowed"}
414Request-URI Too Large{"success":false,"message":"URI Too Long"}
429The limit has been exceeded{"success":false,"message":"You've hit the monthly limit"}

In general, a 2xx HTTP status code indicates that the request was successfully processed.

However, in some cases the API may return a 200 OK response while indicating an application-level error in the response body. In such cases, the request was technically valid, but the operation could not be completed.

These responses include a JSON object with "success":false and an explanatory "message", for example:

HTTP Status CodeReasonResponse
200Incorrect IP address{"success":false,"message":"Invalid IP address"}
200Reserved IP address{"success":false,"message":"Reserved range"}

Code examples

Simple code examples in popular languages:

  • PHP
  • Node.js
  • Go
  • Python
  • Java (11+)
  • Ruby
  • Swift
  • Rust
$ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222'];
$ch = curl_init('https://ipwhois.pro/bulk?key=YOUR_API_KEY');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($ips));
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data[0]['ip'] . ': ' . $data[0]['country'] . ' ' . $data[0]['flag']['emoji']; // Example output: 8.8.8.8: United States 🇺🇸
const ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222'];
const url = `https://ipwhois.pro/bulk?key=YOUR_API_KEY`;

fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(ips)
})
  .then(res => res.json())
  .then(data => {
    console.log(`${data[0].ip}: ${data[0].country} ${data[0].flag.emoji}`); // Example output: 8.8.8.8: United States 🇺🇸
  });
package main

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

func main() {
  ips := []string{"8.8.8.8", "1.1.1.1", "208.67.222.222"}
  body, _ := json.Marshal(ips)

  url := "https://ipwhois.pro/bulk?key=YOUR_API_KEY"
  req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
  req.Header.Set("Content-Type", "application/json")

  client := &http.Client{}
  resp, _ := client.Do(req)
  defer resp.Body.Close()

  b, _ := io.ReadAll(resp.Body)
  data := []map[string]any{}
  json.Unmarshal(b, &data)

  fmt.Println(data[0]["ip"], data[0]["country"]) // Example output: 8.8.8.8 United States
}
import requests

ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222']
url = 'https://ipwhois.pro/bulk?key=YOUR_API_KEY'

response = requests.post(url, json=ips)
data = response.json()

print(data[0]['ip'], data[0]['country'], data[0]['flag']['emoji']) # Example output: 8.8.8.8 United States 🇺🇸
import org.json.JSONArray;
import java.net.URI;
import java.net.http.*;

public class Main {
  public static void main(String[] args) throws Exception {
    var client = HttpClient.newHttpClient();
    var url = "https://ipwhois.pro/bulk?key=YOUR_API_KEY";

    var body = "[\"8.8.8.8\",\"1.1.1.1\",\"208.67.222.222\"]";

    var request = HttpRequest.newBuilder()
      .uri(URI.create(url))
      .header("Content-Type", "application/json")
      .POST(HttpRequest.BodyPublishers.ofString(body))
      .build();

    var data = new JSONArray(client.send(request, HttpResponse.BodyHandlers.ofString()).body());
    var first = data.getJSONObject(0);
    System.out.println(first.getString("ip") + ": " + first.getString("country") + " " + first.getJSONObject("flag").getString("emoji")); // Example output: 8.8.8.8 United States 🇺🇸
  }
}
require 'net/http'
require 'json'
require 'uri'

ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222']
url = 'https://ipwhois.pro/bulk?key=YOUR_API_KEY'

uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = ips.to_json

res = http.request(req)
data = JSON.parse(res.body)

puts data[0]['ip'] + ': ' + data[0]['country'] + ' ' + data[0]['flag']['emoji'] # Example output: 8.8.8.8: United States 🇺🇸
import Foundation

let url = "https://ipwhois.pro/bulk?key=YOUR_API_KEY"
let ips = ["8.8.8.8", "1.1.1.1", "208.67.222.222"]

var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: ips)

let sem = DispatchSemaphore(value: 0)

URLSession.shared.dataTask(with: request) { data, _, _ in
  guard let data = data else { sem.signal(); return }
  let json = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]]
  let first = json?.first
  let ip = first?["ip"] as? String ?? ""
  let country = first?["country"] as? String ?? ""
  let flag = (first?["flag"] as? [String: Any])?["emoji"] as? String ?? ""
  print(ip, country, flag) // Example output: 8.8.8.8 United States 🇺🇸
  sem.signal()
}.resume()

sem.wait()
use reqwest;
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
  let url = format!("https://ipwhois.pro/bulk?key=YOUR_API_KEY");
  let ips = vec!["8.8.8.8", "1.1.1.1", "208.67.222.222"];

  let data: Value = reqwest::Client::new()
    .post(url)
    .json(&ips)
    .send().await?
    .json().await?;

  println!("{}: {}", data[0]["ip"], data[0]["country"]); // Example output: 8.8.8.8: United States
  Ok(())
}