Skip to main content
POST /ip-check scores any public IP address against three independent reputation sources and returns one verdict. This page explains the rules behind that verdict; the fields themselves are documented in Check an IP address.
Every source is also readable on its own, for free, through Compare every source. That endpoint calls no vendor: it reports the locally held public blocklists plus whatever a recent paid check on the same address left in the cache. Use it when you want to see who says what rather than a single verdict.

The sources

Three vendors are queried in parallel, each returning its own reading: Scamalytics, IPQualityScore and ipdata.co. Every response names them, so you can see who said what rather than trusting a single opaque number. They publish incompatible scales — two report a 0–100 fraud score, the third a set of booleans. Each is therefore first collapsed onto a shared three-step band (clean, suspicious, high_risk) on its own scale, and only then combined.

How the verdict is decided

The verdict is a fixed table, not a tuned formula. Rules are evaluated in order and the first match wins: Corroboration is what separates risky from suspicious: one vendor calling an address questionable is an outlier, two agreeing is a signal.

risk_score is a maximum, not an average

The reported risk_score is the highest normalised value any vendor gave. Averaging was rejected on purpose — a mean over two 0–100 scores and one boolean object is arithmetic on units that do not mean the same thing, and it would let one lenient vendor dilute a genuine warning from another.

Proxy and datacenter detection never move the verdict

signals.proxy_detected and signals.datacenter are reported, and deliberately not scored. You hand us a bare address and no statement of what it is meant to be. “This looks like a proxy” is a defect for a residential product and the entire point of a datacenter one, so grading it would be guessing at your intent — and it would mark every datacenter address as bad, which would make the check useless for exactly the customers who run them. Read those two signals yourself, against what you know the address is for.

When a source cannot be reached

A vendor that fails is excluded from the verdict, never counted as clean. Its entry comes back with status: "unavailable" and no band, and the response is flagged partial: true so you know the verdict rests on fewer opinions than usual.
Never read a missing band as a good sign. partial: true means less evidence, not less risk.
If fewer than two sources answer, no verdict is issued at all: the call fails with 503 and nothing is charged. Our own outage cannot cost you money or consume a free check.

What a check costs

A check costs $0.10, debited from your wallet.
The charge is in USD and nothing is converted. A balance held only in another currency answers 409 with error.code of INSUFFICIENT_FUNDS even though the account has funds. Top up your USD balance before checking.
Two things make a check free. Free checks you have earned. Every proxy order delivered to you grants one. Orders that failed provisioning and were refunded do not count — the entitlement follows delivery, not payment. Call Get check quota to see how many are left. A recent result for the same address. Results are cached for 15 minutes, and the cache is keyed on the address alone — not on who asked. If anyone checked that address recently, you get the stored answer at no charge.
Because the cache is shared, a check can be free even when your quota reads free_available: 0. It cannot be predicted before you send the address, so quote the price as “$0.10” and treat a zero charge as a bonus rather than promising an exact amount up front.
Tell the two apart from the response: free: true with cached: true is a reused answer, free: true with cached: false spent one of your own.

What lands in your history

List checks is a record of stored results, not a log of every request you sent. Two kinds of call write nothing:
  • Cache hits. The answer belongs to whoever originally paid for it, so no row is created and id comes back null.
  • Failed checks. A 503 produces no verdict, so there is nothing to store.
Everything else gets an id you can reopen later with Get a check.

Availability

The endpoint is behind the ip_reputation.check.enabled feature flag. Read GET /flags to find out whether it is on for your account; a disabled feature answers 403.