Skip to content

Flask CAPTCHA Integration

This recipe shows how to integrate TrustCaptcha into a Flask application. The frontend setup is the same as for any other Python application — this page focuses on the server-side validation.

The setup section gets you to a working integration in three small steps using a route function directly. Below it, an optional refactor section shows the more reusable Flask-idiomatic approach (a custom decorator).

You should have already completed the following steps before you wire TrustCaptcha into your Flask application.

  1. Read Get-Started: Get a quick overview of the concepts behind TrustCaptcha and the integration process in get started.

  2. Existing CAPTCHA: If you don’t have a CAPTCHA yet, sign in or create a new user account. Then create a new CAPTCHA.


First, add the TrustCaptcha script to your page (see the JavaScript Guide for version pinning and self-hosting options).

Then place the <trustcaptcha-component> element inside your Jinja template form. The widget appends a hidden tc-verification-token field on submit, which your Flask view receives in request.form like any other form input.

templates/contact.html
<script type="module" src="https://cdn.trustcomponent.com/trustcaptcha/3.0.x/trustcaptcha.esm.min.js"></script>
<form method="post" action="{{ url_for('submit') }}">
<label>Email</label>
<input type="email" name="email" required>
<trustcaptcha-component sitekey="<your_site_key>"></trustcaptcha-component>
<button type="submit">Send</button>
</form>

See the Widget Overview for the full property reference.

Terminal window
pip install "trustcaptcha>=3.0.0,<4.0.0"
app.py
from flask import Flask, render_template, request, redirect, url_for
from trustcaptcha.trust_captcha import TrustCaptcha
app = Flask(__name__)
@app.post("/contact")
def submit():
# In production, load from env: os.environ["TRUSTCAPTCHA_API_KEY"]
api_key = "<your_api_key>"
token = request.form.get("tc-verification-token", "")
try:
trust_captcha = TrustCaptcha(api_key)
result = trust_captcha.get_verification_result(token)
except Exception:
return render_template("contact.html", error="CAPTCHA verification failed."), 400
if not result.verification_passed or result.score > 0.5:
return render_template("contact.html", error="CAPTCHA verification failed."), 400
# CAPTCHA passed — request data is safe to use.
# ... your business logic ...
return redirect(url_for("contact_success"))

That’s it — the form is now protected. For real deployments, move the API key out of the source code (see the comment) and consider explicit failover handling — see Failover Behavior for the reasoning and a code template.


If you protect more than one route, the most idiomatic Flask approach is a custom decorator. The verification call then becomes a single @verify_trust_captcha line on every protected route.

Build the SDK once at startup and store it on the Flask app — current_app makes it reachable from any view or decorator without import gymnastics:

app.py
import os
from flask import Flask
from trustcaptcha.trust_captcha import TrustCaptcha
app = Flask(__name__)
app.extensions["trust_captcha"] = TrustCaptcha(os.environ["TRUSTCAPTCHA_API_KEY"])
captcha.py
from functools import wraps
from flask import abort, current_app, request
def verify_trust_captcha(view):
@wraps(view)
def wrapper(*args, **kwargs):
trust_captcha = current_app.extensions["trust_captcha"]
token = request.form.get("tc-verification-token", "")
try:
result = trust_captcha.get_verification_result(token)
except Exception:
abort(400, description="CAPTCHA verification failed.")
if not result.verification_passed or result.score > 0.5:
abort(400, description="CAPTCHA verification failed.")
return view(*args, **kwargs)
return wrapper
app.py
from captcha import verify_trust_captcha
@app.post("/contact")
@verify_trust_captcha
def submit():
# CAPTCHA already verified — request data is safe to use.
return redirect(url_for("contact_success"))

Adding @verify_trust_captcha to any view now opts the route into CAPTCHA verification.


Generic error response. The Stage 1 controller re-renders contact.html on failure. The Stage 2 decorator uses abort(400) so it works for any route — register an error handler if you want to render a specific page for 400 responses.

Singleton SDK instance. Building TrustCaptcha once at startup (as shown in Stage 2) gives better connection reuse than building it inside every view. For configured usage (custom timeouts, proxy, custom API host), pass them to the constructor: TrustCaptcha(api_key, api_host=..., proxy=...). See the Python Guide for the full constructor options.


Once you have wired TrustCaptcha into your Flask application, you can use TrustCaptcha to its full extent. However, we still recommend the following additional technical and organizational measures:

  • Security rules: You can find many security settings for your CAPTCHA in the CAPTCHA settings. These include, for example, authorized websites, CAPTCHA bypass for specific IP addresses, bypass keys, IP based blocking, geoblocking, individual difficulty and duration of the CAPTCHA, and much more. Learn more about the security rules.

  • Privacy & GDPR compliance: Include a passage in your privacy policy that refers to the use of TrustCaptcha. We also recommend that you enter into a data processing agreement with us to stay GDPR-compliant. Learn more about data protection.

  • Accessibility & UX: Customize TrustCaptcha to your website so that your website is as accessible as possible and offers the best possible user experience. More about accessibility.

  • Failover behavior: Decide how your backend should behave when our service is temporarily unreachable. This is particularly important for high-availability flows where blocking real users during an outage is worse than letting through a small amount of unverified traffic. Learn more about failover behavior.

  • Testing: If you use automated testing, make sure that the CAPTCHA does not block it. Learn more about testing.