Skip to main content

Python client for the CTFd API

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.name, me.score)

        # 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.1.0.tar.gz (31.3 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.1.0-py3-none-any.whl (42.5 kB view details)

Uploaded Python 3

File details

Details for the file ctfd_api-0.1.0.tar.gz.

File metadata

  • Download URL: ctfd_api-0.1.0.tar.gz
  • Upload date:
  • Size: 31.3 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.1.0.tar.gz
Algorithm Hash digest
SHA256 58df0b8e83ff235d013d6b960493a30bd62b1b9924614042225fe5cc56555ae8
MD5 005982a190845e70cc809e8a8ba8a92a
BLAKE2b-256 9069c16e3c0efcbbc131c67bbe1b6d39da83072c2a4e7492c0199f0d7c238b9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctfd_api-0.1.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.1.0-py3-none-any.whl.

File metadata

  • Download URL: ctfd_api-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 42.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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bd880f1c7dfcb6139114064fdd01689c3374833757f34c0799742a088d473913
MD5 6cb6739509a843612b64ef0abec91922
BLAKE2b-256 f4b46ef4601a3ea10328dcdac25afb2a055a55293afda2ec14760ad236e53a48

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctfd_api-0.1.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