The reason people stay on a fraud API they have outgrown is almost never the argument. It is that moving means touching parsing code, alert thresholds, dashboards and tests — a week of engineering against a saving that does not cover it. So LayerCall speaks the shape you already parse, on both products.
minFraud
score and insights, same POST, same body. Your account id goes in the username position and is read and discarded — we have nothing to match it against, and pretending to validate it would be theatre.
Before
POST https://minfraud.maxmind.com/minfraud/v2.0/insights Authorization: Basic <account_id>:<license_key>
After
POST https://www.layercall.com/compat/maxmind/minfraud/v2.0/insights Authorization: Basic <anything>:<your_layercall_key>
GeoIP2 Precision
country, city and insights, at the same three depths — country only, then city and location, then the full trait set.
Before
GET https://geoip.maxmind.com/geoip/v2.1/insights/1.2.3.4
After
GET https://www.layercall.com/compat/maxmind/geoip/v2.1/insights/1.2.3.4
What maps to what
| Their field | What we put in it |
|---|---|
| risk_score | our 0–100 score, clamped into their 0.01–99 range |
| disposition.action | accept / manual_review / reject — exactly our three verdicts |
| ip_address.risk | the IP component's own score |
| ip_address.country.iso_code / city.names.en | resolved country and city |
| ip_address.traits.is_anonymous | VPN OR proxy OR Tor, matching their umbrella meaning |
| ip_address.traits.is_anonymous_vpn | commercial VPN exit detection |
| ip_address.traits.is_public_proxy | open-proxy detection |
| ip_address.traits.is_tor_exit_node | the Tor Project's own exit list, refreshed every 6 hours |
| ip_address.traits.is_hosting_provider | datacenter ASN classification |
| ip_address.traits.isp / organization / autonomous_system_* | ASN and operator |
| email.is_disposable / is_free | disposable-domain and consumer-provider detection |
| email.is_high_risk | anything our verdict would not wave through |
| email.first_seen | when LAYERCALL first saw the address — see the note below |
| warnings[] | their shape, used to name every input block we ignored |
Every response also carries a layercall object with what their schema has nowhere to put: the phone component — one of the strongest signals at signup, and a field minFraud only carries as part of an address — plus the linkage counts, the actor classification and the ranked top signals behind the score.
What we return null for, and why
A compatibility layer that fills every field so the response looks complete is a compatibility layer that lies. Anything we do not measure comes back null — not false, which would assert we checked. This is the complete list.
minfraud
funds_remainingqueries_remainingcredit_cardbilling_addressshipping_addressbilling_phoneshipping_phonesubscoresNothing in a card, an address or an order reaches our engine, so any score derived from them would be ours to invent. funds_remaining and queries_remaining belong to a prepaid balance model we do not run — usage and spend live on your dashboard, and the per-hour rate limit is a different thing wearing a similar name.
traits
is_residential_proxyis_anonymous_proxyis_satellite_provideris_legitimate_proxystatic_ip_scoreuser_countuser_typenetworkconnection_typedomainmobile_country_codemobile_network_codeis_residential_proxy is the one worth calling out: it needs a residential-proxy feed that is a paid subscription we have deliberately not bought yet. It is the field somebody switching would check first, and returning false on every address would be a lie that looks like a feature. The rest are either deprecated upstream or built from minFraud's own traffic, which we do not see.
location
latitudelongitudeaccuracy_radiustime_zonemetro_codepopulation_densityaverage_incomelocal_timeCoordinates, accuracy radius and timezone are not in our geo bundle. A guessed latitude is worse than none, and an accuracy_radius would be a confidence we never computed.
domain_first_seendomain_first_seen is a corpus statistic from their network. Ours — when LayerCall first saw the address — is a different fact, so it is returned as itself under email.first_seen rather than dressed up as theirs.
Three things that will differ
The score range, at the ends
Their scale is 0.01 to 99 and excludes both ends; ours is an integer 0 to 100. A perfectly clean address cannot be reported as 0 and a certain block cannot be reported as 100, so both are clamped. Your threshold keeps compiling — it will not keep meaning the same thing, because different engines disagree, which is the whole reason anyone runs both. The bulk checker settles that with your own list, no account needed.
factors is not served
Its value is the per-category subscores — email tenure, device, AVS, issuer id number — computed from signals we do not collect. A factors response with every subscore null would be an empty envelope with the right name on it, so it returns a 404 that says this rather than a 200 that implies otherwise. Use insights.
Ignored input is reported, not swallowed
Send a shipping address, an order or a card and the request succeeds — with a LAYERCALL_INPUT_IGNORED entry in their own warnings array naming each block that had no effect. The code is namespaced deliberately: borrowing one of theirs for a condition that is not theirs is how an error handler comes to do the wrong thing confidently.
The free tier is 1,000 lookups a month with no card and no daily cap — enough to point a copy of your real traffic at both and compare the answers rather than the marketing.
MaxMind, minFraud and GeoIP2 are trademarks of MaxMind, Inc. LayerCall is not affiliated with or endorsed by them. This page documents response formats we implement so their customers can leave without rewriting; every value above is produced by LayerCall’s own engine.