A Python wrapper for the CTFd API — automate and script your CTF platforms with a clean, pythonic interface.
Project description
ctfd.py
Async Python client for the CTFd API (v1).
Installation
uv add ctfd-api
# or
pip install ctfd-api
Requirements
- Python 3.13+
- httpx (installed automatically)
Quick start
import asyncio
from ctfd import CTFdClient
async def main():
async with CTFdClient('https://my-ctf.example.com', token='ctfd_...') as ctfd:
# Get the current user
me = await ctfd.users.me()
print(me.id, me.name)
# List all challenges
challenges = await ctfd.challenges.list()
for ch in challenges:
print(ch.name, ch.value, ch.category)
asyncio.run(main())
Authentication
Pass an API token obtained from Profile → API Access Tokens:
ctfd = CTFdClient('https://my-ctf.example.com', token='ctfd_abc123')
Without a token the client still works for public endpoints (e.g. scoreboard).
Resources
Every swagger tag maps to an attribute on CTFdClient:
| Attribute | Resource |
|---|---|
ctfd.challenges |
Challenges, attempts, sub-resources |
ctfd.users |
Users, /me, solves, fails, awards |
ctfd.teams |
Teams, /me, members, solves, fails, awards |
ctfd.scoreboard |
Full list, top-N |
ctfd.flags |
Flags, types |
ctfd.hints |
Hints |
ctfd.tags |
Tags |
ctfd.topics |
Topics |
ctfd.awards |
Awards |
ctfd.submissions |
Submissions |
ctfd.files |
Files, upload, download |
ctfd.notifications |
Notifications |
ctfd.configs |
Config keys, fields |
ctfd.pages |
Pages |
ctfd.tokens |
API tokens |
ctfd.unlocks |
Unlocks |
ctfd.comments |
Comments |
ctfd.shares |
Shares |
ctfd.brackets |
Brackets |
ctfd.solutions |
Solutions |
ctfd.statistics |
Statistics aggregates |
ctfd.exports |
Export archive |
Pagination
List endpoints return the first page. Use .iter() to walk all pages automatically:
async with CTFdClient('https://my-ctf.example.com', token='ctfd_...') as ctfd:
# All users, page by page
async for user in ctfd.users.iter():
print(user.id, user.name)
# Or collect everything at once
all_submissions = await ctfd.submissions.iter().all()
Common operations
Submit a flag
result = await ctfd.challenges.attempt(challenge_id=42, submission='flag{example}')
print(result['status']) # 'correct' or 'incorrect'
Create a challenge (admin)
from ctfd.models import Challenge
ch = await ctfd.challenges.create({
'name': 'My Challenge',
'description': 'Find the flag.',
'value': 100,
'category': 'web',
'type': 'standard',
'state': 'visible',
})
print(ch.id)
Manage flags (admin)
flag = await ctfd.flags.create({
'challenge_id': ch.id,
'type': 'static',
'content': 'flag{secret}',
})
await ctfd.flags.delete(flag.id)
Upload a file (admin)
with open('attachment.zip', 'rb') as f:
files = await ctfd.files.create({
'files': [('file', ('attachment.zip', f, 'application/zip'))],
'type': 'challenge',
'challenge_id': 42,
})
Export (admin)
# Download entirely in memory
data = await ctfd.exports.raw()
with open('ctfd_backup.zip', 'wb') as f:
f.write(data)
# Or stream to disk
async with CTFdClient(...) as ctfd:
with open('ctfd_backup.zip', 'wb') as f:
async for chunk in ctfd.exports.stream():
f.write(chunk)
Team management (admin)
# Add a user to a team
await ctfd.teams.add_member(team_id=5, user_id=12)
# Remove a member
await ctfd.teams.remove_member(team_id=5, user_id=12)
Config (admin)
# Bulk update
await ctfd.configs.bulk_update({'ctf_name': 'My CTF', 'ctf_description': 'Have fun!'})
# Single key
cfg = await ctfd.configs.get('ctf_name')
print(cfg.value)
Error handling
from ctfd import (
CTFdAuthenticationError,
CTFdNotFoundError,
CTFdPermissionError,
CTFdRateLimitError,
CTFdValidationError,
)
try:
ch = await ctfd.challenges.get(9999)
except CTFdNotFoundError:
print('challenge not found')
except CTFdAuthenticationError:
print('invalid or missing token')
except CTFdPermissionError:
print('admin rights required')
except CTFdValidationError as e:
print('bad request:', e.errors)
except CTFdRateLimitError:
print('rate limited, slow down')
Dev setup
uv sync # install deps + dev tools
uv run pre-commit install # install git hooks
uv run pytest # run tests
uv run pytest --cov # tests with coverage
uv run ruff check ctfd # lint
uv run mypy # type-check
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ctfd_api-0.2.0.tar.gz.
File metadata
- Download URL: ctfd_api-0.2.0.tar.gz
- Upload date:
- Size: 32.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e70c7efa4918994d57fbdb8eca5d9525dcbaeaf46f1c8d2fbf2aa1815e895ec
|
|
| MD5 |
a35f4e8b1824a9729b0938a75dd53c15
|
|
| BLAKE2b-256 |
4399337cf64128a5cb784fe1081ff9d7c0e8ff3e21ad8be865b1eb94b22f2c95
|
Provenance
The following attestation bundles were made for ctfd_api-0.2.0.tar.gz:
Publisher:
publish-to-pypi.yml on DonAsako/ctfd.py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctfd_api-0.2.0.tar.gz -
Subject digest:
1e70c7efa4918994d57fbdb8eca5d9525dcbaeaf46f1c8d2fbf2aa1815e895ec - Sigstore transparency entry: 1647432707
- Sigstore integration time:
-
Permalink:
DonAsako/ctfd.py@0dae012d80652cb7982cce9740532935487250cb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/DonAsako
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@0dae012d80652cb7982cce9740532935487250cb -
Trigger Event:
push
-
Statement type:
File details
Details for the file ctfd_api-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ctfd_api-0.2.0-py3-none-any.whl
- Upload date:
- Size: 43.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5f117fb6ed021d6e939cfc875b4679436c21ec0efa084bd0704899909ce719d
|
|
| MD5 |
6e4f13979aba3e240f3633ec648d9118
|
|
| BLAKE2b-256 |
047a3c253ffec9909b5793c7e622de5c505f20ead992ac61c24d44c4861b81f8
|
Provenance
The following attestation bundles were made for ctfd_api-0.2.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on DonAsako/ctfd.py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctfd_api-0.2.0-py3-none-any.whl -
Subject digest:
b5f117fb6ed021d6e939cfc875b4679436c21ec0efa084bd0704899909ce719d - Sigstore transparency entry: 1647432875
- Sigstore integration time:
-
Permalink:
DonAsako/ctfd.py@0dae012d80652cb7982cce9740532935487250cb -
Branch / Tag:
refs/heads/main - Owner: https://github.com/DonAsako
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@0dae012d80652cb7982cce9740532935487250cb -
Trigger Event:
push
-
Statement type: