Drekord Discord API Wrapper
A lightweight, async Discord REST API wrapper for Python.
Drekord is NOT a bot framework (like
discord.py). It is a pure REST API client designed to be integrated into any async application that needs to interact with the Discord API, without maintaining a persistent WebSocket connection.
Features
- Pure REST: No WebSocket gateway; just API calls x)
- Fully async: Built on
aiohttpfor cleanasync/awaitusage - Object-Oriented: Call methods directly on channels, messages, users, guilds, etc.
- discord.py-inspired: Familiar API if you've used discord.py before
- Rate-limit awareness: Automatic retry on rate limits and transient errors
Installation
pip install drekord
Or from source:
git clone https://github.com/drek124/drekord.git
cd drekord
pip install -e .
Quick Start
import asyncio
import drekord
async def main():
async with drekord.Client(token="YOUR_BOT_TOKEN") as client:
# Get the current bot user
me = await client.me()
print(f"Logged in as {me.username}")
# Fetch a channel and send a message
channel = await client.fetch_channel(CHANNEL_ID)
msg = await channel.send("Hello from Drekord! 🎉")
print(f"Sent message {msg.id}")
# Read the last 10 messages
messages = await channel.fetch_messages(limit=10)
for m in messages:
print(f"{m.author.username}: {m.content}")
asyncio.run(main())
API Overview
Client Methods
Top-level fetch_* methods on the client:
await client.me() # Current bot user
await client.fetch_user(user_id) # Any user
await client.fetch_channel(channel_id) # Any channel
await client.fetch_message(channel_id, msg_id) # A specific message
await client.fetch_guilds() # List bot's guilds
await client.fetch_guild(guild_id) # A specific guild
await client.fetch_member(guild_id, user_id) # A guild member
await client.fetch_webhook(webhook_id) # A webhook
await client.fetch_webhooks(channel_id) # Webhooks in a channel
Channels
channel = await client.fetch_channel(channel_id)
# Send messages
msg = await channel.send("Hello!", tts=False)
msg = await channel.send(embeds=[embed])
msg = await channel.send(view=layout_view) # Components V2
# Read messages
messages = await channel.fetch_messages(limit=50)
message = await channel.fetch_message(message_id)
# Edit the channel
channel = await channel.edit(name="renamed", topic="New topic")
# Delete the channel
await channel.delete()
# Typing indicator
await channel.typing()
# Invites
invites = await channel.fetch_invites()
Messages
message = await channel.fetch_message(msg_id)
# Edit
await message.edit(content="Edited!")
# Delete
await message.delete(reason="Cleanup requested")
# Pin/Unpin
await message.pin()
await message.unpin()
# Reactions
await message.add_reaction("👍")
await message.remove_reaction("👍")
# Crosspost (announcements)
await message.crosspost()
Users
user = await client.fetch_user(user_id)
# Properties
print(user.username)
print(user.display_name) # global_name or username
print(user.avatar_url())
# Send a DM (auto-creates DM channel)
await user.send("Hello!")
await user.send("Check this out!", embeds=[embed])
# Or create the DM channel explicitly
dm_channel = await user.create_dm()
# Re-fetch
user = await user.fetch()
Guilds
guild = await client.fetch_guild(guild_id)
# Properties
print(guild.name)
# Fetch sub-resources
channels = await guild.fetch_channels()
members = await guild.fetch_members(limit=100)
roles = await guild.fetch_roles()
emojis = await guild.fetch_emojis()
bans = await guild.fetch_bans()
audit_logs = await guild.fetch_audit_logs(limit=50)
# Create
new_channel = await guild.create_channel({"name": "new-channel", "type": 0})
new_role = await guild.create_role({"name": "Moderator", "color": 0xFF0000})
# Edit
guild = await guild.edit({"name": "New Name"})
# Leave / Delete
await guild.leave()
await guild.delete()
Members
member = await client.fetch_member(guild_id, user_id)
# Properties
print(member.display_name)
print(member.roles)
print(member.joined_at)
# Actions
await member.kick(reason="Rule violation")
await member.ban(reason="Spam", delete_message_days=7)
await member.edit(nick="New Nick", roles=[role_id1, role_id2])
Webhooks
webhook = await client.fetch_webhook(webhook_id)
# Execute (send)
await webhook.execute("Hello!", username="Bot")
await webhook.execute(embeds=[embed], wait=True)
# Edit
await webhook.edit({"name": "New Name"})
# Delete
await webhook.delete()
Embeds
embed = drekord.Embed(
title="Hello",
description="World",
color=0x5865F2,
)
embed.set_thumbnail(url="https://example.com/thumb.png")
embed.add_field(name="Field 1", value="Value 1", inline=True)
embed.set_footer(text="Footer text")
await channel.send(embeds=[embed])
Components V2
from drekord.ui import (
LayoutView, Container, TextDisplay, Separator,
ActionRow, Button, Section, Thumbnail,
)
view = LayoutView()
view.add_item(Container(
TextDisplay("## Hello World"),
Separator(),
ActionRow(
Button(label="Click Me", style=1, custom_id="btn_1"),
),
accent_color="#5865F2",
))
await channel.send(view=view)
Raw Requests (Escape Hatch)
For endpoints not yet covered:
data = await client.request("GET", "/gateway")
data = await client.request("POST", "/channels/123/threads", json={"name": "Thread"})
Error Handling
Drekord raises typed exceptions for all error cases:
import drekord
try:
msg = await channel.send("Hello!")
except drekord.ForbiddenError:
print("I don't have permission to send messages here!")
except drekord.NotFoundError:
print("Channel not found!")
except drekord.RateLimitedError as e:
print(f"Rate limited! Retry after {e.retry_after}s")
except drekord.HTTPError as e:
print(f"HTTP error {e.status_code}: {e}")
Exception Hierarchy
DrekordError
└── HTTPError
├── BadRequestError (400)
├── UnauthorizedError (401)
├── ForbiddenError (403)
├── NotFoundError (404)
├── RateLimitedError (429)
└── DiscordServerError (5xx)
Models
All API responses are returned as model objects with typed properties:
user = await client.fetch_user(user_id)
print(user.id) # int
print(user.username) # str
print(user.display_name) # str (global_name or username)
print(user.bot) # bool
print(user.avatar_url()) # str | None
channel = await client.fetch_channel(channel_id)
print(channel.id) # int
print(channel.name) # str | None
print(channel.type_name) # str ("text", "voice", etc.)
message = await channel.fetch_message(msg_id)
print(message.content) # str
print(message.author) # User | None
print(message.embeds) # list[Embed]
License
MIT
Release files for drekord 2.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| drekord-2.0.3.tar.gz | 27.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| drekord-2.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.8 kB
Release files / drekord-2.0.3.tar.gz
| Download URL | drekord-2.0.3.tar.gz |
|---|---|
| Size | 27.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
43b025eb1d7f12b1ef50a9033d5bd349402639bd61cac50976b142c3b8b344de
|
|
BLAKE2b-256 checksum How to use checksums |
8f903d7f802e75c968a010a143ec4d5138c200531c4ad4b71a409296e19c2cc3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / drekord-2.0.3-py3-none-any.whl
| Download URL | drekord-2.0.3-py3-none-any.whl |
|---|---|
| Size | 29.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f1cedbc078b88ad988d3d3a56c184ea7408ba98a8223626422780e92bc108df2
|
|
BLAKE2b-256 checksum How to use checksums |
e0f89fce614e2756a5f1a9660d3f19da2ada8204635ac07741a3ee575c8cc90b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|