# Block datacenter and bot signups with one IP lookup

Scripted signups come from cloud boxes. How is_datacenter, hosting_provider and asn_type separate them from people, and the proxy mistake that hides it.

Date: 2026-09-04

Most scripted signups do not come from someone's living room. They come from a virtual machine at a cloud provider, because that is where scripts run: cheap, disposable, and reachable from a shell. A person filling in your form from home arrives on a broadband or mobile network with an operator whose name says telecom, cable or wireless. That single difference, infrastructure versus eyeball network, is the highest-yield check you can run on a registration, and one IP lookup answers it.

This guide shows what to read in the response, which decision to attach to each field, and the one server misconfiguration that quietly turns the check into noise.

## What the response says about infrastructure

Three fields carry the answer, from broad to specific.

`is_datacenter` is true when the address sits in a known hosting or cloud range. It is built from two kinds of source: the providers' own published range lists (Amazon Web Services publishes `ip-ranges.json`, Google Cloud `cloud.json`, Microsoft its weekly service-tags file, Oracle its public ranges, DigitalOcean, Linode and Vultr RFC 8805 geofeeds, Cloudflare and Fastly their edge ranges) and the open X4BNet list, which classifies whole autonomous systems as datacenter space. The flag is tri-state: `false` is a checked negative, `null` means no feed covered the address family, which today never happens for this signal, since the feeds carry IPv6.

`hosting_provider` names the cloud when the address is inside that provider's own feed: Amazon Web Services, Google Cloud, Microsoft Azure, Oracle Cloud, DigitalOcean, Linode, Vultr, Cloudflare or Fastly. It stays null when the flag came only from the aggregate list, so a null does not mean "not hosting"; it means "hosting, provider not named".

`asn_type` classifies the network operator: `hosting`, `isp`, `education`, `government`, or null when the operator's name settles nothing. It is `hosting` whenever `is_datacenter` is true, and it is derived from the operator name otherwise, conservatively. An unrecognised name answers null rather than a guess.

Together they let you write the policy in plain terms. A signup from `is_datacenter: true` with `hosting_provider: "Amazon Web Services"` and a free-mail address is a script until proven otherwise. A signup from `asn_type: "isp"` with `is_datacenter: false` is a person on a home line, whatever else you know about them.

## Attach a decision, not a score

The `risk_score` adds 40 for a datacenter address, which lands in the medium band on its own. That is deliberate: hosting space is not proof of abuse. Plenty of legitimate traffic comes from servers, including your own customers' backends calling your API, corporate VPN concentrators hosted in a cloud, and developers testing from a workstation in a datacenter office. A score is the wrong tool for a decision that depends on what the visitor is doing.

Branch on the flag and the context instead:

- **Free trial signups.** Require email confirmation before the trial starts when `is_datacenter` is true. A script that cannot read a mailbox stops here; a person is delayed by a minute.
- **Account creation with a payment method.** Let it through. The card check is stronger evidence than the network.
- **Contact and lead forms.** Tag the lead with `hosting_provider` and route datacenter submissions to a review queue rather than to sales. Reading the provider name in the queue is often enough to close the case: a burst of leads from one cloud region is not a campaign result.
- **API calls to your own service.** Never block on this flag. Your customers' servers live in datacenters by definition.

The [IP intelligence guide](/docs/ip-intelligence) documents every field the policy can read, and the [endpoint reference](/docs/api/lookup-ip) carries the full example response.

## Run it where the signup happens

A server-side check is one request. Send the address the visitor connected from, not the one in a form field, and read the flags:

```bash
curl https://spaw.co/api/v1/ip/203.0.113.9 \
  -H "Authorization: Bearer sk_live_…"
```

```json
{
  "success": true,
  "data": {
    "ip": "203.0.113.9",
    "asn": 16509,
    "org": "Amazon.com, Inc.",
    "asn_type": "hosting",
    "is_datacenter": true,
    "hosting_provider": "Amazon Web Services",
    "is_tor": false,
    "is_vpn": false,
    "is_relay": false,
    "is_anonymous": false,
    "risk_score": 40,
    "risk_level": "medium",
    "risk_signals": [
      { "signal": "datacenter", "weight": 40, "dataset": "datacenter-ranges" }
    ]
  },
  "meta": { "credits_used": 1, "credits_remaining": 9, "cache_hit": false }
}
```

A repeat of the same address inside seven days is free, so a visitor who reloads the form does not cost a second credit. If your policy forbids keeping even that marker, add `?privacy=1` and every call is a fresh, billable lookup with no trace kept.

For a form that should react before the submit, the [browser endpoint](/docs/api/lookup-ip-public) runs the same lookup from the page with a publishable key locked to your domains. Leave the `ip` field out and the visitor's own address is looked up. Give the key a daily credit cap, because it sits in page source; once the cap is spent the endpoint answers `429 KEY_SPEND_CAP_REACHED` until the next day, and free answers never count toward it.

## Batch the backlog

Existing accounts deserve the same look. Export the registration addresses of the last quarter and send them to `POST /api/v1/ip/batch`, up to 1,000 per call, or queue up to 100,000 as a bulk run and download the CSV. The `hosting_provider` column groups cleanly in a spreadsheet: a spike of accounts from one cloud, created minutes apart, with sequential usernames, is a pattern you can act on without any scoring at all. Bulk runs are billed per fresh answer, and repeats inside the run are looked up once.

## The misconfiguration that makes everyone Cloudflare

If your application sits behind Cloudflare, or any reverse proxy, and you pass the connecting address to the lookup, every visitor comes back as `hosting_provider: "Cloudflare"` with `is_datacenter: true`. Nothing is wrong with the data. The address you sent really is Cloudflare's, because the proxy connected to you on the visitor's behalf.

Cloudflare puts the visitor's address in the `CF-Connecting-IP` header and appends it to `X-Forwarded-For` (Cloudflare's header reference, checked 2026-09-04: https://developers.cloudflare.com/fundamentals/reference/http-headers/). Your framework needs to be told which proxies to trust before it reads those headers, otherwise a visitor could forge them. In Laravel that is the trusted-proxies middleware; in Django, `SECURE_PROXY_SSL_HEADER` and a middleware that reads the forwarded address; in Express, `app.set('trust proxy', …)` with the proxy's ranges. Trust exactly the proxy's published ranges, never every address, and read only the headers the proxy sets itself.

The tell in your data is unmistakable: a steady stream of Cloudflare or Fastly addresses at signup, one per visitor, spread across the day. Real traffic from a CDN edge does not look like that. Fix the proxy configuration first, then re-run the batch; the datacenter flags that remain are the ones worth reading.

## What the flag cannot tell you

Open range lists know where clouds and hosting providers are. They do not know whether a specific address at a residential ISP is being rented out as a proxy. A residential proxy looks, by construction, like a person on a home line: `asn_type: "isp"`, `is_datacenter: false`, a plausible city. No open dataset can tell the two apart, and this API does not pretend to. If residential proxies are your problem, treat the datacenter flag as the first filter, not the last, and look at behaviour on your side: velocity per address, reuse of the same address across accounts, mismatches between the located country and the billing country.

Sources for the range data are named in every response. AWS documents its range file at https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html, Google Cloud at https://cloud.google.com/compute/docs/faq#find_ip_range, and RFC 8805 (https://www.rfc-editor.org/rfc/rfc8805) defines the geofeed format DigitalOcean, Linode and Vultr publish. All were checked on 2026-09-04.

## What to do next

- Read the [IP intelligence guide](/docs/ip-intelligence) for every field, the risk arithmetic, and the privacy rules.
- Wire the [browser endpoint](/docs/api/lookup-ip-public) into the signup page so the policy runs before the submit.
- Decide how to treat VPN and relay users with [a VPN and relay policy that does not punish real people](/guides/a-vpn-and-relay-policy-that-does-not-punish-real-people).
- Try the flags on any address, without an account, on the [IP intelligence product page](/products/ip-intelligence).

Reference: https://spaw.co/guides/block-datacenter-and-bot-signups-with-an-ip-lookup
