Tutorial: MTG Price Discord Bot
A small Node.js bot that responds to /price <card name>
with the current TCGplayer market price.
What you'll need
- An IWMM API key — see Getting Started
- A Discord application + bot token (discord.com/developers)
- Node 20+
1. Install dependencies
npm init -y
npm install discord.js
2. Register the slash command
Save as register.mjs and run once:
import { REST, Routes, SlashCommandBuilder } from "discord.js";
const command = new SlashCommandBuilder()
.setName("price")
.setDescription("Look up the current price of a Magic card")
.addStringOption(o => o.setName("card").setDescription("Card name").setRequired(true));
const rest = new REST({ version: "10" }).setToken(process.env.DISCORD_TOKEN);
await rest.put(
Routes.applicationCommands(process.env.DISCORD_APP_ID),
{ body: [command.toJSON()] }
);
console.log("Command registered.");
3. Wire up the bot
Save as bot.mjs:
import { Client, GatewayIntentBits } from "discord.js";
const API = "https://iwantmymtg.net/api/v1";
const headers = { Authorization: `Bearer ${process.env.IWMM_API_KEY}` };
async function lookupPrice(name) {
const res = await fetch(`${API}/cards?q=${encodeURIComponent(name)}&limit=1`, { headers });
if (!res.ok) throw new Error(`API ${res.status}`);
const { data } = await res.json();
if (!data?.length) return null;
const card = data[0];
const pricesRes = await fetch(`${API}/cards/${card.id}/prices`, { headers });
const prices = (await pricesRes.json()).data;
return { card, prices };
}
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on("interactionCreate", async (i) => {
if (!i.isChatInputCommand() || i.commandName !== "price") return;
await i.deferReply();
try {
const result = await lookupPrice(i.options.getString("card"));
if (!result) return i.editReply("No card found.");
const { card, prices } = result;
const normal = prices.normal ? `$${prices.normal.toFixed(2)}` : "—";
const foil = prices.foil ? `$${prices.foil.toFixed(2)}` : "—";
await i.editReply(`**${card.name}** (${card.setCode.toUpperCase()})\nNormal: ${normal} · Foil: ${foil}`);
} catch (e) {
await i.editReply(`Lookup failed: ${e.message}`);
}
});
client.login(process.env.DISCORD_TOKEN);
4. Run it
export DISCORD_TOKEN=...
export DISCORD_APP_ID=...
export IWMM_API_KEY=iwm_live_...
node register.mjs # one-time
node bot.mjs
Invite the bot to a server, run /price Lightning Bolt,
and you should see a price reply.
Where to go next
- Cache results in memory for a minute to keep request volume down on free tier
- Add a
setoption and look up by set code + number for exact printings - Show price history with
/cards/:cardId/price-history+ an embed