Skip to main content

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

GPL-3.0-or-later

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ctfd_api-0.2.0.tar.gz (32.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ctfd_api-0.2.0-py3-none-any.whl (43.5 kB view details)

Uploaded Python 3

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

Hashes for ctfd_api-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1e70c7efa4918994d57fbdb8eca5d9525dcbaeaf46f1c8d2fbf2aa1815e895ec
MD5 a35f4e8b1824a9729b0938a75dd53c15
BLAKE2b-256 4399337cf64128a5cb784fe1081ff9d7c0e8ff3e21ad8be865b1eb94b22f2c95

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctfd_api-0.2.0.tar.gz:

Publisher: publish-to-pypi.yml on DonAsako/ctfd.py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for ctfd_api-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b5f117fb6ed021d6e939cfc875b4679436c21ec0efa084bd0704899909ce719d
MD5 6e4f13979aba3e240f3633ec648d9118
BLAKE2b-256 047a3c253ffec9909b5793c7e622de5c505f20ead992ac61c24d44c4861b81f8

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page