Batch IP lookup, up to 100 addresses at once
Look up to 100 IPv4 and IPv6 addresses in one POST with the host.sh batch API: body formats, options, per-address errors and code examples.
POST /v2/lookup/batch/ takes a list of addresses and answers with one result per address, in the same order. It always needs a token, sent as a Bearer header or ?token= (the body is the list, so it can't carry the token). Every address counts as one lookup.
Request
POST https://api.host.sh/v2/lookup/batch/ Authorization: Bearer YOUR_TOKEN
The body is one of:
- a JSON array:
["8.8.8.8", "1.1.1.1"] - a JSON object with an
ipsarray:{"ips": ["8.8.8.8", "1.1.1.1"]} - plain text, addresses separated by commas, spaces or new lines:
8.8.8.8, 1.1.1.1
At most 100 addresses per request. The query string takes fields, lang and pretty, which work as on Lookup and apply to every result. OPTIONS (CORS preflight) gets 204; GET and other methods get 405.
Examples
Replace YOUR_TOKEN with your token.
curl -H "Authorization: Bearer YOUR_TOKEN" \ -d '["8.8.8.8", "1.1.1.1"]' https://api.host.sh/v2/lookup/batch/
const res = await fetch('https://api.host.sh/v2/lookup/batch/?fields=connection.ip,geo.country.code', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_TOKEN', 'Content-Type': 'application/json' },
body: JSON.stringify(['8.8.8.8', '1.1.1.1']),
});
const data = await res.json();
if (!res.ok) throw new Error(data.errors[0].reason + ': ' + data.errors[0].detail);
for (const r of data.results) console.log(r.errors ? r.errors[0].reason : r.connection.ip + ' ' + r.geo.country.code);import requests
r = requests.post(
"https://api.host.sh/v2/lookup/batch/",
params={"fields": "connection.ip,geo.country.code"},
headers={"Authorization": "Bearer YOUR_TOKEN"},
json=["8.8.8.8", "1.1.1.1"],
timeout=30,
)
data = r.json()
if not r.ok:
raise SystemExit(data["errors"][0]["reason"] + ": " + data["errors"][0]["detail"])
for result in data["results"]:
print(result["errors"][0]["reason"] if "errors" in result else result["connection"]["ip"])<?php
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Authorization: Bearer YOUR_TOKEN\r\nContent-Type: application/json\r\n",
'content' => json_encode(['8.8.8.8', '1.1.1.1']),
'ignore_errors' => true,
'timeout' => 30,
]]);
$data = json_decode(file_get_contents('https://api.host.sh/v2/lookup/batch/?fields=connection.ip', false, $context), true);
if (isset($data['errors'])) {
fwrite(STDERR, $data['errors'][0]['reason'] . ': ' . $data['errors'][0]['detail'] . "\n");
exit(1);
}
foreach ($data['results'] as $result) {
echo isset($result['errors']) ? $result['errors'][0]['reason'] : $result['connection']['ip'], "\n";
}package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
)
type Result struct {
Connection struct {
IP string `json:"ip"`
} `json:"connection"`
Errors []struct {
Reason string `json:"reason"`
Detail string `json:"detail"`
} `json:"errors"`
}
func main() {
body, _ := json.Marshal([]string{"8.8.8.8", "1.1.1.1"})
req, err := http.NewRequest("POST", "https://api.host.sh/v2/lookup/batch/?fields=connection.ip", bytes.NewReader(body))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var data struct {
Results []Result `json:"results"`
Result
}
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
log.Fatal(err)
}
if len(data.Errors) > 0 {
log.Fatal(data.Errors[0].Reason + ": " + data.Errors[0].Detail)
}
for _, r := range data.Results {
if len(r.Errors) > 0 {
fmt.Println(r.Errors[0].Reason)
} else {
fmt.Println(r.Connection.IP)
}
}
}Response
results has one item per address, in request order. Each item is the same object a single lookup returns, filtered by fields. An address that isn't valid gets an errors item in its place, and the rest of the batch is still answered with 200:
{"results":[
{"connection":{"ip":"8.8.8.8"},"geo":{"country":{"code":"US"}}},
{"errors":[{"code":400,"message":"Bad request","reason":"INVALID_IP_ADDRESS","detail":"ip must be an IPv4 or IPv6 address"}]}
]}
Batch results have no user_agent, client, os or device sections.
Errors for the whole request
These fail the request instead of one item. Details are on Errors.
| Status | Reason | When |
|---|---|---|
| 400 | NO_IPS | The body is empty or has no addresses |
| 400 | TOO_MANY_IPS | More than 100 addresses |
| 400 | BAD_REQUEST | The body starts like JSON but isn't valid JSON |
| 400 | INVALID_LANGUAGE | lang is not a supported language |
| 401 | TOKEN_MISSING | No token |
| 403 | TOKEN_INVALID | Unknown or disabled token, or no permission for batch lookups |
| 405 | METHOD_NOT_ALLOWED | Not a POST |
This page as Markdown: /docs/batch/index.md