Turing API

Flask bot detection and email validation

A decorator you apply to the handful of routes worth protecting, rather than a global before_request that bills you for favicons.

1. Install

pip install requests flask

2. Add the file

turing.py

import functools
import logging
import os

import requests
from flask import jsonify, request

log = logging.getLogger(__name__)

TURING = "https://uat.turing-api.com"
API_KEY = os.environ.get("TURING_API_KEY", "")


def client_ip():
    forwarded = request.headers.get("X-Forwarded-For", "")
    if forwarded:
        return forwarded.split(",")[0].strip()
    return request.remote_addr or ""


def block_bots(view):
    @functools.wraps(view)
    def wrapper(*args, **kwargs):
        ip = client_ip()
        if not (ip and API_KEY):
            return view(*args, **kwargs)

        try:
            response = requests.post(
                TURING + "/v1/bot-check",
                headers={"Authorization": "Bearer " + API_KEY},
                json={"ip": ip, "user_agent": request.headers.get("User-Agent", "")},
                timeout=3,
            )
            response.raise_for_status()
            verdict = response.json()
        except (requests.RequestException, ValueError) as error:
            log.warning("turing unreachable: %s", error)
            return view(*args, **kwargs)  # fail open

        if verdict.get("is_bot") is True:
            return jsonify(
                error="Automated traffic is not permitted.",
                reasons=verdict.get("reasons", []),
            ), 403

        return view(*args, **kwargs)

    return wrapper

3. Wire it up

from flask import Flask
from turing import block_bots

app = Flask(__name__)

@app.post("/signup")
@block_bots
def signup():
    ...

4. Verify

Confirm the API answers before you debug your Flask wiring. The sample address sits inside a published AWS range, so a correct setup returns is_bot: true.

Notes

Other frameworks