Only show guides for your app
The Python path to a live bot — deploy the discord.py starter, drop in your token, and get /ping answering, then walk through app.py and add commands of your own.
Prefer Python? discord.py gets you to the same place as the JavaScript guide — a bot answering /ping in your server — with the same short path: deploy a starter, paste your token, start. Then we'll go through app.py line by line and add a command.
| At a glance | |
|---|---|
| You need | a bot token from Your first Discord bot, and a Falix server running the Python application |
| Plan | free or premium — free runs while your session timer lasts, premium runs 24/7 |
| Time | about fifteen minutes |
No server yet? See Create your first app server.
Use the template on this page. It writes app.py, requirements.txt, .env, and a couple of helper files, overwriting only files with those names. If the server currently runs a different application, deploying switches it to Python first — that reinstalls the server and wipes its files, with a warning first. On a fresh or already-Python server, nothing else changes.
Open the File Manager and edit .env:
DISCORD_TOKEN=YOUR_TOKEN_HERE
Replace YOUR_TOKEN_HERE with the token from the Developer Portal, save, and close. (See Environment variables & secrets.)
💡 Tip: Keeping the token in
.envmeans it never lives inside your code and never gets committed to Git.
Press Start on the Console page. The first run installs discord.py and python-dotenv from requirements.txt (a short wait), then prints:
Listening as YourBot#0000
That's your success signal. In your Discord server the bot's grey dot turns green — it's now connected.
🎯 Good to know: A bot only makes outbound connections to Discord, so there's no port involved.
In the server you invited the bot to, type /ping. It replies with its latency. If the command isn't in the list yet, wait a couple of minutes — slash commands can take a little while to register the first time — then try again.
Want the bot in a second server? The invite URL you built in the Developer Portal isn't single-use — open it again and pick another server (any where you have Manage Server). The same running bot serves every server it's in; nothing to redeploy.
Here's the app.py the template deployed:
import os
import discord
from discord import app_commands
from dotenv import load_dotenv
load_dotenv()
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
@client.event
async def on_ready():
await tree.sync()
print(f"Listening as {client.user}", flush=True)
@tree.command(name="ping", description="Check that the bot is alive")
async def ping(interaction: discord.Interaction):
await interaction.response.send_message(
f"Pong! {round(client.latency * 1000)} ms"
)
client.run(os.environ["DISCORD_TOKEN"])
The pieces:
load_dotenv() reads .env so os.environ["DISCORD_TOKEN"] finds your token without hard-coding it.discord.Intents.default() is all a slash-command bot needs. Enable a privileged intent (members, message content) in the Developer Portal only when your code actually uses it.discord.Client plus an app_commands.CommandTree — the client is the connection; the tree holds your slash commands.on_ready runs once after login. tree.sync() pushes your commands to Discord (that's what makes /ping appear), and the print(..., flush=True) reports success. The flush=True matters: without it Python buffers output and your line may not show in the console until much later.@tree.command(...) defines a command; the function under it runs when someone uses it, replying with interaction.response.send_message(...).client.run(os.environ["DISCORD_TOKEN"]) connects with the token from .env and keeps the bot alive.Adding a command is one block. Say you want /roll. At the top of the file, add import random next to the other imports, then add the command anywhere before client.run(...):
@tree.command(name="roll", description="Roll a six-sided die")
async def roll(interaction: discord.Interaction):
result = random.randint(1, 6)
await interaction.response.send_message(f"🎲 You rolled a {result}")
Save and restart. Because on_ready calls tree.sync() on every start, /roll registers automatically — after the first-time delay it appears next to /ping. Every command follows this pattern: decorate a function with @tree.command(...).
To pull in a library — a database driver, an HTTP client, anything on PyPI — open the Packages page in your server menu, type the name into the Install package panel, and press Install. Your requirements.txt is updated for you; restart so the bot loads it. Hand-editing requirements.txt works too (it's installed on every start), but the Packages page is the easy path and flags outdated or insecure packages.
LoginFailure: Improper token has been passed. — the token in .env is wrong or was reset. Copy a fresh one from the Developer Portal (resetting invalidates the old) and paste it in. See Bot appears offline.PrivilegedIntentsRequired — your code turned on a privileged intent that isn't enabled on the Bot page in the Developer Portal. Enable it there, or don't request it.applications.commands scope.print() never appears — Python buffers output; pass flush=True (the template already does) so lines show immediately.Listening as … line, as soon as the handler raises. Read it, fix, and restart.