API Reference
Complete reference for all ReportedIP REST API v2 endpoints with code examples in cURL, PHP, Python, and JavaScript.
1,000 checks · 50 reports ·
Contributor 5,000 / 200 ·
Professional 25,000 / 1,000 ·
Business 100,000 / 5,000 per licence ·
Enterprise unlimited.
Business quotas scale with the licence count (x2–x20), e.g. x5 = 500,000 / 25,000.
See Pricing for the full tier comparison.
https://reportedip.com/wp-json/reportedip/v2/
X-Key header. Public endpoints do not require authentication but are rate-limited. Keys, tiers, rate limits and error codes are explained in detail on the Authentication & Rate Limits page.
- Check quota:
GET /check(+1) andPOST /bulk-check(+N, one per valid IP). - Report quota:
POST /report(+1) andPOST /bulk-report(+N, one per submitted report). - Neither quota:
GET /blacklist,/blacklist-categories,/blacklist-stats,/reports/{ip},/trends,/statistics,/verify-key— these are tier-gated features, not metered calls. - Public IP limit: 100 requests/day per source IP, shared across all unauthenticated calls (
/check-public,/stats-public,/search-public,/categories,/attack-map-data). Send anX-Keyheader to switch to the API-key quota path instead.
Public Endpoints
These endpoints are available without authentication. Rate-limited to 100 requests per day per IP address.
GET
/check-public
— Public IP Check
Check the reputation of an IP address without authentication. Returns abuse confidence score, total reports, threat categories, and last report date. Counts +1 against the per-IP public-API daily limit (shared with the other unauthenticated endpoints).
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ip | string | Yes | — | IPv4 or IPv6 address to check |
| verbose | boolean | No | false | Include detailed confidence breakdown in response |
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/check-public?ip=1.2.3.4"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/check-public?ip=1.2.3.4'
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/check-public',
params={'ip': '1.2.3.4'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/check-public?ip=1.2.3.4'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"ip": "1.2.3.4",
"abuseConfidencePercentage": 87,
"totalReports": 42,
"lastReportedAt": "2026-03-09T14:22:31Z",
"categories": [18, 21, 22],
"countryCode": "CN",
"isp": "China Telecom"
}
}
GET
/stats-public
— Detailed Public Statistics
Retrieve extended public statistics with category breakdowns, top threat types, and historical data. Counts +1 against the per-IP public-API daily limit.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/stats-public"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/stats-public'
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/stats-public'
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/stats-public'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"totalIPs": 158432,
"totalReports": 892741,
"last24h": {
"newIPs": 342,
"newReports": 1847
},
"last7d": {
"newIPs": 2156,
"newReports": 12493
},
"categoryBreakdown": [
{"id": 18, "name": "Brute-Force", "count": 45231},
{"id": 21, "name": "Web App Attack", "count": 32198},
{"id": 22, "name": "SSH", "count": 28764},
{"id": 15, "name": "Port Scan", "count": 18432}
],
"topCountries": [
{"code": "CN", "count": 42156},
{"code": "US", "count": 18743},
{"code": "RU", "count": 15892}
]
}
}
GET
/search-public
— Public IP Search
Search the public reputation database by partial IP, country code, or ASN. Useful for the public-facing search bar on reportedip.com. Counts +1 against the per-IP public-API daily limit.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| q | string | Yes | — | Search query: full or partial IP, country code (e.g. "CN"), or ASN. |
| limit | int | No | 20 | Number of results (max 50). |
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/search-public?q=1.2.3"
Response
{
"meta": {"success": true, "code": 200},
"data": {
"query": "1.2.3",
"results": [
{"ip": "1.2.3.4", "abuseConfidencePercentage": 87, "totalReports": 42, "countryCode": "CN"},
{"ip": "1.2.3.5", "abuseConfidencePercentage": 65, "totalReports": 12, "countryCode": "CN"}
]
}
}
GET
/categories
— Threat Categories
Retrieve the list of all 30 predefined threat categories used for classifying IP reports. Each category includes its ID, name, description, and severity level. Counts +1 against the per-IP public-API daily limit when called without an API key; with an X-Key header it does not count against any daily quota.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/categories"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/categories'
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/categories'
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/categories'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": [
{
"id": 1,
"name": "DNS Compromise",
"description": "Compromised DNS server or DNS hijacking",
"severity_level": 8
},
{
"id": 18,
"name": "Brute-Force",
"description": "Login brute-force attempts",
"severity_level": 7
},
{
"id": 21,
"name": "Web App Attack",
"description": "Web application attacks (SQLi, XSS, etc.)",
"severity_level": 9
}
]
}
GET
/attack-map-data
— Live Attack Map Dataset
Aggregated dataset that powers the live attack map on reportedip.com. Returns per-country report counts (top 50), the rolling 24-hour set of cross-country attack connections (top 30, same-country pairs excluded) and a today-logs counter. Cached server-side for 30 minutes — repeat calls within that window all hit the cache. Counts +1 against the per-IP public-API daily limit.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/attack-map-data"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/attack-map-data'
);
$data = json_decode( wp_remote_retrieve_body( $response ), true );
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/attack-map-data'
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/attack-map-data'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"country_data": {
"CN": { "count": 42156, "avgThreat": 7.8 },
"US": { "count": 18743, "avgThreat": 5.4 },
"RU": { "count": 15892, "avgThreat": 7.1 }
},
"country_info": { },
"total_reports": 76791,
"max_reports": 42156,
"recent_attacks": [
{
"id": 1,
"source_country": "CN",
"target_country": "DE",
"confidence_score": 88,
"attack_count": 214
}
],
"today_logs": 1847
}
}
Authenticated Endpoints
These endpoints require a valid API key sent via the X-Key header. Get your API key from the Dashboard.
GET
/check
— Full IP Check
Perform a full IP reputation check with detailed data. Returns comprehensive threat data including confidence breakdown, report history, and ISP information. Charges +1 against your daily check quota.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ip | string | Yes | — | IPv4 or IPv6 address to check |
| verbose | boolean | No | false | Include detailed confidence score breakdown |
| include_reports | boolean | No | false | Include individual report details in response |
Examples
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/check?ip=1.2.3.4&verbose=true"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/check?ip=1.2.3.4&verbose=true',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/check',
params={'ip': '1.2.3.4', 'verbose': 'true'},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/check?ip=1.2.3.4&verbose=true',
{
headers: { 'X-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"ip": "1.2.3.4",
"abuseConfidencePercentage": 87,
"totalReports": 42,
"lastReportedAt": "2026-03-09T14:22:31Z",
"categories": [18, 21, 22],
"countryCode": "CN",
"isp": "China Telecom",
"isWhitelisted": false,
"confidenceBreakdown": {
"reportScore": 24.0,
"diversityScore": 12.0,
"recencyWeight": 13.5,
"severityWeight": 12.3,
"honeypotBonus": 25.0,
"maxConfidence": 100,
"constraintReasons": []
},
"debugInfo": {
"reportCount": 42,
"uniqueReporters": 8,
"honeypotReportsCount": 5,
"effectiveReportCount": 47,
"timeDampeningFactor": 0.92
}
}
}
POST
/report
— Report IP Address
Report a malicious IP address to the community. The report will be aggregated with existing data to update the IP's abuse confidence score. Reports from the same reporter for the same IP within 5 minutes are automatically aggregated. Charges +1 against your daily report quota only — never against the check quota.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ip | string | Yes | — | IPv4 or IPv6 address to report |
| categories | array | Yes | — | Array of threat category IDs (e.g. [18, 21]) |
| comment | string | No | — | Optional comment describing the observed malicious activity |
Examples
curl -X POST \
-H "X-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ip": "1.2.3.4", "categories": [18, 21], "comment": "SSH brute force"}' \
"https://reportedip.com/wp-json/reportedip/v2/report"
$response = wp_remote_post(
'https://reportedip.com/wp-json/reportedip/v2/report',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(array(
'ip' => '1.2.3.4',
'categories' => array(18, 21),
'comment' => 'SSH brute force',
)),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.post(
'https://reportedip.com/wp-json/reportedip/v2/report',
json={
'ip': '1.2.3.4',
'categories': [18, 21],
'comment': 'SSH brute force'
},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/report',
{
method: 'POST',
headers: {
'X-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
ip: '1.2.3.4',
categories: [18, 21],
comment: 'SSH brute force'
})
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "Report submitted successfully"
},
"data": {
"reportId": 58234,
"ip": "1.2.3.4",
"categories": [18, 21],
"abuseConfidencePercentage": 89
}
}
GET
/verify-key
— Verify API Key
Verify your API key and retrieve account details including user role, permissions, rate limits, and current usage statistics. Does not count against any daily quota.
Examples
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/verify-key"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/verify-key',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/verify-key',
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/verify-key',
{
headers: { 'X-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"valid": true,
"role": "reportedip_professional",
"permissions": {
"check": true,
"report": true,
"bulk_check": true,
"bulk_report": false,
"reports_detail": true,
"trends": true
},
"rateLimits": {
"checksPerDay": 25000,
"reportsPerDay": 1000
},
"usage": {
"checksToday": 1247,
"reportsToday": 38
}
}
}
GET
/statistics
— Service Statistics
Public, service-wide statistics: total tracked IPs, total reports, reports submitted today and active API keys. No API key required — rate-limited per IP like the other public endpoints. Does not count against any daily quota. Your personal usage and quota figures live in the <a href="https://reportedip.com/dashboard/">Dashboard</a>.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/statistics"
Response
{
"meta": {"success": true, "code": 200},
"data": {
"total_ips": 181402,
"total_reports": 2643918,
"reports_today": 10457,
"active_api_keys": 312
}
}
Blacklist Endpoints
Access the community-driven IP blacklist, automatically scored from real-time reports. Available in JSON, plain text, and CSV formats for firewall integration.
GET
/blacklist
— Get Blacklist
Retrieve the IP blacklist in various formats. Supports filtering by confidence level and threat category — combine both to build service-specific block lists (SSH, mail, web, FTP). Lists are regenerated server-side every 15 minutes; the endpoint returns an ETag and honors If-None-Match, so unchanged lists answer with 304 Not Modified. Does not count against any daily quota — tier-gated feature (Contributor and up).
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| format | string | No | json | Response format: json, txt, or csv |
| source | string | No | dynamic | Blacklist source: dynamic (default) — the community-driven, automatically scored blacklist. Other values are reserved for internal use |
| confidence | integer | No | 75 | Minimum abuse confidence percentage (0-100). Alias: confidenceMinimum. Recommended for automated blocking: 90 |
| category | string | No | — | One or more threat category IDs, comma-separated (e.g. 22,18 for an SSH block list). Full catalogue via GET /categories |
| limit | integer | No | 10000 | Maximum number of entries to return. Keep the default — smaller values truncate the list |
Examples
# JSON format
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90"
# Plain text (one IP per line, for firewall rules)
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90&format=txt&limit=10000"
# Service-specific list: SSH attackers only (categories 22 + 18)
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90&format=txt&limit=10000&category=22,18"
# Conditional request: returns 304 Not Modified if the list is unchanged
curl -H "X-Key: YOUR_API_KEY" \
-H 'If-None-Match: W/"2438413-90-2f6c8ad881b6"' \
"https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90&format=txt&limit=10000"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist',
params={
'confidence': 90,
'format': 'txt',
'limit': 10000,
'category': '22,18'
},
headers={'X-Key': 'YOUR_API_KEY'}
)
ips = response.text.splitlines()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/blacklist?confidence=90',
{
headers: { 'X-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
Response
{
"meta": {
"generatedAt": "2026-07-21T17:43:13+00:00",
"confidence": "90",
"confidenceMinimum": "90",
"count": 4821,
"format": "reportapi_v2"
},
"data": [
{
"ip": "1.2.3.4",
"ipAddress": "1.2.3.4",
"abuseConfidencePercentage": 100,
"lastReportedAt": "2026-07-21T14:22:31+00:00"
},
{
"ip": "5.6.7.8",
"ipAddress": "5.6.7.8",
"abuseConfidencePercentage": 95,
"lastReportedAt": "2026-07-21T13:45:12+00:00"
}
]
}
GET
/blacklist-categories
— Blacklist Categories
Retrieve all available blacklist categories used for classifying entries. Does not count against any daily quota.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/blacklist-categories"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-categories'
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-categories'
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-categories'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": [
{
"id": 1,
"name": "Botnet",
"slug": "botnet",
"description": "Known botnet command and control or participant"
},
{
"id": 2,
"name": "Brute-Force",
"slug": "brute-force",
"description": "Credential brute-force attacks"
},
{
"id": 3,
"name": "Spam",
"slug": "spam",
"description": "Spam sources"
}
]
}
GET
/blacklist-stats
— Blacklist Statistics
Retrieve statistics about the blacklist including total counts and distribution by confidence level and category. Does not count against any daily quota.
Examples
curl "https://reportedip.com/wp-json/reportedip/v2/blacklist-stats"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-stats'
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-stats'
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/blacklist-stats'
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"total": 12483,
"community": 12483,
"byConfidence": {
"90-100": 4821,
"75-89": 3247,
"50-74": 2891,
"25-49": 1524
},
"byCategory": [
{"name": "Brute-Force", "count": 5432},
{"name": "Web App Attack", "count": 3218},
{"name": "SSH", "count": 2847},
{"name": "Port Scan", "count": 986}
]
}
}
Advanced Endpoints
Advanced endpoints for power users. Require Professional or Enterprise tier API keys.
GET
/reports/{ip}
— Reports for IP
Retrieve all individual reports for a specific IP address. Returns paginated report details including reporter info, categories, and timestamps. Does not count against any daily quota — tier-gated feature (Professional and up).
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ip | string | Yes | — | IPv4 or IPv6 address (in URL path) |
| page | integer | No | 1 | Page number for pagination |
| per_page | integer | No | 25 | Reports per page |
Examples
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/reports/1.2.3.4?page=1&per_page=25"
$ip = '1.2.3.4';
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/reports/' . $ip . '?page=1',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
ip = '1.2.3.4'
response = requests.get(
f'https://reportedip.com/wp-json/reportedip/v2/reports/{ip}',
params={'page': 1, 'per_page': 25},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const ip = '1.2.3.4';
const response = await fetch(
`https://reportedip.com/wp-json/reportedip/v2/reports/${ip}?page=1&per_page=25`,
{
headers: { 'X-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"ip": "1.2.3.4",
"reports": [
{
"id": 58234,
"categories": [18, 21],
"comment": "SSH brute force with 500+ attempts",
"reportedAt": "2026-03-09T14:22:31Z",
"reporterRole": "honeypot",
"aggregationCount": 12
},
{
"id": 58102,
"categories": [22],
"comment": "Port scanning on TCP 22, 80, 443, 8080",
"reportedAt": "2026-03-09T10:15:00Z",
"reporterRole": "contributor",
"aggregationCount": 0
}
],
"total": 42,
"page": 1,
"per_page": 25,
"total_pages": 2
}
}
GET
/trends
— Trend Data
Retrieve trend and time-series data showing attack patterns over a specified time period. Useful for dashboards and analytics. Does not count against any daily quota — tier-gated feature (Professional and up).
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| period | string | No | 7d | Time period: 24h, 7d, or 30d |
| category | integer | No | — | Filter by threat category ID |
Examples
curl -H "X-Key: YOUR_API_KEY" \
"https://reportedip.com/wp-json/reportedip/v2/trends?period=7d"
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/trends?period=7d',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/trends',
params={'period': '7d'},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/trends?period=7d',
{
headers: { 'X-Key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"period": "7d",
"timeSeries": [
{"date": "2026-03-03", "reports": 1842, "newIPs": 312},
{"date": "2026-03-04", "reports": 2104, "newIPs": 356},
{"date": "2026-03-05", "reports": 1956, "newIPs": 298},
{"date": "2026-03-06", "reports": 2347, "newIPs": 401},
{"date": "2026-03-07", "reports": 1893, "newIPs": 287},
{"date": "2026-03-08", "reports": 2156, "newIPs": 342},
{"date": "2026-03-09", "reports": 1847, "newIPs": 318}
],
"topCategories": [
{"id": 18, "name": "Brute-Force", "count": 5432},
{"id": 21, "name": "Web App Attack", "count": 3218},
{"id": 22, "name": "SSH", "count": 2847}
]
}
}
POST
/bulk-check
— Bulk IP Check
Check the reputation of multiple IP addresses in a single request. Accepts up to 1,000 IPs per request. Charges +N against your daily check quota where N is the number of valid IPs in the request. If the call would exceed the remaining quota the server returns HTTP 429 and the counter is not incremented.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| ips | array | Yes | — | Array of IPv4/IPv6 addresses (max 1,000) |
Examples
curl -X POST \
-H "X-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ips": ["1.2.3.4", "5.6.7.8", "9.10.11.12"]}' \
"https://reportedip.com/wp-json/reportedip/v2/bulk-check"
$response = wp_remote_post(
'https://reportedip.com/wp-json/reportedip/v2/bulk-check',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(array(
'ips' => array('1.2.3.4', '5.6.7.8', '9.10.11.12'),
)),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
response = requests.post(
'https://reportedip.com/wp-json/reportedip/v2/bulk-check',
json={
'ips': ['1.2.3.4', '5.6.7.8', '9.10.11.12']
},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/bulk-check',
{
method: 'POST',
headers: {
'X-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
ips: ['1.2.3.4', '5.6.7.8', '9.10.11.12']
})
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "OK"
},
"data": {
"results": [
{
"ip": "1.2.3.4",
"abuseConfidencePercentage": 87,
"totalReports": 42,
"lastReportedAt": "2026-03-09T14:22:31Z",
"categories": [18, 21, 22]
},
{
"ip": "5.6.7.8",
"abuseConfidencePercentage": 0,
"totalReports": 0,
"lastReportedAt": null,
"categories": []
},
{
"ip": "9.10.11.12",
"abuseConfidencePercentage": 45,
"totalReports": 3,
"lastReportedAt": "2026-03-07T09:30:00Z",
"categories": [15]
}
],
"checked": 3
}
}
POST
/bulk-report
— Bulk Report
Submit multiple IP reports in a single request using CSV-formatted data. Designed for automated systems and honeypots that need to report large volumes of malicious activity. Charges +N against your daily report quota where N is the number of submitted reports. Never counts against the check quota.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| csv | string | Yes | — | CSV data with columns: IP, Categories, Comment (one report per line) |
Examples
curl -X POST \
-H "X-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"csv": "1.2.3.4,18|21,SSH brute force\n5.6.7.8,22,Port scanning\n9.10.11.12,15|18,Distributed attack"}' \
"https://reportedip.com/wp-json/reportedip/v2/bulk-report"
$csv = "1.2.3.4,18|21,SSH brute force\n";
$csv .= "5.6.7.8,22,Port scanning\n";
$csv .= "9.10.11.12,15|18,Distributed attack";
$response = wp_remote_post(
'https://reportedip.com/wp-json/reportedip/v2/bulk-report',
array(
'headers' => array(
'X-Key' => 'YOUR_API_KEY',
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(array(
'csv' => $csv,
)),
)
);
$data = json_decode(wp_remote_retrieve_body($response), true);
import requests
csv_data = """1.2.3.4,18|21,SSH brute force
5.6.7.8,22,Port scanning
9.10.11.12,15|18,Distributed attack"""
response = requests.post(
'https://reportedip.com/wp-json/reportedip/v2/bulk-report',
json={'csv': csv_data},
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const csvData = [
'1.2.3.4,18|21,SSH brute force',
'5.6.7.8,22,Port scanning',
'9.10.11.12,15|18,Distributed attack'
].join('\n');
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/bulk-report',
{
method: 'POST',
headers: {
'X-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ csv: csvData })
}
);
const data = await response.json();
Response
{
"meta": {
"success": true,
"code": 200,
"message": "Bulk report processed"
},
"data": {
"success_count": 3,
"error_count": 0,
"errors": []
}
}
2FA Relay Endpoints
Managed 2FA delivery for the ReportedIP Hive WordPress plugin. PRO+ tiers may queue authentication mails and SMS through this relay; the service consumes the monthly inclusive quota first and falls back to the prepaid bundle balance once the inclusive cap is reached.
not eligible ·
Professional 500 mails · 25 SMS ·
Business 2,500 mails · 75 SMS ·
Enterprise fair-use unlimited.
PRO and Business may top up via prepaid bundles
(hive_mail_bundle_1000/5000/25000, hive_sms_bundle_50/200/500).
bundle_balance is signed:
a Stripe refund (charge.refunded) on a previously consumed
bundle reduces the saldo and may push it below zero. While the saldo is
non-positive and the inclusive quota is exhausted, sends fail with HTTP
402 cap_reached_buy_bundle until a fresh bundle is purchased.
GET
/relay-quota
— Relay Quota Status
Returns the inclusive monthly quota plus the prepaid bundle balance for both Mail and SMS relay. Used by the Hive plugin dashboard to render usage bars and bundle hints.
Examples
curl -H 'X-Key: YOUR_API_KEY' \
https://reportedip.com/wp-json/reportedip/v2/relay-quota
$response = wp_remote_get(
'https://reportedip.com/wp-json/reportedip/v2/relay-quota',
array( 'headers' => array( 'X-Key' => 'YOUR_API_KEY' ) )
);
$data = json_decode( wp_remote_retrieve_body( $response ), true );
import requests
response = requests.get(
'https://reportedip.com/wp-json/reportedip/v2/relay-quota',
headers={'X-Key': 'YOUR_API_KEY'}
)
data = response.json()
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/relay-quota',
{ headers: { 'X-Key': 'YOUR_API_KEY' } }
);
const data = await response.json();
Response
{
"tier": "professional",
"role": "reportedip_professional",
"mail": {
"sent": 18,
"queued": 0,
"queued_total": 18,
"failed": 0,
"limit": 500,
"inclusive_limit": 500,
"inclusive_used": 18,
"inclusive_available": 482,
"bundle_balance": 1000,
"bundle_eligible": true,
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z"
},
"sms": {
"sent": 3,
"queued": 0,
"queued_total": 3,
"failed": 0,
"limit": 25,
"inclusive_limit": 25,
"inclusive_used": 3,
"inclusive_available": 22,
"bundle_balance": 50,
"bundle_eligible": true,
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z"
},
"mail_bundle_balance": 1000,
"sms_bundle_balance": 50
}
POST
/relay-mail
— Send 2FA Mail
Queues a transactional 2FA mail through the SPF/DKIM/DMARC-verified relay. Consumes one slot from the inclusive monthly mail quota; once exhausted, one credit from `mail_bundle_balance`. Returns HTTP 402 when both are empty.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| recipient | string | Yes | — | Recipient email address |
| subject | string | Yes | — | Mail subject line |
| body_html | string | No | '' | HTML body of the mail |
| body_text | string | No | '' | Plain-text body / fallback |
| reply_to | string | No | '' | Reply-To address — plain email or RFC 5322 <code>Display Name <email@host></code> |
| headers | array | No | [] | Additional mail headers |
| site_url | string | No | '' | Origin site URL (used for per-site daily caps and audit logs) |
Examples
curl -X POST \
-H 'X-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"recipient":"user@example.com","subject":"Your 2FA code","body_html":"<p>Code: 123456</p>","body_text":"Code: 123456"}' \
https://reportedip.com/wp-json/reportedip/v2/relay-mail
$response = wp_remote_post(
'https://reportedip.com/wp-json/reportedip/v2/relay-mail',
array(
'headers' => array( 'X-Key' => 'YOUR_API_KEY', 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'recipient' => 'user@example.com',
'subject' => 'Your 2FA code',
'body_html' => '<p>Code: 123456</p>',
'body_text' => 'Code: 123456',
) ),
)
);
import requests
response = requests.post(
'https://reportedip.com/wp-json/reportedip/v2/relay-mail',
headers={'X-Key': 'YOUR_API_KEY'},
json={
'recipient': 'user@example.com',
'subject': 'Your 2FA code',
'body_html': '<p>Code: 123456</p>',
'body_text': 'Code: 123456',
},
)
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/relay-mail',
{
method: 'POST',
headers: { 'X-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
recipient: 'user@example.com',
subject: 'Your 2FA code',
body_html: '<p>Code: 123456</p>',
body_text: 'Code: 123456',
}),
}
);
Response
{
"ok": true,
"queue_id": 48217,
"remaining_quota": {
"sent": 18,
"queued": 1,
"queued_total": 19,
"failed": 0,
"limit": 500,
"inclusive_limit": 500,
"inclusive_used": 19,
"inclusive_available": 481,
"bundle_balance": 1000,
"bundle_eligible": true,
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z"
}
}
POST
/relay-sms
— Send 2FA SMS
Queues a 2FA SMS via the managed worldwide relay. The body is server-side rendered from a registered template (e.g. <code>2fa_login</code>) so the actual code never appears in client logs. HTTP 402 when both the inclusive quota and the prepaid bundle are exhausted; HTTP 422 with code <code>country_not_supported</code> when the destination country is unavailable for cost or operational reasons; HTTP 429 when the recipient or per-site daily anti-fraud cap is hit.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| recipient_phone | string | Yes | — | Recipient phone in E.164 format (e.g. +49170...) |
| template_code | string | No | '' | Server-side template id, e.g. <code>2fa_login</code>. When set, the message is rendered server-side and <code>message</code> is ignored |
| template_vars | object | No | {} | Template variables: <code>{ code, expiry_min, lang? }</code> |
| message | string | No | '' | Raw message text — used only when no <code>template_code</code> is given. One of <code>message</code> or <code>template_code</code> is required (HTTP 422 otherwise) |
| site_url | string | No | '' | Origin site URL (used for per-site daily caps and audit logs) |
Examples
curl -X POST \
-H 'X-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"recipient_phone":"+491701234567","template_code":"2fa_login","template_vars":{"code":"123456","expiry_min":5,"lang":"de"}}' \
https://reportedip.com/wp-json/reportedip/v2/relay-sms
$response = wp_remote_post(
'https://reportedip.com/wp-json/reportedip/v2/relay-sms',
array(
'headers' => array( 'X-Key' => 'YOUR_API_KEY', 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'recipient_phone' => '+491701234567',
'template_code' => '2fa_login',
'template_vars' => array( 'code' => '123456', 'expiry_min' => 5, 'lang' => 'de' ),
) ),
)
);
import requests
response = requests.post(
'https://reportedip.com/wp-json/reportedip/v2/relay-sms',
headers={'X-Key': 'YOUR_API_KEY'},
json={
'recipient_phone': '+491701234567',
'template_code': '2fa_login',
'template_vars': {'code': '123456', 'expiry_min': 5, 'lang': 'de'},
},
)
const response = await fetch(
'https://reportedip.com/wp-json/reportedip/v2/relay-sms',
{
method: 'POST',
headers: { 'X-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
recipient_phone: '+491701234567',
template_code: '2fa_login',
template_vars: { code: '123456', expiry_min: 5, lang: 'de' },
}),
}
);
Response
{
"ok": true,
"queue_id": 901,
"remaining_quota": {
"sent": 3,
"queued": 1,
"queued_total": 4,
"failed": 0,
"limit": 25,
"inclusive_limit": 25,
"inclusive_used": 4,
"inclusive_available": 21,
"bundle_balance": 50,
"bundle_eligible": true,
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-05-31T23:59:59Z"
}
}
Error responses for relay endpoints
| Status | Code | Meaning |
|---|---|---|
401 | missing_api_key / invalid_api_key | The X-Key header is missing, malformed, or refers to a deactivated key. |
403 | tier_forbidden | The caller's tier (Free / Contributor) is not eligible for relay delivery. |
402 | cap_reached_buy_bundle | Inclusive monthly cap reached and the prepaid bundle balance is empty or negative after a refund. Buy a bundle in the dashboard to resume. |
402 | monthly_cap_reached | Inclusive monthly cap reached on a tier without bundle eligibility. |
429 | recipient_backoff / site_daily_cap | Anti-fraud throttle. The retry-after header indicates how long to wait. |
Deprecated Endpoints
These endpoints are deprecated and will be removed in a future version. Please migrate to the recommended alternatives.
GET
/blacklist-detailed
— Detailed Blacklist (Deprecated)
Previously returned a detailed blacklist. Kept alive for backward compatibility with older Hive installations. Does not count against any daily quota (since v2.5.1 — was counted as a check before).
GET
/blacklist-combined
— Combined Blacklist (Deprecated)
Previously returned a combined blacklist from all sources. Kept alive for backward compatibility with older Hive installations. Does not count against any daily quota (since v2.5.1).
GET
/blacklist-export/git
— Git Export (Deprecated)
Previously exported the blacklist in plain text format for Git repository integration. Kept alive for backward compatibility. Does not count against any daily quota (since v2.5.1).
Last updated: · Maintained by the ReportedIP team