← Blog

Claude API Discord Bot Development Guide

2026-10-24 · 5 min read · SubToAPI Team

If you're searching for a Claude API Discord bot development guide, you're probably trying to figure out three things: how to connect Claude to Discord's gateway, how to handle message formatting and rate limits without the bot falling over, and how to keep conversation context across replies. This guide covers all three with working code, using Node.js and discord.js as the bot framework and a Claude-compatible API for the model calls.

The short version: you listen for messageCreate events, send the message content to Claude's messages endpoint, and reply with the response text. The hard parts are managing per-channel conversation history, respecting Discord's 2000-character message limit, handling streaming responses without spamming edits, and dealing with rate limits gracefully so your bot doesn't silently go offline during a busy server.

Setting Up the Bot

Start with a standard discord.js bot. You'll need a Discord application token from the Discord Developer Portal and the MESSAGE_CONTENT intent enabled.

import { Client, GatewayIntentBits } from "discord.js";

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

client.on("ready", () => console.log(`Logged in as ${client.user.tag}`));
client.login(process.env.DISCORD_TOKEN);

From here, every message your bot receives is just a trigger for a Claude API call. The bot logic itself is intentionally thin — the interesting work happens in how you structure the API calls.

Calling Claude from the Message Handler

A minimal handler sends the user's message as a single-turn request and replies with the output:

client.on("messageCreate", async (message) => {
  if (message.author.bot) return;
  if (!message.mentions.has(client.user)) return;

  const prompt = message.content.replace(/<@!?\d+>/g, "").trim();

  const res = await fetch("https://api.example.com/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.CLAUDE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet",
      max_tokens: 1024,
      messages: [{ role: "user", content: prompt }],
    }),
  });

  const data = await res.json();
  await message.reply(data.content[0].text.slice(0, 2000));
});

This works for a quick prototype, but it has no memory, no error handling, and will crash on Discord's message length limit the moment Claude returns more than 2000 characters.

Handling Conversation Memory

Discord bots feel much more useful when they remember context within a channel or thread. The simplest approach is an in-memory map keyed by channel ID, storing the last N messages:

const histories = new Map();
const MAX_TURNS = 10;

function getHistory(channelId) {
  if (!histories.has(channelId)) histories.set(channelId, []);
  return histories.get(channelId);
}

function pushTurn(channelId, role, content) {
  const history = getHistory(channelId);
  history.push({ role, content });
  if (history.length > MAX_TURNS * 2) history.splice(0, 2);
}

Pass the full history array as messages on each request instead of a single user turn. For anything beyond a hobby bot, swap the in-memory map for Redis or a database table so history survives restarts and works across multiple bot instances.

Splitting Long Responses

Discord caps a single message at 2000 characters. Claude responses regularly exceed that, especially for code or explanations. Split on paragraph or sentence boundaries rather than hard-truncating:

function splitMessage(text, limit = 2000) {
  const chunks = [];
  let remaining = text;
  while (remaining.length > limit) {
    let cut = remaining.lastIndexOf("\n", limit);
    if (cut === -1) cut = limit;
    chunks.push(remaining.slice(0, cut));
    remaining = remaining.slice(cut);
  }
  chunks.push(remaining);
  return chunks;
}

Send each chunk as a separate message.reply or channel.send, with a short delay between sends so Discord's rate limiter doesn't throttle you.

Streaming Responses into Discord

Discord doesn't support true token-by-token streaming in a message, but you can simulate it by editing a message every few hundred milliseconds as tokens arrive. This gives users a "typing" feel for longer answers. Buffer incoming chunks and edit on an interval rather than on every token — editing on every token will hit Discord's per-message edit rate limit almost immediately. A 400–700ms edit interval is usually safe.

Rate Limits and Reliability

Discord bots that get adopted by active servers can generate bursts of requests that outpace what a single API key comfortably handles, especially during peak hours. A few practical mitigations:

This is where a service like SubToAPI is useful if you're already a Claude subscriber and want to avoid juggling separate API billing for a side project. It wraps your existing Claude access in a standard HTTPS endpoint with its own sub_live_ API keys, streaming support, and usage metadata per key — handy if you want to issue a dedicated key per bot or per server and track consumption without building that tooling yourself. Setup is the same /v1/messages-style call shown above; see the quickstart and messages docs for the exact request shape, and streaming docs if you're building the live-edit effect described earlier.

Slash Commands vs. Mentions

Plain messageCreate listeners work but clutter channels with mention-based triggers. For a more polished bot, register a slash command like /ask using Discord's application command API, and pass the option value as the prompt. This also makes it trivial to add parameters — tone, response length, or model choice — as command options rather than parsing free text.

Deployment Notes

Run the bot as a long-lived process (not a serverless function) since discord.js maintains a persistent WebSocket connection to Discord's gateway. A small VPS or container with auto-restart on crash is sufficient for most community bots. Keep your Claude API key and Discord token in environment variables, never in source, and rotate the API key if the bot's host ever changes.

How do I keep my Claude API Discord bot from exceeding rate limits?

Queue requests per channel, cap max_tokens, add exponential backoff on 429s, and avoid firing a new Claude call for every keystroke-level edit — only respond to complete messages.

Can a Discord bot stream Claude responses in real time?

Not token-by-token in a single message bubble, but you can buffer chunks and edit the message every 400–700ms to simulate streaming without hitting Discord's edit rate limit.

Should I give my bot persistent memory of conversations?

Yes, for most use cases — store the last 5–10 turns per channel in Redis or a database, not just in-memory, so context survives bot restarts and scales across multiple server instances.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →