Bulk-delete your Discord messages and reactions, driven by your GDPR data package.
discord-delete reads the exact channel and message IDs from your data package so it only sends DELETEs. The search API is never touched, and nothing breaks on a UI change. It removes messages and reactions, headless or through an interactive terminal UI (TUI).
Automating a user account is against Discord's Terms of Service and can get it banned. While this tool cooperates with Discord's rate limits, that doesn't fully protect against a ban.
paru -S discord-delete-bin # or: yay -S discord-delete-bin Homebrew (macOS)
brew install DatCodeMania/tap/discord-delete Scoop (Windows)
scoop bucket add datcodemania https://github.com/DatCodeMania/scoop-bucket scoop install discord-delete Binary releases (any platform)
Download the official binaries from the releases page .
go install github.com/DatCodeMania/discord-delete@latest Build from source git clone https://github.com/DatCodeMania/discord-delete cd discord-delete go build -o discord-delete . macOS/Windows unsigned binaries The binaries are not code-signed. On macOS, a manually downloaded binary may be blocked by Gatekeeper on first run: choose Open from the right-click menu, or run xattr -dr com.apple.quarantine ./discord-delete (Homebrew does this for you). On Windows, SmartScreen may warn: click More info, then Run anyway.
Obtain your data package: in Discord, go to Settings > Data & Privacy > Request Data and enable both Messages and Activity (Activity for reactions). Discord emails it within a few hours to days.
By default every run is a dry run: it previews what would be deleted and changes nothing. Add --execute (or confirm in the TUI) for a proper run.
Then point discord-delete at your package:
discord-delete # default, with the package picker discord-delete --package package.zip # without During a run: space or p pauses and resumes, [ and ] nudge the pacing floor slower or faster by 0.2s per step, q quits.
discord-delete --package package.zip --no-tui # dry run DISCORD_TOKEN=... discord-delete --package package.zip --no-tui --execute # execute, default config Full help: discord-delete --help
Deleting messages across a large account takes days. Older messages hit a shared, account-wide limit that caps deletion at roughly one every two seconds, so my own 400,000+ message account, the largest tested so far, took over a week. discord-delete can be run headless on a server without any babysitting. Configure a ntfy topic ( --ntfy ) to receive:
DISCORD_TOKEN=... discord-delete --package package.zip --no-tui --execute --ntfy my-topic Note
ntfy.sh topics are publicly viewable by default. Notifications carry only counts and status, no message content or channel names. Anyone who can read your topic can pause/stop the run, so use a hard-to-guess topic, or your own ntfy server.
Every run (including dry) writes a plain-text report when it ends: finished, stopped with q , or aborted.
It holds the run's mode and status, how long it took, and per phase how many items were deleted, skipped, failed, and how many were already gone from an earlier run. Then it lists everything Discord refused to delete (403), grouped by server, with Discord's own reason and whether you are still a member.
Undeletable messages (403), grouped, with the reason Discord returned: Some Server 1,204 in 18 channel(s) (you left this server) reason: Missing Access #weekend crew 9 channel 1046829174553219884 reason: Cannot execute action on a system message Missing Access means you lost access to that server or DM, so rejoin or reopen it and run again: a re-run retries what was skipped and skips what is already deleted.
System messages: Your package includes the ones you triggered (starting a call, renaming a group, adding or removing someone), and these are not deletable. They look like normal messages until Discord rejects the delete.
Reports land in your OS cache directory, timestamped, next to the resume log:
~/.cache/discord-delete/progress/user-<id>-20260717-143022.report.txt # Linux ~/Library/Caches/discord-delete/progress/... # macOS %LocalAppData%\discord-delete\progress\... # Windows Use --report PATH to write it somewhere specific, or DISCORD_DELETE_STATE_DIR to move the whole directory.
That directory also holds the resume log, <key>.deleted.log , one confirmed-gone ID per line. It is keyed by account and therefore works across packages. Delete the log to start over from scratch.
The TUI also saves its settings to <key>.config.json in that directory, so the next run for that account starts where you left off. The token is not saved here. Any explicit flag overrides a saved value, and headless runs ignore the file.
discord-delete also removes reactions you added, reading them from the Activity data in your package. They run as a separate phase, set up in the TUI like everything else.
Headless, enable them with --reactions (messages first by default), or run them on their own:
discord-delete --package package.zip --reactions # messages, then reactions discord-delete --package package.zip --no-messages # reactions only discord-delete --package package.zip --reactions --run-order reactions # reactions first # reactions only, headless, scoped to two channels at a slower per-channel pace: discord-delete --package package.zip --no-messages --no-tui --reaction-channel 1097843025518923776,1099156482310307840 --reaction-delay 1.5 How it works Your data package holds the exact channel and message IDs for everything you posted, so discord-delete never has to search. It sends one DELETE per item to Discord's REST API, the same endpoint the client uses, and reads the response to decide what to do next.
Discord's rate-limit buckets are per channel, so each channel is cleared serially at roughly human pace, many in parallel. Older messages share one account-wide bucket, so the run reduces to one worker. Pacing is adaptive per channel (AIMD off a floor), so a run settles on the fastest rate Discord tolerates. A 429 only means slow down: it pauses every worker without aborting. Only a sustained wall of 401s (which is probably a dead token) aborts.
Every confirmed-gone ID (deleted or already absent) is written to a log as it happens. A re-run filters those out before queuing anything, so a crash or exit loses no progress.
Deleting needs your user token (a dry run does not). Two ways to provide it:
The token is then verified against Discord ( GET /users/@me ), and the tool shows who you are logged in as.
The two most popular alternatives are below, both browser-based. A large account takes days on any of these; discord-delete is the one you start in a console and leave alone. Discrub is the closest comparison: actively maintained, with a well-designed web app for anyone not comfortable in a terminal. Pick whichever suits your use case.
Discord takes a few hours to a few days to send the package. The only way to speed it up is to request less: Messages for messages, Activity for reactions. The package covers what existed when Discord built it. Anything posted after that is not in it, so if you ever intend to re-delete you must request a fresh one. discord-delete does not delete other people's messages, even where you have Manage Messages. License MIT. See LICENSE .
Hacker News
news.ycombinator.com