Geolocalización de IP en lote con Python
Enriquece una lista de IP contra POST /v1/lookup en lotes de 100, con asociación por fila, salida con puntos de control y espera ante 429; llamado directamente por HTTP, ya que el SDK oficial de Python no cubre este endpoint.
Antes de empezar
- Una cuenta de Watch-IP y una clave secreta de lookup: POST /v1/lookup es exclusivamente de servidor a servidor; nunca la llames desde un navegador ni la incluyas en un cliente.
- Python 3.9 o superior.
- Una lista local de IP para enriquecer: esta guía las lee desde un CSV con una IP por línea.
1. Por qué esto usa HTTP directo en lugar del SDK
watch-ip-sdk, el paquete oficial de Python, solo implementa GET /v1/geo, el endpoint de visitante orientado al navegador; no tiene ningún método para POST /v1/lookup. Esta guía llama a la API directamente con urllib de la biblioteca estándar, siguiendo el mismo enfoque sin dependencias del propio SDK en lugar de añadir una nueva solo para esto.
2. Lee y valida la lista de entrada
Conserva la posición de fila original de cada IP para que un resultado pueda asociarse de vuelta con el registro del que provino; la API devuelve los resultados en el mismo orden en que recibió las IP, lo cual solo ayuda si tu propia lista mantiene ese mismo orden.
Python
import csv
with open("ips.csv", newline="") as f:
ips = [row[0].strip() for row in csv.reader(f) if row and row[0].strip()]
print(f"Se cargaron {len(ips)} IP")3. Divide en lotes de 100
POST /v1/lookup acepta de 1 a 100 IP por llamada y valida todo el lote antes de consultar nada: una dirección malformada hace fallar todo el fragmento, así que valida la sintaxis de las IP localmente primero si tu lista de entrada podría estar sucia.
Python
def chunk(items, size=100):
for i in range(0, len(items), size):
yield items[i : i + size]4. Envía cada lote y registra los resultados con puntos de control
Escribe los resultados a medida que cada lote se completa en lugar de mantener todo en memoria hasta el final, para que una falla a mitad de una lista larga no pierda los lotes que ya tuvieron éxito. found: false significa que la IP es sintácticamente válida pero no tiene ninguna entrada en la base de datos: un resultado normal, no un error.
Python
import json
import time
import urllib.error
import urllib.request
API_URL = "https://api.watch-ip.com/v1/lookup"
SECRET_KEY = "YOUR_SECRET_KEY"
def lookup_batch(ips):
request = urllib.request.Request(
API_URL,
data=json.dumps({"ips": ips}).encode(),
headers={
"Authorization": f"Bearer {SECRET_KEY}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(request) as response:
return json.load(response)["results"]
with open("results.jsonl", "a") as out:
for batch in chunk(ips):
try:
results = lookup_batch(batch)
except urllib.error.HTTPError as err:
if err.code == 429:
time.sleep(60) # esperar a que termine la ventana de un minuto actual
results = lookup_batch(batch)
else:
raise
for result in results:
out.write(json.dumps(result) + "\n")5. Reanudar un trabajo parcial
Como los resultados se añaden un lote a la vez, reiniciar después de una falla solo necesita omitir las IP ya escritas en results.jsonl en lugar de volver a enviar toda la lista; volver a enviar una IP duplicada se factura de nuevo, ya que cada entrada de un lote se mide individualmente en lugar de deduplicarse.
Python
done = set()
with open("results.jsonl") as f:
for line in f:
done.add(json.loads(line)["ip"])
remaining = [ip for ip in ips if ip not in done]Errores y casos límite
Un 400 significa que el lote tenía más de 100 IP, una lista vacía, o una dirección malformada en algún punto; todo el fragmento falla, no solo la entrada incorrecta. Un 403 significa que se usó una clave publicable en lugar de una clave secreta. Mezclar direcciones IPv4 e IPv6 en el mismo lote no supone ningún problema; cada una se consulta de forma independiente, con los mismos campos anulables en ambos casos.
Siguiente paso
Consulta la referencia de bulk IP lookup para conocer el esquema exacto de la respuesta, y errores y límites de tasa para la lista completa de códigos de estado que puede devolver este endpoint.
Páginas relacionadas
- Búsqueda masiva de IPResuelve hasta 100 direcciones IP proporcionadas en una sola solicitud de backend.
- Referencia de POST /v1/lookupReferencia completa de campos para el endpoint de lookup de IP en lote.
- SDK de Pythonwatch-ip-sdk en PyPI.
- Errores y límites de tasaForma de la respuesta de error, códigos de estado y orientación sobre reintentos.