Welcome
Hitprobe protects websites, apps, and ecommerce transactions against bad actors. Stop bonus abuse, reduce chargebacks, block multiple signups, limit credential sharing, limit free content, and more. Hitprobe shows you the true picture behind every event.
- Device and browser fingerprinting: Hitprobe provides a stable device ID that remains consistent even when visitors attempt to look like a new user (i.e. using a VPN, switching to incognito, etc.). Our technology combines fingerprinting, probabilistic matching, and server-side techniques.
- Counts and cardinality metrics: See how many times a device has been seen (per hour, day, month, etc.), and the unique IP networks, email addresses, and phone numbers linked to it. Act instantly on API responses, or dig into the details with the Hitprobe console.
- Plug and play risk protection: Ready made smart rules help you to protect your app from risks such as remote geolocation (block countries), automated bots, anonymous users (VPN, Tor, etc.), throwaway email addresses, and more.
- Extended email, phone, IP, and address checks: Check email deliverability, disposable domains, phone number reachability, parse addresses, and much more. Also provides country and timezone.
- Easy-to-use console: For reviewing events, configuring rules, and one-click allowlisting of safe emails or IP addresses.
You can learn more about Hitprobe on the main website.
New here?
The fastest way to start is to follow our getting started tutorial. This will walk you through implementing Hitprobe and explain all the essentials.
Need something specific?
If you have specific questions on how to use the Hitprobe platform, you may want to start with either the API reference or our guides.
The API reference has examples and a detailed description of all the properties and status codes for the event API.
The guides section aims to focus on particular topics to help with troubleshooting and improving your understanding of how Hitprobe works.
⏱️ 5 minute quick start
Very short on time? Implement Hitprobe in 5 minutes. Make sure you've created an account and added a site.
Include the agent script in the <head>
section of the page:
<script src="https://app.hitprobe.com/p/v1.js" async defer></script>
Then, create a hidden form field that will hold the probe_id
generated by the agent. It can have any name but we'll call it probe_id
here. It must have a data-hp-site-key
attribute with your site key (you can get this from site settings in the console):
<input type="hidden" name="probe_id" data-hp-site-key="<INSERT_SITE_KEY>">
Does your site use a Content Security Policy (CSP)?
You'll need to add some Hitprobe domains to allow the agent to work:
Directive | Add the domain |
---|---|
script-src | *.hitprobe.com , *.hitprobe.net |
connect-src | *.hitprobe.com , *.hitprobe.net |
You should now turn your attention to the server. Check you are now receiving a probe_id
field alongside the regular fields on the form. Go ahead and send it to the event API along with the person's details and the secret_key
that you can also copy from the console.
- cURL
- Ruby
- Node
- PHP
- Python
- Go
- .NET
- Java
curl 'https://app.hitprobe.com/api/v1/event' \
-H 'Content-Type: application/json' \
--data '{
"email": "[email protected]",
"probe_id": "<INSERT_PROBE_ID>",
"ip": "<INSERT_REMOTE_IP>",
"secret_key": "<INSERT_SECRET_KEY>"
}'
require 'net/http'
require 'json'
uri = URI('https://app.hitprobe.com/api/v1/event')
req = Net::HTTP::Post.new(uri)
req.content_type = 'application/json'
req.body = {
'email' => '[email protected]',
'probe_id' => '<INSERT_PROBE_ID>',
'ip' => '<INSERT_REMOTE_IP>',
'secret_key' => '<INSERT_SECRET_KEY>'
}.to_json
req_options = {
use_ssl: uri.scheme == 'https'
}
res = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(req)
end
import fetch from 'node-fetch';
fetch('https://app.hitprobe.com/api/v1/event', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'email': '[email protected]',
'probe_id': '<INSERT_PROBE_ID>',
'ip': '<INSERT_REMOTE_IP>',
'secret_key': '<INSERT_SECRET_KEY>'
})
});
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client();
$response = $client->post('https://app.hitprobe.com/api/v1/event', [
'headers' => [
'Content-Type' => 'application/json'
],
'json' => [
'email' => '[email protected]',
'probe_id' => '<INSERT_PROBE_ID>',
'ip' => '<INSERT_REMOTE_IP>',
'secret_key' => '<INSERT_SECRET_KEY>'
]
]);
import requests
headers = {
'Content-Type': 'application/json',
}
json_data = {
'email': '[email protected]',
'probe_id': '<INSERT_PROBE_ID>',
'ip': '<INSERT_REMOTE_IP>',
'secret_key': '<INSERT_SECRET_KEY>',
}
response = requests.post('https://app.hitprobe.com/api/v1/event', headers=headers, json=json_data)
package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
)
func main() {
client := &http.Client{}
var data = strings.NewReader(`{
"email": "[email protected]",
"probe_id": "<INSERT_PROBE_ID>",
"ip": "<INSERT_REMOTE_IP>",
"secret_key": "<INSERT_SECRET_KEY>"
}`)
req, err := http.NewRequest("POST", "https://app.hitprobe.com/api/v1/event", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}
using System.Net.Http;
using System.Net.Http.Headers;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://app.hitprobe.com/api/v1/event");
request.Content = new StringContent("{ \"email\": \"[email protected]\", \"probe_id\": \"<INSERT_PROBE_ID>\", \"ip\": \"<INSERT_REMOTE_IP>\", \"secret_key\": \"<INSERT_SECRET_KEY>\" }");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://app.hitprobe.com/api/v1/event"))
.POST(BodyPublishers.ofString("{ \"email\": \"[email protected]\", \"probe_id\": \"<INSERT_PROBE_ID>\", \"ip\": \"<INSERT_REMOTE_IP>\", \"secret_key\": \"<INSERT_SECRET_KEY>\" }"))
.setHeader("Content-Type", "application/json")
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
You should get back a 200
status response with your first event result:
{
"id": "d4bhu2b1hGOpMn1a76SXl",
"url": "https://app.hitprobe.com/app/events/d4bhu2b1hGOpMn1a76SXl",
"external_id": null,
"outcome": "allow",
"rules": [],
"device": {
"success": true,
"valid": true,
"probe_id": "ctwCYFyAUteNpsBgAw7xYL",
"probe_created_at": "2024-05-20 09:10:27.656374",
"probe_mins_elapsed": 10,
"probe_unique": true,
"probe_ip_match": true,
"probe_suspect": false,
"device_id": "kMZNtJAbyZtFBSZesnNnwg",
"ipv4": "204.158.96.40",
"ipv6": "2041:0000:140f::875b:131b",
"browser_name": "Chrome",
"private_browsing": false,
"headless_browser": false,
"platform": "MacIntel",
"vendor": "Google Inc.",
"memory": 8,
"hardware_concurrency": 10,
"language": "en-GB",
"screen_resolution": "3440x1440",
"timezone": "Europe/London",
"timezone_mismatch": false,
"bot": false,
"anonymous": false,
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"ua_browser_family": "Chrome",
"ua_browser_version": "124.0.0",
"ua_os_family": "Mac OS X",
"ua_os_version": "10.15.7",
"ua_device_family": "Mac",
"ua_device_brand": "Apple",
"ua_device_model": "Mac",
"ua_mobile": false,
"ua_tablet": false,
"ua_touch_capable": false,
"ua_pc": true,
"ua_crawler": false,
"first_seen": "2024-05-20 09:10:27.832895",
"last_seen": "2024-05-20 09:18:05.526443",
"seen_hour": 1,
"seen_day": 1,
"seen_month": 1,
"seen_quarter": 1,
"unique_networks": 1,
"unique_emails": 1,
"unique_phone_numbers": 1,
"unique_postal_codes": 1
},
"email": {
"success": true,
"valid": true,
"email": "[email protected]",
"local_part": "dan",
"normalized_email": "[email protected]",
"normalized_local_part": "dan",
"domain": "example.com",
"domain_tld": "com",
"domain_tld_high_risk": false,
"digits_count": 0,
"name_match": true,
"free": true,
"disposable": false,
"business": false,
"breaches_count": 8,
"breaches_list": [
"CafePress",
"PDL",
"Lastfm",
...
],
"seen_hour": 1,
"seen_day": 1,
"seen_month": 1,
"seen_quarter": 1,
"unique_devices": 1
},
"ip": {
"success": true,
"in_country": true,
"ip": "204.158.96.40",
"ip_network": "204.158.96",
"isp": "Comcast Cable Communications LLC",
"organization": "Comcast Cable Communications LLC",
"carrier": "Verizon",
"type": "business",
"city": "Minneapolis",
"state": "Minnesota",
"country_name": "United States",
"country_code": "US",
"postal_code": "55400",
"timezone": "America/Chicago",
"latitude": 40.9255,
"longitude": -89.3034,
"vpn": true,
"relay": false,
"proxy": false,
"tor": false,
"hosting": false,
"threat": false,
"bogon": false,
"seen_hour": 1,
"seen_day": 1,
"seen_month": 1,
"seen_quarter": 1
}
}
Set your application to respond to a block
or block_review
outcome by preventing the flow from continuing.
🥳 That's the basics done. We recommend you now review the full getting started guide to understand how to strengthen the implementation.