Discord webhook vs bot: which one to use

Use a webhook when you only need to post messages to a channel from another system. Use a bot when you need to read events, answer commands or react to users.

Updated 5 min read

Learning objectives

After reading this article you will be able to:

  • Understand what a webhook and a bot actually do
  • Compare limits, permissions and maintenance cost
  • Pick the right option for your case, or combine both
On this page

A webhook and a bot look alike, because both make a message show up in a Discord channel. The difference is the direction of the conversation. A webhook is an inbox slot: you send a POST and the message appears. A bot is a participant: it receives events, decides what to do and replies.

Picking the wrong one costs time. Someone who runs a bot just to announce "deploy finished" keeps a process alive for no reason. Someone who tries to build commands on a webhook finds out too late that it doesn't listen to anything.

Webhook: send onlyExternalsystemPOSTWebhooktoken in URLBot: sends and receivesUserGatewayor HTTPBotonline 24/7Channel
A webhook only posts into a channel from outside; a bot receives user events and replies, so it stays running.

What is a Discord webhook?#

It is a URL that Discord generates for a channel (in Channel Settings, Integrations, Webhooks). You call it with POST /webhooks/{id}/{token} and no authorization header: the token inside the URL is the credential. Anyone who has the URL can post to the channel.

Through the API, a webhook accepts content (up to 2000 characters), embeds (up to 10 per message), files, polls, and username and avatar_url to change the name and picture of each message. You must send at least one of content, embeds, components, a file or a poll. With ?wait=true the API returns the created message, and with thread_id it posts to a thread.

A minimal example with curl:

bash
curl -X POST "$DISCORD_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{"content": "Deploy finished", "username": "CI"}'

The same in Node.js, with the built-in fetch and no library (save it as .mjs, because of the top-level await):

js
const res = await fetch(process.env.DISCORD_WEBHOOK_URL + "?wait=true", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    embeds: [{ title: "New order", description: "Order #1234 paid", color: 0x57f287 }],
  }),
});

if (res.status === 429) {
  const { retry_after } = await res.json();
  console.error(`Rate limited, retry in ${retry_after}s`);
} else if (!res.ok) {
  console.error("Failed:", res.status, await res.text());
}

What is a Discord bot?#

It is an application with its own user, created in the Developer Portal, that authenticates with a token. It can connect to the Gateway (a WebSocket connection) and receive events in real time: messages, members joining, reactions, slash commands. It can also receive interactions over HTTP if you set an Interactions Endpoint URL, without keeping the Gateway open. Once that URL is set, interactions stop arriving through the Gateway. Every request to the HTTP endpoint carries the X-Signature-Ed25519 and X-Signature-Timestamp headers, and you must verify the signature.

A bot can do almost anything a member can: assign roles, moderate, play audio, show menus and buttons. To build one, see how to make a Discord bot.

Webhook or bot: what is the practical difference?#

Webhook Bot
Direction Only sends messages Sends and receives
Authentication Token inside the URL Bot token
Must stay online No (it is just a URL) Yes on the Gateway; no if HTTP only
Reads messages and events No Yes (message content needs the privileged intent)
Slash commands, buttons No Yes
Scope One channel Every server it was added to
Code One HTTP call A library such as discord.js or discord.py
Maintenance Almost none Update libraries, handle drops, reconnect

One detail about reading: a bot without the privileged MESSAGE_CONTENT intent receives an empty content on messages, with exceptions such as messages that mention the bot, direct messages with it and messages it sends itself. Details in privileged intents.

When is a webhook enough?#

Choose a webhook when the flow is one-way and the trigger comes from outside Discord:

  • monitoring alerts, a failed build or a finished deploy;
  • a notification for a sale, a sign-up or a payment;
  • an RSS, commit or changelog feed in a channel;
  • a cron script that posts a daily summary.

The upside is simplicity. There is no bot token, no intents and no reconnecting. The cost is that a webhook only knows its channel: it does not know who is there, does not hear replies and does not go through role permissions.

When do you need a bot?#

You need a bot as soon as Discord becomes the input of the flow:

  • slash commands (/ticket, /play), buttons and menus;
  • moderation, welcome messages, automatic roles;
  • replies that depend on the user, on history or on a database;
  • music, voice or anything else that uses the Gateway;
  • messages across several servers or channels chosen at runtime.

If the bot must answer around the clock, it needs a process that is always on. See how to keep a Discord bot online 24/7.

Which limits affect each one?#

The global API limit is 50 requests per second per bot, and routes have their own limits that Discord asks you not to hard-code. Requests without an authorization header, such as webhook calls, are limited by IP. When you go over, the API answers 429 with retry_after in seconds. Wait for the time in the Retry-After header or that field before retrying. The example above already reads the field. More in Discord 429 errors.

One more caution: if a webhook returns 404, stop using it (it was probably deleted). The documentation says not to keep retrying, because repeated attempts lead to a temporary restriction.

Content limits are the same for both: 2000 characters of text, 10 embeds per message and 6000 characters across all embed text combined.

Can you combine the two?#

Yes, and many projects do. The bot handles commands and events inside the server. External systems (CI, payment gateway, monitor) send alerts through a webhook, without depending on the bot being online at that moment. If the bot goes down, the alerts keep arriving.

A practical rule: start with a webhook if you only need to notify. Switch to a bot the first time you need to read or reply.

How to do this on Square Cloud#

A webhook needs no hosting, but the code that fires it has to run somewhere. A Gateway bot, on the other hand, needs a process that is always on. On Square Cloud, containers never go to sleep, and automatic restart (on by default for dashboard deploys) brings the bot back when the process exits with an error.

You deploy the bot in Node.js, Python, Java or another runtime by uploading a ZIP in the dashboard, or with the CLI, GitHub or the VS Code extension. The plan's RAM is a pool shared across your applications, with a minimum of 256 MB per background application such as a bot. The Hobby plan starts at R$12.49 per month. Keep the bot token and the webhook URL as the application's environment variables, not in the code.

To get started, see the Discord bot hosting page.

Bot hosting

Keep your bot online 24/7 with automatic restarts

Learn more

FAQs

Does a Discord webhook need hosting?

The webhook itself does not, because it is just a Discord URL. What needs somewhere to run is your code that calls that URL, such as a script, a cron job or a CI service. A bot that uses the Gateway does: it is a process that stays connected all the time.

Can a webhook read messages or respond to commands?

No. An incoming webhook only posts messages to the channel. To read messages or react to users you need a bot (Gateway) or an app that receives interactions over HTTP.

Can I use a webhook and a bot together?

Yes, and it is common. The bot handles commands and events, and webhooks deliver alerts from external systems such as deploys, monitoring or payments.

Is it safe to keep the webhook URL in my code?

No. The token is part of the URL and anyone who has it can post to the channel. Keep it in an environment variable and generate a new URL if it leaks.

Next articleTelegram bot webhook vs long pollingLong polling pulls updates from Telegram and needs no public URL; a webhook receives an HTTPS POST. Compare both and see the code to switch between them.