← Developer Portal

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

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 set option and look up by set code + number for exact printings
  • Show price history with /cards/:cardId/price-history + an embed