[Guide] Adding Recaptcha V3 to Frappe Web Page Forms

Quick draft for someone who might find this useful


I am submitting my form without reloading the page so cannot use the submit event. Instead, my form has a Save button

<div id='captcha_container'></div>

<form method='post'>
    <input placeholder="First Name" type="text" id="first_name" name="first_name" autocomplete="given-name">
    <button id='save'>Save</button>


1. Add to Website Script doctype


2. Web Page Script

// add save button attributes which wouldn't work when using the page builder 
// but work when using pure HTML

$(document).ready( ()=>{
        'class': 'g-recaptcha btn',
        'data-callback': 'onClick',
        'data-action': 'click'

function onClick(token) {
    // all mandatory
    if(! args.first_name) {
       msgprint("All fields are necessary", "Hang on buddy...");
    //verify captcha and perform your desired action
        "method": "my_app.api.verify_captcha",
        "args": {
            'token': token
        freeze: true,
        callback: (r) => {
            let verified = r.message; //result is score 0.1 - 1 or false
            if (verified){
                if (parseFloat(verified) > 0.3){
                    // finally, perform your desired action
                    // e.g submit your form with frappe.call() or similar
                else {
                    msgprint("Looks like you're a robot 🤖", "Wait a minute...");
            else {
                msgprint("Something's wrong with robot checker 🤖, the developers will be notified", "Uh oh...");


"""file location: <frappe-bench>/apps/my_app/my_app/api.py"""

from urllib.parse import urlencode
from urllib.request import urlopen
import json

def verify_captcha(token):
    URL = 'https://www.google.com/recaptcha/api/siteverify'
    private_key = 'RECAPTCHA_SERVER_KEY'
    params = urlencode({
        'secret': private_key,
        'response': token,

    data = urlopen(URL, params.encode('utf-8')).read()
    result = json.loads(data)
    success = result.get('success', None)

    # frappe.errprint( float(result.get('score', 1)) )

    if success == True:
        # return the score if verification was successful
        return float( result.get('score', 1) ) 
        frappe.errprint( 'reCaptcha failed ' + str(result) )
        return False

Possible improvements

  • Keys can be saved in a frappe doctype
  • I’m running two calls to the server, first to verify the token, and a second to submit my form. One call to the server can handle both

NOTE: This is working perfectly but still not giving me data on the google analytics UI, its a known issue but I’ve given up trying

UPDATE: Report data are streaming in after a few days. The first two charts at least.