Mittwoch, 30. April 2008

Using reCAPTCHA with Google App Engine

We have no image creation capabilities in Google App Engine (yet). So if you want to display a CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) within a GAE application, you need to do this by using an external service provider.

One of these providers is reCAPTCHA - and the nice thing about reCAPTCHA is, that your visitors are helping to digitize books by solving the CAPTCHA.

So how do we start?
At first, you should get a service key on
After you registered, you can create an unlimited number of service keys for your domain.
Note: If you want to use reCAPTCHA with your dev environment, you need to get keys for localhost.
After generating one pair of keys, the screen should look like this:

Write down your public and private key - we will need them later. If you forget them, you can return to to find them out. Don't tell anyone your private key.

After having registered for the service, you should get a simple service class I adapted from to GAE. You can download the adapted class here. Rename it to and copy it somewhere (I prefer putting it in a subdirectory named recaptcha/client/ - remember to add an empty in every directory if you do this) within your App Engine project.

For demonstration, I will be extending the helloworld example from GAE.

Go to your and import the captcha and environ modules:
from os import environ
from recaptcha.client import captcha
Then go to your MainPage controller and add the following lines:
chtml = captcha.displayhtml(
public_key = "YOUR-PUBLIC-KEY",
use_ssl = False,
error = None)

template_values = {
'captchahtml': chtml
Exchange YOUR-PUBLIC-KEY with your public key - if you don't, you'll get a message:
"Invalid public key. Make sure you copy and pasted it correctly."

Now switch to your HTML template and add the captchahtml output within your form tags:
{{ captchahtml }}

When you open the site in your browser, you'll see the recaptcha iframe:

Now when the form gets submitted, we need to check if the captcha input was correct. Therefore, we need to change into the post method of our Guestbook object and add some code:

def post(self):
challenge = self.request.get('recaptcha_challenge_field')
response = self.request.get('recaptcha_response_field')
remoteip = environ['REMOTE_ADDR']

cResponse = captcha.submit(

if cResponse.is_valid:
# response was valid
# other stuff goes here
error = cResponse.error_code

Exchange YOUR-PRIVATE-KEY with your previously generated private key.

The user inputs to the reCAPTCHA iframe will be validated together with the remote IP (your visitor's IP) and the challenge. We get a response from the reCAPTCHA API server and a RecaptchaResponse object will hold the answer.

The RecaptchaResponse object has two properties:

  • is_valid is set to True if the test was successful (otherwise it'l bee False)
  • error_code will hold an API error code if there was a problem.

Note: In the Getting Started Guide for GAE, the form gets submitted to the Guestbook controller. Normally you would submit to the MainPage controller and pass the error code from the RecaptchaResponse object (if there is one) to the displayhtml method of the captcha class:

chtml = captcha.displayhtml(
public_key = "YOUR-PUBLIC-KEY",
use_ssl = False,
error = cResponse.error_code)

That will display a human readable error message to the user:

and lets your visitor redoing the CAPTCHA without losing his/her previously entered values in the other form fields.

Keep in mind: For every submitted CAPTCHA, a request to the reCAPTCHA server is made. This request is synchronous, so the response to your visitor will get delayed by the time it takes to fetch the response. If the reCAPTCHA server can not be reached, the error code recaptcha-not-reachable will be returned.


  1. You are awesome! Thank you so much for sharing this -- I could get it running in no time.

    The app I'm building is

    What are you working on?

    Let me know whenever you're in Berlin. It would be awesome to meet a fellow GAE & Django enthusiast.

  2. the "environ['REMOTE_ADDR']" line didn't work for me.

    It can be replaced by:

    remoteip = self.request.remote_addr

  3. Thanks so much for posting this. It was very helpful to use as a starting point. I went ahead and added in the template color which was pretty easy.

    One note -- your post mentions that you need a separate key for localhost. The recaptcha docs say otherwise, which makes it easy to push things into production: says "If one of your servers is "localhost" or "", reCAPTCHA will not enforce the same-domain rule. Just use the same key as for the production server."

    The other thing I have found is that the human readable error message is not showing up for me. Oh well.

  4. Thanks for this -- going in a project now. -- Matthew Smith

  5. @Matthew: great, let me know the URL when it is live!

  6. Hey, thanks a lot! Simple & Clear!

  7. excellent - thanks!!

  8. Thanks for sharing! Your instructions were clear and made it very easy for me to install it in my site, I've been attacked by spammers lately (1000 fake comments this morning!) so I really appreciate this post.

    Ernesto -

  9. hello, i do this as you wrote (sample guestbook & your instructions) but not work, cResponse.error_code always eq "incorrect-captcha-sol" :(
    if you have a free time please help me, code here
    Thanks you.

  10. Thanks for this it is very useful!

  11. With you help I added recaptcha to my app in 5 mins, thanks.

  12. how about reCAPTCHA in google app engine java? please help me

  13. Thank you for sharing this!!!!!

  14. After adding 'from recaptcha.client import captcha' I have error 'ImportError: No module named recaptcha.client'. What should I do?

  15. great tutorial, got it working in a few mins :-) thanks

  16. Hello, good tutorial but the link to the file is broken :(.

  17. Hello, Im had a problem using https, so I changed the url for becouse of a certificate error.

  18. Awesome, thanks for your solution , it really works!

  19. Thank a lot for your solution. Was looking all over the net for it. Found the official plugin but there was no documentation accompanying it :( . Was good to go with your solution. Currently working on a hobby topic at given url. Will be releasing it soon. You are welcome :)