Developers
One endpoint, every stack
Guarda is a plain HTTPS API returning JSON, so there is no SDK to install, no binary database to schedule, and nothing to keep in sync. If your language can make a GET request, the integration is finished in a few minutes.
Code examples
curl "https://guarda.net/api/public/v2/8.8.8.8" \
-H "X-API-Key: $GUARDA_KEY"const res = await fetch(`https://guarda.net/api/public/v2/${ip}`, {
headers: { "X-API-Key": process.env.GUARDA_KEY },
signal: AbortSignal.timeout(400),
});
const data = await res.json();
if (data.risk_level === "critical") challenge(user);import os, requests
r = requests.get(
f"https://guarda.net/api/public/v2/{ip}",
headers={"X-API-Key": os.environ["GUARDA_KEY"]},
timeout=0.5,
)
data = r.json()
print(data["country_code"], data["proxy_type"], data["risk"])$ch = curl_init("https://guarda.net/api/public/v2/" . $ip);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT_MS => 500,
CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("GUARDA_KEY")],
]);
$data = json_decode(curl_exec($ch), true);req, _ := http.NewRequest("GET", "https://guarda.net/api/public/v2/"+ip, nil)
req.Header.Set("X-API-Key", os.Getenv("GUARDA_KEY"))
client := &http.Client{Timeout: 500 * time.Millisecond}
resp, err := client.Do(req)uri = URI("https://guarda.net/api/public/v2/#{ip}")
req = Net::HTTP::Get.new(uri, "X-API-Key" => ENV["GUARDA_KEY"])
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: 0.5) do |http|
http.request(req)
end
data = JSON.parse(res.body)HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://guarda.net/api/public/v2/" + ip))
.header("X-API-Key", System.getenv("GUARDA_KEY"))
.timeout(Duration.ofMillis(500))
.build();
HttpResponse<String> res = client.send(req, BodyHandlers.ofString());Patterns worth copying
Get the client IP right
Behind a CDN or load balancer the socket address is your own edge. Read the header your infrastructure sets — CF-Connecting-IP on Cloudflare, the leftmost untrusted entry of X-Forwarded-For elsewhere — and trust it only when the request came from your own network. Parsing the header blindly is a spoofing hole, because anything can be prepended by the client.
Resolve once per session
Look the address up when a session is created, keep the country and classification in the session, and re-resolve only when the address changes. Most integrations cut their query volume by an order of magnitude with this one change.
Cache, including failures
An hour of in-memory caching per address covers almost all repeat traffic without going stale. Cache negative results too, with a shorter lifetime, so a transient error does not turn into a retry storm.
Always define a fallback
Give the call a hard timeout of a few hundred milliseconds and decide in advance what happens when it expires. A page rendered in the wrong currency is a small annoyance; a page that never renders is lost revenue.
Run it at the edge
If your app already runs on an edge runtime, resolve there and attach the result to the request context. The added latency becomes negligible and your origin receives the data as plain fields.
Batch offline work
For log enrichment and back-office review, send lists rather than one address at a time. The bulk endpoint and the bulk tool both accept pasted lists and return the same fields as a single lookup.
Where teams plug it in
Signup and login. Score the address before the account exists, and again on unusual sign-ins. Challenge rather than deny, and keep the reasons with the account event.
Checkout. Combine classification with the mismatch between billing country and connection country. Neither is damning alone; together they are the classic reshipping pattern.
Analytics. Exclude hosting ASNs from engagement metrics and your conversion rates suddenly reconcile.
Game and voice servers. Filter anonymised connections at join time instead of moderating the aftermath.
Content and licensing. Treat anonymised traffic as unknown location rather than as the country of the exit node, or the restriction is trivially bypassed.
