Claude API Discord Bot Setup Guide
If you want to put Claude inside a Discord server — answering questions, summarizing threads, or running a help channel — you need three things: a Discord bot application, a way to call the Claude API, and a small Node.js service that connects the two. This guide walks through all three, from registering the bot to deploying a working message handler.
The setup takes about 30 minutes if you already have API access. If you don't, you can either apply for direct Anthropic API access or use a service like SubToAPI that turns your existing Claude access into a standard HTTPS API key — useful if you don't want to manage separate billing or wait on enterprise approval for a side project bot.
Step 1: Create the Discord Application and Bot
- Go to the Discord Developer Portal and click New Application.
- Under Bot, click Add Bot, then copy the bot token — you'll need it as an environment variable.
- Under Bot settings, enable Message Content Intent. Without this, your bot can't read message text.
- Under OAuth2 > URL Generator, select the
botscope and permissions forSend Messages,Read Message History, andUse Slash Commands. Use the generated URL to invite the bot to your test server.
Step 2: Get a Claude API Key
You need an API key to send requests to Claude. There are two common paths:
- Direct Anthropic access: works fine, but usage tracking, team permissions, and rate-limit visibility are minimal out of the box.
- SubToAPI: wraps your Claude access into a standard
sub_live_...key with per-key usage metadata, streaming support, and team seats — handy if more than one person maintains the bot. Sign up at /signup and grab your key from the dashboard.
Either way, store the key as an environment variable, never hardcode it:
export DISCORD_TOKEN="your-discord-bot-token"
export CLAUDE_API_KEY="sub_live_xxxxxxxxxxxx"
Step 3: Scaffold the Bot Project
mkdir claude-discord-bot && cd claude-discord-bot
npm init -y
npm install discord.js dotenv
Create index.js:
import { Client, GatewayIntentBits } from "discord.js";
import "dotenv/config";
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.once("ready", () => {
console.log(`Logged in as ${client.user.tag}`);
});
client.login(process.env.DISCORD_TOKEN);
Run it with node index.js. If the bot shows as online in your server, you're ready to wire in Claude.
Step 4: Call Claude on Incoming Messages
Add a message listener that forwards user text to Claude and replies with the response. This example uses SubToAPI's /v1/messages endpoint, which follows the same request shape as Anthropic's API:
client.on("messageCreate", async (message) => {
if (message.author.bot) return;
if (!message.mentions.has(client.user)) return;
const prompt = message.content.replace(/<@!?\d+>/, "").trim();
if (!prompt) return;
await message.channel.sendTyping();
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.CLAUDE_API_KEY}`,
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 800,
system: "You are a concise, helpful assistant inside a Discord server.",
messages: [{ role: "user", content: prompt }],
}),
});
const data = await response.json();
const reply = data.content?.[0]?.text ?? "Something went wrong.";
// Discord caps messages at 2000 characters
await message.reply(reply.slice(0, 1990));
});
Mention the bot in a channel (@YourBot what's the deadline for the launch?) and it should reply with Claude's answer. Full request/response details are in /docs/messages; if you're new to the API shape in general, /docs/quickstart covers the basics end to end.
Step 5: Add Streaming for Longer Replies
Discord's rate limits make long single replies slow to send. For a better experience, stream Claude's output and edit the message incrementally:
const stream = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.CLAUDE_API_KEY}`,
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 800,
stream: true,
messages: [{ role: "user", content: prompt }],
}),
});
Parse the server-sent events and batch edits every few hundred milliseconds rather than on every token — Discord's message edit rate limit will throttle you otherwise. See /docs/streaming for the event format.
Step 6: Handle Conversation Memory
Discord doesn't give you conversation state for free. For a bot that remembers context within a channel:
- Keep a short in-memory array of the last 10–20 messages per channel ID.
- Trim it aggressively — Claude's context window is large, but sending your entire channel history on every reply wastes tokens and money.
- Persist nothing by default; if you need memory across restarts, use Redis or a flat file keyed by channel ID.
const history = new Map(); // channelId -> messages array
function pushHistory(channelId, role, content) {
const arr = history.get(channelId) ?? [];
arr.push({ role, content });
if (arr.length > 20) arr.shift();
history.set(channelId, arr);
}
Step 7: Deploy It
A Discord bot just needs a long-running process — any small VM, a Fly.io app, or a Railway deployment works. Make sure your DISCORD_TOKEN and CLAUDE_API_KEY are set as environment secrets, not committed to the repo, and that the process restarts automatically on crash (pm2 or a systemd unit is enough for small bots).
If multiple teammates maintain the bot, give each person their own SubToAPI key under a shared workspace rather than passing one key around — see /pricing for Team and Scale plans with seat-based access.
Common Pitfalls
- Bot doesn't respond: check that Message Content Intent is enabled in the Developer Portal — this is the #1 cause of silent failures.
- Replies get cut off: Discord messages cap at 2000 characters; split long replies or summarize with a tighter
max_tokens. - Rate limit errors from Claude: add basic retry-with-backoff logic around your fetch call, especially if several channels message the bot at once.
Questions
Do I need an Anthropic Business account to build a Discord bot? No. A standard API key works fine for personal or small-team bots. If you want usage visibility and team seats without separate enterprise billing, a wrapped key from a service like SubToAPI is a lighter-weight option.
Can the bot handle multiple servers at once? Yes — discord.js handles multi-guild connections natively. Just key your conversation history and rate-limit logic by channel or guild ID so contexts don't bleed into each other.
How do I keep API costs predictable? Cap max_tokens per reply, trim conversation history aggressively, and only respond when the bot is mentioned rather than on every message in a channel. Monitoring usage per key through your API provider's dashboard also helps catch runaway channels early.