API Docs
One API call to assess risk from email addresses and IP intelligence. Get a risk score in under 20ms.
Quickstart
Get your API Key
You'll need an API key to make requests. Sign up for a free account to get one.
Make your first request
Make a GET request with your key via header or query param. IP is optional — add it for a combined risk score.
Risk Check (email + optional IP)
curl -s "https://api.ipasis.com/v1/validate-email?email=user@mailinator.com&ip=185.220.101.4" \ -H "X-API-Key: <your_api_key>"
IP Lookup only
curl -s "https://api.ipasis.com/v1/lookup?ip=8.8.8.8" -H "X-API-Key: <your_api_key>"
Risk Check
GET /v1/validate-email?email=<email>&ip=<ip>
The primary endpoint. Pass an email to get a risk score. Add an optional IP address to include IP-based signals (VPN, proxy, Tor, datacenter) in the score.
Parameters
| Parameter | Required | Description |
|---|---|---|
| Yes | The email address to validate. | |
| ip | No | An IP address to include in the risk assessment. When provided, IP signals (VPN, Tor, proxy, etc.) are factored into the risk score. |
Try It
Examples
curl -s "https://api.ipasis.com/v1/validate-email?email=user@mailinator.com&ip=185.220.101.4" \ -H "X-API-Key: <your_api_key>"
fetch('https://api.ipasis.com/v1/validate-email?email=user@mailinator.com&ip=185.220.101.4', {
headers: { 'X-API-Key': '<your_api_key>' }
}).then(r => r.json()).then(console.log)import requests
r = requests.get(
'https://api.ipasis.com/v1/validate-email',
params={'email': 'user@mailinator.com', 'ip': '185.220.101.4'},
headers={'X-API-Key': '<your_api_key>'}
)
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
endpoint := "https://api.ipasis.com/v1/validate-email"
u, _ := url.Parse(endpoint)
q := u.Query()
q.Set("email", "user@mailinator.com")
q.Set("ip", "185.220.101.4")
u.RawQuery = q.Encode()
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("X-API-Key", "<your_api_key>")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}Response Fields
The email validation API returns a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| request_id | string | Unique request identifier for debugging. |
| success | boolean | Whether the request was processed successfully. |
| processed_at | string | ISO 8601 timestamp of when the request was processed. |
| risk | ||
| risk.score | number | Risk score from 0 (safe) to 100 (dangerous). |
| risk.level | string | Risk level: LOW (0-30), MEDIUM (31-60), HIGH (61-85), CRITICAL (86-100). |
| risk.recommendation | string | Suggested action: ALLOW (0-30), REVIEW (31-85), or BLOCK (86-100). |
| risk.primary_reasons | string[] | List of factors contributing to the risk score (e.g. email_disposable, ip_is_tor). |
| email.address | string | The normalized email address. |
| email.status | string | valid or invalid. |
| email.deliverability | string | deliverable, risky, or undeliverable. |
| email.type | string | personal, disposable, role, or business. |
| email.domain_age_days | number | null | Age of the email domain in days (null if unavailable). |
| email.checks | ||
| email.checks.is_valid_syntax | boolean | Whether the email has valid RFC syntax. |
| email.checks.is_disposable | boolean | True if the domain is a known disposable/temporary email provider (120k+ domains tracked). |
| email.checks.is_gibberish | boolean | True if the local part appears bot-generated (high entropy, hex patterns, excessive digits). |
| email.checks.is_newborn_domain | boolean | True if the domain was registered less than 14 days ago. |
| email.checks.is_role_account | boolean | True if the email is a role-based address (admin@, support@, info@, etc.). |
| email.checks.mx_records_found | boolean | null | Whether the domain has MX records. Null if DNS lookup timed out. |
| email.checks.is_catch_all | boolean | null | Whether the domain accepts all emails. Null if not checked. |
| email.checks.smtp_connect | boolean | null | Whether SMTP connection was successful. Null if not checked. |
| ip (included when ip parameter is provided) | ||
| ip.* | object | Full IP lookup response (same fields as the IP Lookup endpoint above). |
Risk Score Breakdown
The risk score (0-100) is calculated by combining email and IP signals:
| Signal | Points | Reason Code |
|---|---|---|
| Invalid email syntax | 100 | email_invalid_syntax |
| No MX records | 100 | email_no_mx_records |
| Disposable email domain | +40 | email_disposable |
| Newborn domain (<14 days) | +35 | email_newborn_domain |
| Gibberish/bot-like username | +25 | email_gibberish_username |
| Role account (admin@, info@) | +10 | email_role_account |
| IP is Tor exit node | +50 | ip_is_tor |
| IP is public proxy | +40 | ip_is_proxy |
| IP is VPN | +30 | ip_is_vpn |
| IP reported for abuse | +25 | ip_abuse_reported |
| IP is hosting/datacenter | +20 | ip_is_hosting |
Score is capped at 100. Tor/Proxy/VPN signals are mutually exclusive (highest value wins).
IP Lookup
GET /v1/lookup?ip=<ip>
Try It
Examples
fetch('https://api.ipasis.com/v1/lookup?ip=8.8.8.8', {
headers: { 'X-API-Key': '<your_api_key>' }
}).then(r => r.json()).then(console.log)import requests
r = requests.get('https://api.ipasis.com/v1/lookup', params={'ip':'8.8.8.8'}, headers={'X-API-Key':'<your_api_key>'})
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
endpoint := "https://api.ipasis.com/v1/lookup"
u, _ := url.Parse(endpoint)
q := u.Query()
q.Set("ip", "8.8.8.8")
u.RawQuery = q.Encode()
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("X-API-Key", "<your_api_key>")
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
String url = "https://api.ipasis.com/v1/lookup?ip=8.8.8.8";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("X-API-Key", "<your_api_key>")
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}Response Fields
The IP lookup API returns a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
| ip | string | The query IP address. |
| city | string | City name. |
| region | string | Region or state name. |
| country | string | Two-letter ISO 3166-1 country code (e.g., US). |
| loc | string | Latitude and Longitude (comma separated). |
| postal | string | Postal or ZIP code. |
| timezone | string | Timezone (IANA format). |
| asn.ASN | string | Autonomous System Number (e.g. AS15169). |
| asn.Name | string | Name of the ASN owner. |
| asn.Route | string | The BGP route block for this IP. |
| asn.Type | string | ASN type (isp, hosting, business, etc). |
| asn.Domain | string | Domain associated with the ASN. |
| company.Name | string | Company name associated with the IP range. |
| company.Domain | string | Company domain name. |
| company.Type | string | Company type. |
| privacy.vpn | boolean | True if the IP belongs to a commercial VPN provider. |
| privacy.proxy | boolean | True if the IP is a known public proxy. |
| privacy.tor | boolean | True if the IP is a Tor exit node. |
| privacy.relay | boolean | True if the IP is an iCloud Private Relay or similar privacy network. |
| privacy.hosting | boolean | True if the IP belongs to a hosting provider/datacenter (non-residential). |
| privacy.AI | boolean | True if the IP belongs to an AI provider like OpenAI etc. |
| privacy.abuse | boolean | True if the IP has been reported for abuse recently. |
| privacy.crawler | boolean | True if the IP is a known crawler (e.g., Googlebot, Bingbot). |
| privacy.Service | string | Specific service name if identified (e.g. "NordVPN"). |
| abuse.Address | string | Abuse contact address. |
| abuse.Country | string | Abuse contact country. |
| abuse.Email | string | Abuse contact email. |
| abuse.Name | string | Abuse contact name. |
| abuse.Network | string | Network CIDR involved in abuse reports. |
| abuse.Phone | string | Abuse contact phone. |
| domains.Total | number | Total number of domains hosted on this IP. |
| domains.Page | number | Current page number for domain pagination. |
| domains.Domains | string[] | List of domains hosted on this IP (may be null/empty). |
Errors
Standard HTTP status codes are used to indicate success or failure.
| Code | Meaning | Description |
|---|---|---|
| 400 | Bad Request | Missing or invalid parameters (IP address or email). |
| 401 | Unauthorized | Missing or invalid API key. |
| 403 | Forbidden | Quota exceeded or account inactive. |
| 429 | Too Many Requests | Rate limit exceeded. |
| 500 | Internal Error | Something went wrong on our end. |
Rate Limits
We limit the number of requests you can make to ensure fair usage and stability. Limits apply across both endpoints.
- Hobby (Free): 1,000 requests per day.
- Startup ($29/mo): 150,000 requests per month.
- Business ($49/mo): 500,000 requests per month.
- Scale ($199/mo): 2,000,000 requests per month.
If you exceed the limit, you will receive a 429 Too Many Requests response. Check the X-RateLimit-Limit and X-RateLimit-Remaining response headers to track your usage.
MMDB Snapshot
We provide an ipasis.mmdb snapshot compatible with the included GeoAPI.