API

Build a Discord Bot That Removes Image Backgrounds (discord.js, 80 Lines)

September 6, 20266 min readBy BG Clear Editorial

Servers full of streamers, gamers and small shops keep asking the same thing in chat: "can someone remove the background from this?" A bot answers in five seconds. This is a complete discord.js bot with one slash command, /cutout, that accepts an image attachment, runs it through the bgclear API and replies with a transparent PNG. It uses free preview-size results by default, so the bot costs nothing to run until a user asks for HD.

In this guide

Setup

Create an application and bot in the Discord Developer Portal, copy the bot token and application ID, invite the bot with the applications.commands scope, and get a bgclear key from the API dashboard (10 free full-resolution credits; previews are free without limit).

npm init -y && npm i discord.js
export DISCORD_TOKEN=... DISCORD_APP_ID=... BGCLEAR_API_KEY=bgc_live_...

Register the slash command

// register.js — run once (and again when the command definition changes)
import { REST, Routes, SlashCommandBuilder } from "discord.js";

const command = new SlashCommandBuilder()
  .setName("cutout")
  .setDescription("Remove the background from an image")
  .addAttachmentOption((o) => o.setName("image").setDescription("JPG, PNG or WebP").setRequired(true))
  .addBooleanOption((o) => o.setName("hd").setDescription("Full resolution (uses 1 credit)"))
  .addStringOption((o) => o.setName("bg").setDescription("Background: transparent (default) or a hex colour like ffffff"));

const rest = new REST().setToken(process.env.DISCORD_TOKEN);
await rest.put(Routes.applicationCommands(process.env.DISCORD_APP_ID), { body: [command.toJSON()] });
console.log("registered /cutout");

The bot

Discord gives you three seconds to acknowledge an interaction, and background removal takes longer than that, so the handler defers first and edits the reply when the PNG is ready. Attachments arrive as CDN URLs, which the API can fetch directly — no download step in the bot.

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

const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const cooldown = new Map();                      // userId -> timestamp of last call
const COOLDOWN_MS = 15_000;

client.on("interactionCreate", async (interaction) => {
  if (!interaction.isChatInputCommand() || interaction.commandName !== "cutout") return;

  const last = cooldown.get(interaction.user.id) ?? 0;
  if (Date.now() - last < COOLDOWN_MS) {
    return interaction.reply({ content: "Easy — one cutout every 15 seconds.", ephemeral: true });
  }
  cooldown.set(interaction.user.id, Date.now());

  const attachment = interaction.options.getAttachment("image", true);
  if (!attachment.contentType?.startsWith("image/")) {
    return interaction.reply({ content: "Please attach a JPG, PNG or WebP.", ephemeral: true });
  }
  await interaction.deferReply();               // we have 3 s to ack; processing takes ~5 s

  const hd = interaction.options.getBoolean("hd") ?? false;
  const bg = interaction.options.getString("bg") ?? "transparent";

  const res = await fetch("https://www.bgclear.ai/api/v1/remove", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BGCLEAR_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `discord-${attachment.id}-${hd ? "full" : "preview"}`,
    },
    body: JSON.stringify({ image_url: attachment.url, size: hd ? "full" : "preview", format: "png", bg_color: bg }),
  });

  if (!res.ok) {
    const { error } = await res.json().catch(() => ({ error: { message: res.statusText } }));
    const msg = res.status === 402 ? "The bot is out of HD credits — try without hd for a free preview."
              : `Could not process that image (${error?.code ?? res.status}): ${error?.message ?? ""}`;
    return interaction.editReply(msg);
  }

  const png = Buffer.from(await res.arrayBuffer());
  const remaining = res.headers.get("X-Credits-Remaining");
  await interaction.editReply({
    content: hd ? `HD cutout · credits left: ${remaining}` : "Preview cutout (≤800 px, free). Add `hd:true` for full resolution.",
    files: [new AttachmentBuilder(png, { name: "cutout.png" })],
  });
});

client.login(process.env.DISCORD_TOKEN);

Run node register.js once, then node bot.js. Discord's own upload limit applies to the reply (it depends on the server's boost tier); a full-resolution PNG of a very large photo can exceed it — request format: "webp" for HD replies if that happens.

Keeping it free (and when to charge)

Default to preview: it is free at the API, unlimited, unwatermarked, and 800 px is plenty for a Discord embed. Gate hd:true behind a role check (interaction.member.roles.cache.has(PREMIUM_ROLE_ID)) or a per-server allowance, because each HD image costs one credit — $9 for 100, $39 for 500, $129 for 2,000, no subscription (pricing). The idempotency key on the attachment id means a retried interaction never charges twice, and the API's 60-requests-per-minute limit is far above what a single server produces; if you run the bot in many servers, add a global queue.

For a Telegram version of the same bot, and for the hosted-proxy pattern the bot could call instead of holding the key itself, see the blog; the request format is remove.bg's, which matters if you are rewriting a bot that used remove.bg before its API closes on 1 December 2026 (mapping).

Frequently asked questions

Why deferReply?

Discord requires an acknowledgement within three seconds; background removal takes about five. deferReply shows 'thinking…' and editReply delivers the result.

Can the bot process an image someone already posted, without re-uploading?

Yes — add a message context-menu command (ApplicationCommandType.Message) and read the first attachment of the target message; the rest of the handler is identical.

Where should the API key live?

In the bot's environment on the host that runs it (a VPS, Railway, Fly.io), never in the repository. Rotate it from the dashboard if it leaks.

What does the bot cost to run?

Nothing for previews. HD cutouts are one credit each ($0.065–0.09 depending on pack); credits never expire.

Ship it with the bgclear API

remove.bg-compatible endpoints, credits from $9 for 100 images, no subscription. Pay, get a key, make your first call in under two minutes.

Get API credits →

Tools for this guide

Background Removal API Free

background removal api free

Cheapest Background Remover API

cheapest background remover api

Discord PFP Background Remover

discord pfp background remover

Keep reading

API

Remove Image Backgrounds in Node.js with fetch and FormData (No SDK)

Node 18+ built-in fetch, a file upload, a URL input, an Express proxy endpoint so your browser never sees the key, and retries that cannot double-charge.

API

A Serverless Proxy for a Background Removal API on Cloudflare Workers and Vercel Edge

Forty lines that keep your API key off the client, stream the PNG straight back, add a shared-token check and a per-IP cooldown — on Cloudflare Workers, then the same thing as a Vercel Edge route, with the body-size limits that decide which input mode to use.

Avatar

Remove Background from Discord Profile Pictures

Practical 2026 guide to discord pfp background remover for Discord users working on avatars and server icons. Free tool, HD output, no signup.

API

A Free Background Removal API for GitHub Projects: What Is Actually Free, Plus a GitHub Action

The honest free tier (unlimited previews, 10 credits, a free RapidAPI plan), how to keep the key out of a public repo, and a GitHub Actions workflow that removes backgrounds from images in CI — free in pull requests, paid only on release.