Como adicionar geolocalização IP ao seu SaaS em 20 minutos
Quatro recursos SaaS que precisam de geolocalização de IP: padrões de moeda, banners GDPR, detecção de fraudes e painéis analíticos. Código funcional para cada um, sem necessidade do Google Maps.
Seu SaaS mostra dólares americanos para um usuário em Berlim. Seu banner de cookie aparece para visitantes no Texas. Seu sistema antifraude não pode sinalizar quando um IP nigeriano usa um endereço de cobrança alemão. Quatro recursos precisa de geolocalização de IP e você pode adicionar todos os quatro em 20 minutos com uma API.
A chamada da API
Cada recurso nesta postagem começa com o mesmo endpoint. Aqui está a curvatura crua:
curl -X POST https://api.botoi.com/v1/ip/lookup \\
-H "Content-Type: application/json" \\
-d '{"ip": "8.8.8.8"}'
Resposta:
{
"success": true,
"data": {
"ip": "8.8.8.8",
"city": "Mountain View",
"region": "California",
"country": "US",
"countryName": "United States",
"latitude": 37.386,
"longitude": -122.0838,
"timezone": "America/Los_Angeles",
"isp": "Google LLC",
"org": "Google Public DNS",
"as": "AS15169 Google LLC"
}
}
Um POST fornece cidade, região, código do país, nome completo do país, coordenadas, fuso horário, ISP, organização e número AS. São dados suficientes para alimentar todos os quatro recursos abaixo.
Recurso 1: seleção automática de moeda na finalização da compra
Mostrar a moeda errada na finalização da compra mata as taxas de conversão. Um visitante da Alemanha vê "$ 49,99" e tem que converter mentalmente para euros antes de decidir. Pior, eles podem suponha que você não atenda a região deles.
Corrija isso com um middleware que mapeie o país IP do visitante para uma moeda padrão:
const COUNTRY_CURRENCY = {
US: "USD", GB: "GBP", DE: "EUR", FR: "EUR", JP: "JPY",
IN: "INR", BR: "BRL", AU: "AUD", CA: "CAD", CN: "CNY",
KR: "KRW", MX: "MXN", SE: "SEK", CH: "CHF", SG: "SGD",
};
async function currencyMiddleware(req, res, next) {
const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;
try {
const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.BOTOI_API_KEY,
},
body: JSON.stringify({ ip }),
});
const { data } = await response.json();
req.defaultCurrency = COUNTRY_CURRENCY[data.country] || "USD";
} catch {
req.defaultCurrency = "USD";
}
next();
}
// Usage in Express
app.get("/checkout", currencyMiddleware, (req, res) => {
res.render("checkout", { currency: req.defaultCurrency });
});
O mapa país-moeda cobre os 15 principais mercados de SaaS. Estenda-o para o seu público. O fallback para USD lida com falhas de API normalmente; nenhum visitante jamais vê um checkout quebrado porque uma chamada de geolocalização expirou.
Recurso 2: Banner de cookie GDPR apenas para visitantes da UE
Mostrar um banner de consentimento de cookies para todos os visitantes é desnecessário e irritante. O GDPR se aplica a visitantes na União Europeia. Todos os outros podem pular.
Este middleware verifica o país IP do visitante em relação à lista de estados membros da UE e define um cookie que o frontend lê:
const EU_COUNTRIES = new Set([
"AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
"DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
"PL", "PT", "RO", "SK", "SI", "ES", "SE",
]);
async function gdprMiddleware(req, res, next) {
const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;
try {
const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.BOTOI_API_KEY,
},
body: JSON.stringify({ ip }),
});
const { data } = await response.json();
req.isEU = EU_COUNTRIES.has(data.country);
} catch {
// Default to showing the banner when the lookup fails
req.isEU = true;
}
next();
}
// Set a cookie so the frontend knows whether to show the banner
app.use(gdprMiddleware, (req, res, next) => {
res.cookie("gdpr_applies", req.isEU ? "1" : "0", {
httpOnly: false,
maxAge: 86400 * 1000,
});
next();
});
No frontend, leia o cookie e alterne o banner:
// Frontend: read the cookie and conditionally show the banner
function shouldShowCookieBanner() {
const match = document.cookie.match(/gdpr_applies=(\d)/);
return match ? match[1] === "1" : true; // default to showing
}
if (shouldShowCookieBanner()) {
document.getElementById("cookie-banner").style.display = "block";
}
O comportamento padrão em caso de falha é mostrar o banner. Isto peca pelo lado da conformidade; se a pesquisa geográfica falhar, você ainda atenderá aos requisitos do GDPR. O cookie dura 24 horas, portanto, você só chama a API uma vez por visitante por dia.
Recurso 3: Detecção de fraude com incompatibilidade geográfica
Quando alguém finaliza a compra com um endereço de cobrança na Alemanha, mas seu IP é geolocalizado para Nigéria, esse é um sinal que vale a pena investigar. Isso não significa que a transação seja fraudulento; as pessoas viajam, usam VPNs e compram presentes para amigos no exterior. Mas são dados apontar as necessidades da sua equipe de revisão de fraudes.
async function checkGeoMismatch(ip, billingCountry) {
const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.BOTOI_API_KEY,
},
body: JSON.stringify({ ip }),
});
const { data } = await response.json();
const mismatch = data.country !== billingCountry;
return {
mismatch,
ipCountry: data.country,
ipCity: data.city,
billingCountry,
riskNote: mismatch
? \`IP located in \${data.countryName} but billing address is \${billingCountry}\`
: null,
};
}
// Usage during checkout
app.post("/checkout", async (req, res) => {
const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;
const { billingCountry } = req.body;
const geo = await checkGeoMismatch(ip, billingCountry);
if (geo.mismatch) {
// Flag for manual review instead of blocking
await flagOrder(req.body.orderId, geo.riskNote);
}
// Continue processing the order
await processOrder(req.body);
res.json({ success: true });
});
A função retorna um objeto estruturado com o sinalizador de incompatibilidade e um formato legível por humanos nota de risco que sua equipe de suporte pode revisar. Sinalize o pedido para revisão manual em vez de bloqueando-o completamente. Combine isso com outros sinais (idade do domínio de e-mail, pagamento velocidade, impressão digital do dispositivo) para uma imagem mais completa.
Recurso 4: Painel analítico com distribuição de usuários
Saber onde seus usuários estão ajuda você a decidir quais idiomas oferecer suporte e quais regiões direcionar com marketing e onde colocar servidores de borda. Este script processa um lote de IPs de visitantes e produz uma distribuição ordenada por país:
async function buildCountryDistribution(ips) {
const counts = {};
// Process in batches to respect rate limits
for (const ip of ips) {
try {
const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.BOTOI_API_KEY,
},
body: JSON.stringify({ ip }),
});
const { data } = await response.json();
const country = data.countryName || "Unknown";
counts[country] = (counts[country] || 0) + 1;
} catch {
counts["Unknown"] = (counts["Unknown"] || 0) + 1;
}
}
// Sort by count descending
return Object.entries(counts)
.sort(([, a], [, b]) => b - a)
.map(([country, count]) => ({
country,
count,
percentage: ((count / ips.length) * 100).toFixed(1) + "%",
}));
}
// Example output:
// [
// { country: "United States", count: 4521, percentage: "34.2%" },
// { country: "Germany", count: 1893, percentage: "14.3%" },
// { country: "United Kingdom", count: 1247, percentage: "9.4%" },
// ...
// ]
Execute isso como um trabalho noturno em seus logs de acesso. A saída informa exatamente qual os países conduzem a maior parte do tráfego. Se 14% dos seus usuários estiverem na Alemanha, mas apenas seu aplicativo oferece suporte ao inglês, essa é uma oportunidade de localização que você pode quantificar.
Estratégia de cache
Os mapeamentos de IP para local não mudam com frequência. Não há razão para chamar a API novamente para o mesmo IP dentro de uma sessão. Este cache usa um mapa simples com TTL de 1 hora:
class GeoCache {
constructor(ttlMs = 60 * 60 * 1000) {
this.cache = new Map();
this.ttlMs = ttlMs;
}
get(ip) {
const entry = this.cache.get(ip);
if (!entry) return null;
if (Date.now() - entry.timestamp > this.ttlMs) {
this.cache.delete(ip);
return null;
}
return entry.data;
}
set(ip, data) {
this.cache.set(ip, { data, timestamp: Date.now() });
}
}
const geoCache = new GeoCache();
async function lookupWithCache(ip) {
const cached = geoCache.get(ip);
if (cached) return cached;
const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.BOTOI_API_KEY,
},
body: JSON.stringify({ ip }),
});
const { data } = await response.json();
geoCache.set(ip, data);
return data;
}
Para servidores Node.js de instância única, o mapa na memória funciona bem. Se você executar vários instâncias atrás de um balanceador de carga, troque o Mapa por Redis. A mesma lógica TTL se aplica; armazene os dados geográficos como uma string JSON com validade de 3.600 segundos.
Extraindo o IP do cliente
A parte mais complicada da geolocalização por IP não é a chamada da API; está obtendo o IP correto
em primeiro lugar. Se seu aplicativo estiver atrás de um proxy reverso, balanceador de carga ou CDN,
req.connection.remoteAddress retorna o IP do proxy, não o do visitante.
Veja como obter o IP real do cliente em cada ambiente:
// Express
const ip = req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip;
// Next.js (App Router)
import { headers } from "next/headers";
const headerList = await headers();
const ip = headerList.get("x-forwarded-for")?.split(",")[0]?.trim();
// Cloudflare Workers
const ip = request.headers.get("cf-connecting-ip");
// Vercel (edge or serverless)
const ip = request.headers.get("x-real-ip")
|| request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();
Sempre divida na primeira vírgula x-forwarded-for. Este cabeçalho pode conter
uma cadeia de IPs quando o tráfego passa por vários proxies. A primeira entrada é a
IP do cliente original.
Se você estiver no Cloudflare, use cf-connecting-ip. Cloudflare define este cabeçalho
em cada solicitação e é mais difícil falsificar do que x-forwarded-for.
FAQ
- Como adiciono geolocalização IP ao meu aplicativo SaaS?
- Envie o IP do seu visitante para uma API de geolocalização de IP (como POST /v1/ip/lookup) e use os dados de país, cidade e fuso horário retornados para personalizar sua experiência. Os casos de uso comuns incluem seleção automática de moeda na finalização da compra, exibição de banners do GDPR para visitantes da UE, sinalização de fraudes de incompatibilidade geográfica e construção de painéis analíticos. Você pode adicionar todos os quatro recursos com uma única API.
- Qual é a melhor API de localização de IP para produtos SaaS?
- Procure uma API que retorne dados de país, cidade, região, coordenadas, fuso horário e ISP em uma única chamada. O /v1/ip/lookup do Botoi retorna todos esses campos sem necessidade de inscrição para acesso anônimo (5 req/min). Para uso em produção, uma chave de API gratuita fornece 1.000 solicitações por dia. Os planos pagos começam em US$ 9/mês.
- Posso localizar geograficamente usuários por IP sem o Google Maps?
- Sim. APIs de geolocalização IP retornam dados de latitude, longitude, cidade e país sem exigir o Google Maps ou qualquer serviço de mapeamento. Você só precisa de uma biblioteca de mapeamento se quiser exibir a localização em um mapa visual. Para recursos como padrões de moeda, conformidade com GDPR e detecção de fraudes, os dados brutos de geolocalização da API são tudo que você precisa.
- Quão precisa é a geolocalização por IP para detectar a localização do usuário?
- A geolocalização IP é precisa ao nível do país cerca de 99% das vezes e ao nível da cidade cerca de 80-90% das vezes. A precisão cai para operadoras de celular e usuários de VPN. Para recursos de SaaS, como seleção de moeda e conformidade com GDPR, a precisão em nível de país é suficiente. Para detecção de fraudes, combine a geolocalização de IP com dados de endereço de cobrança, em vez de depender da precisão em nível de cidade.
- Devo armazenar em cache os resultados de geolocalização de IP no meu SaaS?
- Sim. Os mapeamentos de IP para local mudam com pouca frequência, portanto, armazenar os resultados em cache por 1 hora por IP reduz significativamente as chamadas de API. Use um mapa na memória para implantações de instância única ou Redis para configurações de várias instâncias. A maioria dos aplicativos SaaS apresenta taxas de acertos de cache de 60 a 80% porque os visitantes que retornam acessam o mesmo IP em uma sessão.
Comece a construir com botoi
150+ endpoints de API para consultas, processamento de texto, geração de imagens e utilitários para desenvolvedores. Plano gratuito, sem cartão de crédito.