# IP check

`POST /v1/ip/check` · **1 credit**

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `ip` | string | yes | An IPv4 or IPv6 address. |

```bash [curl]
curl https://veqen-app.com/v1/ip/check \
  -H "Authorization: Bearer $VEQEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip": "185.220.101.1"}'
```

```python [Python]
r = requests.post(
    "https://veqen-app.com/v1/ip/check",
    headers={"Authorization": f"Bearer {VEQEN_API_KEY}"},
    json={"ip": visitor_ip},
    timeout=10,
)
```

```javascript [JavaScript]
const r = await fetch("https://veqen-app.com/v1/ip/check", {
  method: "POST",
  headers: { Authorization: `Bearer ${VEQEN_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ ip: visitorIp }),
});
```

`GET /v1/ip/check?ip=...` works too. Leave out `ip` on the GET form to check the address the request came from.

## Which IP to send

Send the **visitor's** IP as your server sees it, never a value the browser reports.

- No proxy in front of your app: use the connection's remote address.
- Behind a load balancer or CDN: use the left-most address in `X-Forwarded-For` **that your own proxy added**. Cloudflare also gives you `CF-Connecting-IP`.

## Response

```json
{
  "ip": "185.220.101.1",
  "valid": true,
  "version": 4,
  "type": "public",
  "location": {
    "country_code": "DE",
    "country": "Germany",
    "in_eu": true,
    "region": "Hamburg",
    "city": "Hamburg (Hamburg-Nord)",
    "continent": "EU",
    "latitude": 53.5735,
    "longitude": 10.0548
  },
  "network": {
    "asn": 60729,
    "organization": "Stiftung Erneuerbare Freiheit",
    "kind": "isp",
    "kind_label": "Internet service provider",
    "known_network": null
  },
  "flags": { "hosting": false, "vpn_likely": false, "relay": false, "tor": true },
  "risk": {
    "score": 85,
    "level": "high",
    "verdict": "refuse",
    "reasons": [
      { "code": "tor_exit_node", "impact": 85, "message": "This address is a Tor exit node." }
    ]
  }
}
```

### Fields

| Field | What it tells you |
|---|---|
| `type` | `public` for normal internet addresses. `private`, `loopback` and others mean you're sending an internal address, probably your own proxy's. |
| `location` | Country, region and city. Country is reliable; city is a best guess. |
| `network.asn` / `organization` | Who owns the network the address belongs to. |
| `network.kind` | `isp` (home, business or mobile provider), `cloud`, `hosting`, `vpn_hosting` (hosting popular with commercial VPNs), `cdn` (CDN or privacy relay) or `unknown`. |
| `flags.hosting` | The address is a server, not a person's connection. |
| `flags.vpn_likely` | The network is mostly used by VPN services. |
| `flags.relay` | Traffic comes through a CDN or privacy relay such as Cloudflare WARP. Low risk on its own. |
| `flags.tor` | The address is a current Tor exit node (list refreshed hourly). |

## Frequently asked questions

### Should I block datacenter IPs?

Not outright. Real people use VPNs. Treat it as one signal: a datacenter IP **plus** a throwaway email is a bot pattern; a datacenter IP with a company email is usually a person on a VPN. The [signup check](/docs/signup-check) weighs these together for you.

### Where does the location data come from?

From DB-IP's free Lite databases (licensed CC BY 4.0), refreshed monthly. Tor exits come from the Tor Project and refresh hourly.
