discordforge python library
A fully async Python SDK for the DiscordForge bot listing platform. Works with small bots and large sharded/auto-sharded bots equally.
Requirements
- Python 3.11+
httpx >= 0.27
Installation
pip install discordforge
Quickstart
import os
import discord
from discordforge import ForgeClient, AutoPoster
bot = discord.Client(intents=discord.Intents.default())
forge = ForgeClient(os.environ["DISCORDFORGE_API_KEY"], bot_id=os.environ["BOT_ID"])
async def setup_hook():
await forge.sync_from_discordpy(bot.tree, category="General")
AutoPoster(forge, bot).start()
bot.setup_hook = setup_hook
bot.run(os.environ["BOT_TOKEN"])
AutoPoster posts your stats every 5 minutes automatically. It waits until all shards are ready before the first post, so counts are always accurate.
Want to post every 30 minutes instead? Pass interval=1800.0. Minimum is 300s (API limit).
AutoPoster(forge, bot, interval=1800.0).start()
Usage with discord.py
AutoPoster
poster = AutoPoster(forge, bot, interval=300.0) # minimum 300s
poster.on("post", lambda stats: print(f"Posted {stats.server_count} servers"))
poster.on("error", lambda err: print(f"Error: {err}"))
poster.start()
Callbacks can be sync or async. The poster keeps running even if a post fails.
Manual stat posting
from discordforge import ForgeClient, BotStats
forge = ForgeClient("YOUR_API_KEY", bot_id="YOUR_BOT_ID")
@bot.event
async def on_ready():
await forge.post_stats(BotStats(
server_count=len(bot.guilds),
shard_count=bot.shard_count,
user_count=len(bot.users),
))
Check if a user voted
vote = await forge.check_vote("USER_DISCORD_ID")
if vote.has_voted:
print(f"Voted at {vote.voted_at}, next vote available at {vote.next_vote_at}")
else:
await ctx.send("You haven't voted yet! https://discordforge.org/bots/YOUR_BOT_ID")
Get your bot's public listing info
info = await forge.get_bot()
print(f"{info.name} has {info.vote_count} votes across {info.server_count} servers")
Sync slash commands
Pass your discord.py command tree and it maps commands automatically:
async def setup_hook():
await bot.tree.sync()
await forge.sync_from_discordpy(bot.tree, category="General")
Or pass a raw list if you want full control:
from discordforge import DiscordCommand
await forge.sync_commands([
DiscordCommand(name="ping", description="Check bot latency"),
DiscordCommand(name="help", description="Show help menu"),
])
Configuration
from discordforge import ForgeClient, ClientOptions
forge = ForgeClient(
"YOUR_API_KEY",
bot_id="YOUR_BOT_ID",
options=ClientOptions(
timeout=10.0, # seconds per request
retries=3, # retries on 5xx / network errors
max_connections=10, # httpx connection pool size
max_keepalive_connections=5,
),
)
Error handling
from discordforge.errors import ForgeAPIError, ForgeRateLimitError, ForgeAuthError, ForgeNotFoundError
try:
await forge.post_stats(BotStats(server_count=100))
except ForgeRateLimitError as e:
print(f"Rate limited, retry in {e.retry_after}s")
except ForgeAuthError:
print("Invalid API key")
except ForgeAPIError as e:
print(f"API error {e.status}: {e}")
| Exception | When raised |
|---|---|
ForgeRateLimitError |
429 – backs off and retries automatically, only raises after all retries exhausted |
ForgeAuthError |
401 – invalid or missing API key |
ForgeNotFoundError |
404 – bot ID not found on DiscordForge |
ForgeAPIError |
any other non-2xx response |
AutoPoster reference
poster = AutoPoster(
forge, # ForgeClient instance
bot, # any discord client (discord.py, nextcord, py-cord, eris-style)
interval=300.0, # seconds between posts (minimum 300 – API rate limit)
start_immediately=True, # post as soon as ready fires
)
poster.on("post", callback) # called after each successful post with BotStats
poster.on("error", callback) # called on failure – poster keeps running
poster.start() # start the background task
poster.stop() # cancel the task (can restart with start())
poster.destroy() # stop + clear all listeners
poster.is_running # bool
Callbacks can be sync or async – both work:
async def on_post(stats):
await log_channel.send(f"Posted {stats.server_count} servers")
def on_error(err):
print(f"Error: {err}")
poster.on("post", on_post)
poster.on("error", on_error)
Types reference
| Type | Fields |
|---|---|
BotStats |
server_count, shard_count, user_count, voice_connections |
VoteMetadata |
has_voted, voted_at, next_vote_at |
BotInfo |
id, name, vote_count, server_count |
DiscordCommand |
name, description, type, options |
CustomCommand |
name, description, usage, category |
Examples
The examples/ folder has ready-to-run scripts covering every API feature:
| File | What it shows |
|---|---|
post_stats.py |
Post server/shard/user counts |
check_vote.py |
Check if a user voted in the last 8h |
get_bot.py |
Fetch your bot's public listing info |
sync_commands.py |
Sync commands using DiscordCommand or CustomCommand |
sync_commands_discordpy.py |
Auto-sync directly from bot.tree |
autoposter.py |
AutoPoster with post/error callbacks |
vote_reward.py |
Full vote reward flow with role assignment |
error_handling.py |
Catching all error types |
bot.py |
Complete bot with all slash commands and AutoPoster |
All examples use environment variables for credentials – copy and set these before running:
export DISCORDFORGE_API_KEY="your_api_key"
export BOT_ID="your_bot_discord_id"
export BOT_TOKEN="your_bot_token" # discord.py examples only
export VOTER_ROLE_ID="role_id" # vote_reward.py only
Running tests
pip install -e ".[dev]"
pytest tests/ -v
License
MIT
Metadata
Release files for discordforge 1.0.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| discordforge-1.0.5.tar.gz | 19.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| discordforge-1.0.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.8 kB
Release files / discordforge-1.0.5.tar.gz
| Download URL | discordforge-1.0.5.tar.gz |
|---|---|
| Size | 19.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cda39a3eefc39e15e6f46471db09bd72273e219e78f4c7b6470c77e6236d9793
|
|
BLAKE2b-256 checksum How to use checksums |
0689532140b87ccb0e38363d762571cfa8e03e7890bd371c14668c10da24bbc4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency logRelease files / discordforge-1.0.5-py3-none-any.whl
| Download URL | discordforge-1.0.5-py3-none-any.whl |
|---|---|
| Size | 12.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c2bf6e5cd5f7f37b0ea8fca90b1c342ee2caa1966a8d556759f4d5868f487966
|
|
BLAKE2b-256 checksum How to use checksums |
c88fa357196a046a3f7691e21cb0eadfd6304e3a10d62b85f1272b3301340b43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.
Transparency log