. Or use the REST API directly with POST /v1/verify."}},{"@type":"Question","name":"Is there a free tier?","acceptedAnswer":{"@type":"Answer","text":"Yes. Get an API key instantly with no signup required. The free tier includes 1,000 verifications per day (30,000/month). No credit card needed."}},{"@type":"Question","name":"What signals does Device.AI analyze?","acceptedAnswer":{"@type":"Answer","text":"Device.AI analyzes user agent, automation framework presence (Selenium, Puppeteer, PhantomJS, etc.), canvas fingerprint, WebGL renderer, screen resolution, timezone, hardware concurrency, browser feature support, and connection type."}}]}]}
Device.AI/Documentation

API Documentation

Device.AI provides real-time device intelligence and bot detection through a simple REST API. Get your API key instantly — no signup required — and start detecting bots in under 60 seconds.

v1Base URL: https://device.ai

#Quickstart

Three steps to protect your site from bots. No account creation, no email, no credit card.

1. Get your API key

Terminalbash
curl -X POST https://device.ai/v1/keys \
  -H "Content-Type: application/json"

# Response:
# {
#   "key": "dv_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
#   "rateLimit": 1000,
#   "period": "daily"
# }

2. Verify a visitor

Terminalbash
curl -X POST https://device.ai/v1/verify \
  -H "Content-Type: application/json" \
  -d '{
    "key": "dv_live_YOUR_KEY",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
    "ip": "203.0.113.42"
  }'

# Response:
# {
#   "score": 0.92,
#   "bot": false,
#   "risk": "low",
#   "keyInfo": {
#     "usage": 1,
#     "limit": 1000,
#     "period": "daily"
#   }
# }

3. Block or allow based on score

server.jsjavascript
// In your API route or middleware
const response = await fetch('https://device.ai/v1/verify', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    key: process.env.DEVICE_AI_KEY,
    userAgent: req.headers['user-agent'],
    ip: req.ip
  })
});

const { score, bot, risk } = await response.json();

if (bot || score < 0.3) {
  return res.status(403).json({ error: 'bot_detected' });
}

// Continue with normal request handling...

#Authentication

All API requests are authenticated using your API key, passed in the request body. Keys are prefixed with dv_live_ for production use.

Important: Keep your API key secure. Do not expose it in client-side code. Always call the Device.AI API from your backend server.

#Generate API Key

POST/v1/keys

Generate a new API key instantly. No authentication required. Each key includes 1,000 free verifications per day.

Request

No request body required. Just send an empty POST.

Examplebash
curl -X POST https://device.ai/v1/keys \
  -H "Content-Type: application/json"

Response

201 Createdjson
{
  "key": "dv_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
  "rateLimit": 1000,
  "period": "daily",
  "created": "2026-04-01T10:00:00.000Z",
  "id": 42
}

Response Fields

keystring

Your API key. Store this securely — it cannot be retrieved again.

rateLimitinteger

Maximum verifications allowed per period (default: 1,000).

periodstring

Rate limit window — currently "daily" (resets at midnight UTC).

createdISO 8601

Timestamp when the key was created.

idinteger

Internal key identifier.

#Verify Endpoint

POST/v1/verify

Score a visitor's device for bot likelihood. Returns a trust score from 0.0 (definitely a bot) to 1.0 (definitely human).

Request Body

keystringrequired

Your Device.AI API key.

userAgentstring

The visitor's User-Agent header. Strongly recommended for accurate scoring.

ipstring

The visitor's IP address. Used for reputation lookup and geo-analysis.

Example Requestbash
curl -X POST https://device.ai/v1/verify \
  -H "Content-Type: application/json" \
  -d '{
    "key": "dv_live_YOUR_KEY",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
    "ip": "203.0.113.42"
  }'

Response

200 OKjson
{
  "score": 0.92,
  "bot": false,
  "risk": "low",
  "keyInfo": {
    "usage": 47,
    "limit": 1000,
    "period": "daily"
  }
}

Response Fields

scorefloat

Trust score from 0.0 to 1.0. Higher = more likely human.

botboolean

Whether the visitor is classified as a bot (score < 0.3).

riskstring

Risk level: "low" (≥0.7), "medium" (0.3–0.7), or "high" (<0.3).

keyInfo.usageinteger

Number of verifications used in the current period.

keyInfo.limitinteger

Maximum verifications allowed per period.

keyInfo.periodstring

Current rate limit period.

#Shield (WAF Middleware)

POST/v1/shieldNEW

Lightweight server-side bot blocking for middleware and edge functions. Unlike /v1/verifywhich uses client-side fingerprinting for deep scoring, Shield is optimized for server-side-only environments where you need fast (<50ms) allow/block decisions based on IP, User-Agent, geo, and referrer signals.

When to use Shield vs Verify: Use /v1/shield in server-side middleware (Next.js middleware, Express, Cloudflare Workers) where you only have request headers. Use /v1/verify when you also have client-side signals from detect.js.

Request Body

keystringrequired

Your Device.AI API key.

ipstring

The visitor's IP address from x-forwarded-for or equivalent.

userAgentstring

The visitor's User-Agent header.

geoobject

Geo information: { country: "US", region: "NY", city: "New York" }

urlstring

The request path being accessed (for path-based rules).

referrerstring

The request referrer (for spam referrer blocking).

rulesobject

Optional per-request rules — see Rules section below.

Rules Object

blockCountriesstring[]

ISO country codes to block outright, e.g. ["NG", "CN"]

challengeCountriesstring[]

ISO codes to score more aggressively (raises threat score).

blockPathsstring[]

URL regex patterns to apply extra protection on, e.g. ["/admin.*", "/api/.*"]

allowBotsboolean

Allow known good bots like Googlebot. Default: true.

strictModeboolean

Lower blocking threshold for sensitive routes. Default: false.

Example Requestbash
curl -X POST https://device.ai/v1/shield \
  -H "Content-Type: application/json" \
  -d '{
    "key": "dv_live_YOUR_KEY",
    "ip": "203.0.113.42",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
    "geo": { "country": "NG" },
    "url": "/api/submit",
    "referrer": "https://syndicatedsearch.goog",
    "rules": {
      "blockCountries": ["NG"],
      "blockPaths": ["/admin.*"],
      "strictMode": true
    }
  }'

Response

200 OKjson
{
  "action": "BLOCK",
  "score": 0.0,
  "reason": "blocked_country:NG, bad_referrer",
  "signals": ["blocked_country:NG", "bad_referrer"],
  "geo": { "country": "NG" },
  "cached": false
}

Response Fields

actionstring

"ALLOW", "BLOCK", or "CHALLENGE". Use this to decide what to do with the request.

scorefloat

Trust score from 0.0 to 1.0 (same scale as /v1/verify).

reasonstring

Human-readable explanation of the decision.

signalsstring[]

Individual signals that contributed to the score.

geoobject

Resolved geo data from the request.

cachedboolean

Whether the result was served from cache.

Signal Types

good_bot

Recognized search engine or social media crawler

bad_ua

User-Agent matches known bot/scraper patterns

no_ua / short_ua

Missing or suspiciously short User-Agent string

blocked_country:XX

Country matched an explicit block rule

challenge_country:XX

Country matched a challenge rule

high_bot_country:XX

Country has high historical bot traffic

bad_referrer

Referrer matches known spam/fake referral source

protected_path:pattern

URL matched a protected path rule

no_ip

No IP address provided (suspicious)

Fail-open by design: If Shield encounters an internal error, it returns action: "ALLOW"to prevent blocking legitimate traffic. Your site's availability always takes priority over bot blocking.

Next.js Middleware Integration

Drop-in middleware that protects your entire site with one file:

middleware.tstypescript
import { NextRequest, NextResponse } from 'next/server';

const DEVICE_AI_KEY = process.env.DEVICE_AI_KEY!;
const SHIELD_URL = 'https://device.ai/v1/shield';

export async function middleware(request: NextRequest) {
  // Skip static assets
  if (request.nextUrl.pathname.startsWith('/_next') ||
      request.nextUrl.pathname.startsWith('/favicon')) {
    return NextResponse.next();
  }

  try {
    const res = await fetch(SHIELD_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        key: DEVICE_AI_KEY,
        ip: request.headers.get('x-forwarded-for')?.split(',')[0]?.trim(),
        userAgent: request.headers.get('user-agent') || '',
        geo: {
          country: request.geo?.country,
          region: request.geo?.region,
          city: request.geo?.city,
        },
        url: request.nextUrl.pathname,
        referrer: request.headers.get('referer') || '',
        rules: {
          blockCountries: ['NG'],       // Block Nigerian bots
          challengeCountries: ['CN'],   // Challenge Chinese traffic
          blockPaths: ['/admin.*', '/api/sensitive.*'],
          strictMode: false,
        },
      }),
    });

    const { action } = await res.json();

    if (action === 'BLOCK') {
      return new NextResponse('Access denied', { status: 403 });
    }
  } catch {
    // Fail open — never block real users due to API errors
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Cloudflare Worker

worker.jsjavascript
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // Skip static assets
    if (url.pathname.match(/\.(js|css|png|jpg|svg|ico)$/)) {
      return fetch(request);
    }

    try {
      const shield = await fetch('https://device.ai/v1/shield', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          key: env.DEVICE_AI_KEY,
          ip: request.headers.get('cf-connecting-ip'),
          userAgent: request.headers.get('user-agent') || '',
          geo: {
            country: request.cf?.country,
            region: request.cf?.region,
          },
          url: url.pathname,
          referrer: request.headers.get('referer') || '',
        }),
      });

      const { action, reason } = await shield.json();

      if (action === 'BLOCK') {
        return new Response('Blocked by Device.AI Shield', {
          status: 403,
          headers: { 'X-Shield-Reason': reason },
        });
      }
    } catch {
      // Fail open
    }

    return fetch(request);
  },
};

Express.js Middleware

middleware/shield.jsjavascript
const DEVICE_AI_KEY = process.env.DEVICE_AI_KEY;

async function shieldMiddleware(req, res, next) {
  try {
    const response = await fetch('https://device.ai/v1/shield', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        key: DEVICE_AI_KEY,
        ip: req.ip || req.headers['x-forwarded-for'],
        userAgent: req.headers['user-agent'],
        url: req.path,
        referrer: req.headers.referer || '',
      }),
    });

    const { action } = await response.json();

    if (action === 'BLOCK') {
      return res.status(403).json({ error: 'blocked_by_shield' });
    }

    next();
  } catch {
    next(); // Fail open
  }
}

// Protect all routes:
// app.use(shieldMiddleware);

// Protect specific routes:
// app.use('/api', shieldMiddleware);
// app.use('/admin', shieldMiddleware);
module.exports = shieldMiddleware;

#Scoring Model

Device.AI analyzes multiple device signals to generate a trust score. The current model evaluates:

User-Agent Analysis

Known bot patterns, browser consistency, header anomalies

Good Bot Allowlist

Googlebot, Bingbot, GPTBot, and 20+ verified crawlers are auto-allowed

UA Length & Structure

Suspiciously short or malformed user agents are penalized

Browser Fingerprint

Missing standard browser identifiers lower the score

Score Ranges

0.7 – 1.0Low RiskHuman visitor or verified good bot
0.3 – 0.7Medium RiskSuspicious — may need additional verification
0.0 – 0.3High RiskLikely a bot — recommend blocking

#Rate Limits

Every API key includes a daily verification quota. When you exceed it, requests return a 429 status with an upgrade link.

Instant (Free)No signup required
1,000/day
Pro ($19/mo)~3,300/day
100,000/mo
Business ($79/mo)~16,600/day
500,000/mo
Scale ($249/mo)~66,600/day
2,000,000/mo

Rate Limit Response

429 Too Many Requestsjson
{
  "error": "Rate limit exceeded",
  "rateLimit": {
    "limit": 1000,
    "used": 1000
  },
  "upgradeUrl": "https://device.ai/upgrade?key=dv_live_YOUR_KEY"
}

#Error Handling

Device.AI uses standard HTTP status codes. Errors return a JSON body with an error field.

200
Success

Verification completed. Check the score.

201
Created

API key generated successfully.

400
Bad Request

Missing required field (e.g., API key).

403
Forbidden

Invalid or deactivated API key.

429
Too Many Requests

Rate limit exceeded. Upgrade or wait for daily reset.

500
Server Error

Something went wrong on our end. Retry with backoff.

#Framework Integrations

Device.AI works with any backend. Here are copy-paste examples for popular frameworks.

Next.js (App Router)

src/middleware.tstypescript
import { NextRequest, NextResponse } from 'next/server';

export async function middleware(request: NextRequest) {
  // Skip static assets and API routes
  if (request.nextUrl.pathname.startsWith('/_next')) return;

  try {
    const res = await fetch('https://device.ai/v1/verify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        key: process.env.DEVICE_AI_KEY,
        userAgent: request.headers.get('user-agent') || '',
        ip: request.headers.get('x-forwarded-for') || request.ip,
      }),
    });

    const { bot, score } = await res.json();

    if (bot) {
      return NextResponse.json(
        { error: 'Access denied' },
        { status: 403 }
      );
    }
  } catch {
    // Fail open — don't block users if Device.AI is unreachable
  }

  return NextResponse.next();
}

Express.js

middleware/botGuard.jsjavascript
const DEVICE_AI_KEY = process.env.DEVICE_AI_KEY;

async function botGuard(req, res, next) {
  try {
    const response = await fetch('https://device.ai/v1/verify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        key: DEVICE_AI_KEY,
        userAgent: req.headers['user-agent'],
        ip: req.ip,
      }),
    });

    const { bot, score } = await response.json();

    if (bot) {
      return res.status(403).json({ error: 'bot_detected' });
    }

    // Attach score to request for downstream use
    req.deviceScore = score;
    next();
  } catch (err) {
    // Fail open
    next();
  }
}

// Usage:
// app.use('/api', botGuard);
module.exports = botGuard;

Python (Flask)

bot_guard.pypython
import os, requests
from functools import wraps
from flask import request, jsonify

DEVICE_AI_KEY = os.environ['DEVICE_AI_KEY']

def bot_guard(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        try:
            resp = requests.post('https://device.ai/v1/verify', json={
                'key': DEVICE_AI_KEY,
                'userAgent': request.headers.get('User-Agent', ''),
                'ip': request.remote_addr,
            }, timeout=2)

            data = resp.json()
            if data.get('bot'):
                return jsonify({'error': 'bot_detected'}), 403

        except Exception:
            pass  # Fail open

        return f(*args, **kwargs)
    return decorated

# Usage:
# @app.route('/submit')
# @bot_guard
# def submit():
#     ...

#SDKs & Libraries

Official SDKs are coming soon. In the meantime, the REST API works with any HTTP client.

🟢

Node.js

Coming soon

🐍

Python

Coming soon

🔵

Go

Planned

🟣

PHP

Planned

Ready to get started?

Get your free API key and start detecting bots in under a minute.

Get Free API Key →