# `⛅` Cloudstile
An unofficial Cloudflare Turnstile library with both asynchronous and synchronous support out of the box.
[](https://codecov.io/github/NotAussie/cloudstile) [](https://github.com/NotAussie/cloudstile/actions/workflows/tests.yml) [](https://github.com/NotAussie/cloudstile/actions/workflows/pylint.yml) [](https://github.com/NotAussie/cloudstile/actions/workflows/mypy.yml)
## `📥` Installation
**Cloudstile** is available for download via PyPI. To install it, simply do:
```shell
pip install cloudstile
```
## `🎭` Example
Here are some basic examples of how to validate a user's turnstile token.
> [!WARNING]
> These examples expect the user's IP to be transparent. If you're using something like Cloudflare's proxy service, then you'll need to access the corresponding header for your use case.
### `🍷` Quart *(Asynchronous)*
```python
from quart import Quart, request, jsonify
from cloudstile import AsyncTurnstile
app = Quart(__name__)
turnstile = AsyncTurnstile(token="...")
@app.route("/submit", methods=["POST"])
async def submit():
body = await request.form
response = await turnstile.validate(
body.get("cf-turnstile-response", "..."),
request.remote_addr,
)
return jsonify(response.model_dump()) # <- Response is a pydantic object
```
### `🏃♀️` FastAPI *(Asynchronous)*
```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from cloudstile import AsyncTurnstile
app = FastAPI()
turnstile = AsyncTurnstile(token="...")
@app.post("/submit")
async def submit(request: Request):
body = await request.form()
response = await turnstile.validate(
body.get("cf-turnstile-response", "..."),
request.client.host,
)
return JSONResponse(response.model_dump()) # <- Response is a pydantic object
```
### `🦥` Flask *(Synchronous)*
```python
from flask import Flask, request, jsonify
from cloudstile import SyncTurnstile
app = Flask(__name__)
turnstile = SyncTurnstile(token="...")
@app.route("/submit", methods=["POST"])
def submit():
body = request.form
response = turnstile.validate(
body.get("cf-turnstile-response", "..."),
request.remote_addr,
)
return jsonify(response.model_dump()) # <- Response is a pydantic object
```
Raw data
{
"_id": null,
"home_page": "https://github.com/notaussie/cloudstile",
"name": "cloudstile",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "cloudflare, turnstile, captcha",
"author": "NotAussie",
"author_email": "notaussie@duck.com",
"download_url": "https://files.pythonhosted.org/packages/82/1b/c7c93a5923b827e4ce9b2df0066afeae56513b8e3391587c7b7398fd3659/cloudstile-2.0.1.tar.gz",
"platform": null,
"description": "# `\u26c5` Cloudstile\nAn unofficial Cloudflare Turnstile library with both asynchronous and synchronous support out of the box.\n\n[](https://codecov.io/github/NotAussie/cloudstile) [](https://github.com/NotAussie/cloudstile/actions/workflows/tests.yml) [](https://github.com/NotAussie/cloudstile/actions/workflows/pylint.yml) [](https://github.com/NotAussie/cloudstile/actions/workflows/mypy.yml)\n\n## `\ud83d\udce5` Installation\n**Cloudstile** is available for download via PyPI. To install it, simply do:\n```shell\npip install cloudstile\n```\n\n## `\ud83c\udfad` Example\n\nHere are some basic examples of how to validate a user's turnstile token.\n\n> [!WARNING]\n> These examples expect the user's IP to be transparent. If you're using something like Cloudflare's proxy service, then you'll need to access the corresponding header for your use case.\n\n### `\ud83c\udf77` Quart *(Asynchronous)*\n\n```python\nfrom quart import Quart, request, jsonify\nfrom cloudstile import AsyncTurnstile\n\napp = Quart(__name__)\nturnstile = AsyncTurnstile(token=\"...\")\n\n@app.route(\"/submit\", methods=[\"POST\"])\nasync def submit():\n\n body = await request.form\n\n response = await turnstile.validate(\n body.get(\"cf-turnstile-response\", \"...\"),\n request.remote_addr,\n )\n\n return jsonify(response.model_dump()) # <- Response is a pydantic object\n\n```\n\n### `\ud83c\udfc3\u200d\u2640\ufe0f` FastAPI *(Asynchronous)*\n\n```python\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import JSONResponse\nfrom cloudstile import AsyncTurnstile\n\napp = FastAPI()\nturnstile = AsyncTurnstile(token=\"...\")\n\n@app.post(\"/submit\")\nasync def submit(request: Request):\n\n body = await request.form()\n\n response = await turnstile.validate(\n body.get(\"cf-turnstile-response\", \"...\"),\n request.client.host,\n )\n\n return JSONResponse(response.model_dump()) # <- Response is a pydantic object\n\n```\n\n\n### `\ud83e\udda5` Flask *(Synchronous)*\n\n```python\nfrom flask import Flask, request, jsonify\nfrom cloudstile import SyncTurnstile\n\napp = Flask(__name__)\nturnstile = SyncTurnstile(token=\"...\")\n\n@app.route(\"/submit\", methods=[\"POST\"])\ndef submit():\n\n body = request.form\n\n response = turnstile.validate(\n body.get(\"cf-turnstile-response\", \"...\"),\n request.remote_addr,\n )\n\n return jsonify(response.model_dump()) # <- Response is a pydantic object\n\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Unofficial Cloudflare Turnstile library for Python with async and sync support.",
"version": "2.0.1",
"project_urls": {
"Homepage": "https://github.com/notaussie/cloudstile",
"Repository": "https://github.com/notaussie/cloudstile"
},
"split_keywords": [
"cloudflare",
" turnstile",
" captcha"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1f57a38c2b3604088bff2b824c049e7948b199e2e97b6d3ed461cd8baaf8cad9",
"md5": "c29cd096ce3f0fa576384c794b6d32d7",
"sha256": "919a270da2f92a5128347f294c185b0e9a8b93e85f0d1b5b1dc3e7047a01bdda"
},
"downloads": -1,
"filename": "cloudstile-2.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c29cd096ce3f0fa576384c794b6d32d7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 7884,
"upload_time": "2025-07-26T07:44:20",
"upload_time_iso_8601": "2025-07-26T07:44:20.417459Z",
"url": "https://files.pythonhosted.org/packages/1f/57/a38c2b3604088bff2b824c049e7948b199e2e97b6d3ed461cd8baaf8cad9/cloudstile-2.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "821bc7c93a5923b827e4ce9b2df0066afeae56513b8e3391587c7b7398fd3659",
"md5": "def13726a975046f8437e5dc6670dc93",
"sha256": "7ebe6d1caf7e3a840301f8fe06d3f1a888cf05d96c043522f008b02771397805"
},
"downloads": -1,
"filename": "cloudstile-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "def13726a975046f8437e5dc6670dc93",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 4654,
"upload_time": "2025-07-26T07:44:21",
"upload_time_iso_8601": "2025-07-26T07:44:21.665969Z",
"url": "https://files.pythonhosted.org/packages/82/1b/c7c93a5923b827e4ce9b2df0066afeae56513b8e3391587c7b7398fd3659/cloudstile-2.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-26 07:44:21",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "notaussie",
"github_project": "cloudstile",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "cloudstile"
}