How to Export Discord Server History: Data Requests, Exporters and Bots
Get a whole Discord channel out, not just your own messages. Discord's data request, DiscordChatExporter, a custom bot with the right intents, thread coverage, and what a compliance team will ask for.
The short answer
Discord's native data package holds only the messages your own account sent. Exporting a whole server channel, with the conversations from every member, needs an open-source backup tool or a custom bot with specific permissions. A compliance export needs a bot configured with the Message Content intent and channel history access. An exporter without those permissions skips message text, threads, and uploaded attachments.
Method 1: Discord's data request
Discord provides a tool under account privacy settings to satisfy data privacy regulation such as GDPR. The tool packages your own account activity.
- Open Discord on your desktop or browser client.
- Click the gear icon next to your username in the lower left corner to enter User Settings.
- Select Data & Privacy from the left sidebar.
- Scroll down to the Request all of my Data section.
- Click the Request Data button.
- Select the data packages to include, then confirm your request.
package/
├── account/
├── activity/
├── messages/
│ ├── c1049281048201/
│ │ └── channel.json
│ │ └── messages.csv
│ └── index.json
└── servers/
Discord assembles the package in the background and emails a download link to your registered address. Discord states that the request can take up to 30 days. Plan around that figure when a deadline is attached.
Scope is the limitation. The messages/ folder holds CSV files for every channel you have typed in, and those CSVs list only the messages your account sent. The package carries no context, no inbound customer questions, and no replies from your team. A personal data request is not a server backup.
Method 2: DiscordChatExporter
DiscordChatExporter is an open-source community tool that extracts complete server channel logs into several formats. It ships both a graphical interface and a command-line interface.
The tool reads channels through the Discord API and writes HTML, JSON, CSV, or plain text. The HTML export renders an offline interface close to Discord's own, including theme colours, user roles, markdown formatting, and image embeds.
To run an automated export from the command line, download the executable for your operating system and run the export command:
./DiscordChatExporter.Cli export \
-t "YOUR_BOT_TOKEN" \
-c 982347109283471029 \
-f Json \
-o "./exports/support-channel.json" \
--media \
--reuse-media
The command queries channel ID 982347109283471029, writes the history into a structured JSON file, and downloads the attached files into a local folder.
DiscordChatExporter accepts two kinds of authorization token: bot tokens and personal user tokens. Using a personal user token for automated exports breaks Discord's Terms of Service and can end in account termination. Create an authorised bot account in the Discord Developer Portal and add that bot to your server when you are pulling business records.
Method 3: your own bot
Your own export bot gives you control over rate limits, data schemas, and destination storage.
A custom bot needs two permissions in each target channel:
View Channel, to see the channel in the server hierarchy.Read Message History, to read past messages rather than only new events.
You also enable the Message Content Intent in the Discord Developer Portal under the Bot tab. Message Content is a privileged intent. Once a bot sits in 100 servers or more, it has to pass Discord's verification and have the intent approved before it can read message bodies at all.
Discord caps the messages endpoint at 100 messages per request. For a full channel log, write a loop that queries the API with the before parameter set to the oldest snowflake ID from the previous batch.
import os
import time
import requests
BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN")
CHANNEL_ID = "982347109283471029"
HEADERS = {"Authorization": f"Bot {BOT_TOKEN}"}
BASE_URL = f"https://discord.com/api/v10/channels/{CHANNEL_ID}/messages"
all_messages = []
before_id = None
while True:
params = {"limit": 100}
if before_id:
params["before"] = before_id
response = requests.get(BASE_URL, headers=HEADERS, params=params)
if response.status_code == 429:
retry_after = response.json().get("retry_after", 1.0)
time.sleep(float(retry_after))
continue
messages = response.json()
if not messages:
break
all_messages.extend(messages)
before_id = messages[-1]["id"]
# Check rate limit headers
remaining = response.headers.get("X-RateLimit-Remaining")
if remaining == "0":
reset_after = float(response.headers.get("X-RateLimit-Reset-After", 1.0))
time.sleep(reset_after)
print(f"Exported {len(all_messages)} messages.")
The X-RateLimit-Remaining and X-RateLimit-Reset-After response headers carry the pacing Discord's edge routers expect. Reading them keeps your job away from hard rate limit blocks.
Permissions and what they gate
| You want | You need |
|---|---|
| Read messages in standard channels | View Channel, Read Message History, Message Content Intent |
| Access restricted staff channels | Role grant for the channel override, View Channel |
| Export active and archived threads | Read Message History, plus Send Messages in Threads to join |
| Download images and document attachments | An outbound HTTP client that fetches attachments.url |
| Read direct messages between members | Not available to a bot; a bot reads only its own DMs |
Threads, forums and the parts exporters miss
A loop over the channel list does not produce a complete server export, because Discord splits conversation data across several internal models.
Public threads, private threads, and forum posts are independent objects attached to parent channels. Calling /channels/{channel_id}/messages on a parent channel returns the top-level messages and omits every thread spawned from them.
Forum channels hold no top-level chat messages at all. Every entry in a forum is a thread object. An exporter has to call /channels/{channel_id}/threads/archived/public and /guilds/{guild_id}/threads/active to find the thread IDs before fetching their contents.
Voice channels carry text backlogs too. Those text containers share the voice channel ID and need the same pagination calls as a text channel. A backup script that only enumerates channels where type == 0 (Guild Text) misses every conversation held in a voice text window and every forum thread.
For the same job on another collaboration tool, see the walkthrough on exporting Slack messages on free plans.
How long a full server export runs
Two published limits set the shape of the job. The messages endpoint returns 100 objects per call, and a bot has a global ceiling of 50 requests per second. The per-route bucket on the messages endpoint is tighter than the global ceiling, so plan on a fraction of it.
From there the arithmetic is yours to do. A channel holding 400,000 messages needs 4,000 sequential requests. A single channel cannot be split across parallel workers, because each page needs the oldest snowflake ID from the previous payload to set before. Parallelism comes from running workers on different channel IDs at the same time, bounded by the global limit.
Media download, rather than text pagination, is what makes a first export long. Each attachment is a separate HTTP GET against Discord's content delivery network, so the attachment count and the average attachment size set the length of the run. To size it, multiply those two and divide by your available downstream bandwidth. Both numbers come from your own server, so measure them rather than borrowing an estimate.
A later run is a delta rather than a backfill. Once your database stores the newest snowflake ID for each channel, the next job queries only messages with higher IDs using the after parameter. A nightly job handles a small delta, keeps egress low, and captures records before users delete them.
Reading the export once you have it
A directory of JSON files is hard to query during an audit. Running grep across the export finds an isolated keyword, and it fails when a legal team asks which agent approved a specific contract concession last March. Answering that means resolving author IDs to names and rebuilding threaded replies.
Three steps make raw export files usable:
- Ingest the data into SQLite, DuckDB, or an Elasticsearch index. Flat files cannot do relational lookups between parent channels and child threads.
- Resolve user snowflake IDs against a server member directory. Discord payloads carry the author ID as an integer, so your pipeline joins that integer against a cached user table to show names, current server nicknames, and corporate email addresses.
- Rewrite the attachment URLs in your records to point at your own storage buckets rather than Discord CDN links, which expire.
A Discord snowflake ID encodes its creation timestamp inside the 64-bit integer. You can sort records chronologically and filter date ranges on the numeric value alone, which avoids parsing ISO 8601 strings while indexing millions of rows.
What a compliance team needs
A JSON dump on local storage does not satisfy corporate compliance, SOC 2, or a legal hold.
Discord API message snowflake (e.g. 1198249102938102)
│
├── Must map to: Maya Chen (maya.chen@northstar.example)
├── Must verify: attachment hash matches the disk copy
└── Must record: edit history before deletion
A communications archive that holds up meets five technical criteria:
- Thread completeness. The archive links forum posts and nested threads to their parent records, so the context survives.
- Attachment custody. Files uploaded to Discord are served through a CDN with expiring signatures, so an export script fetches each payload at run time and stores it beside the message metadata.
- Modification history. The Discord API returns only the current state of a message. When a team member edits a message, a later export shows the edited text, so a compliance pipeline logs the edit events as they occur.
- Identity mapping. Discord identifiers are snowflake integers and display nicknames, so an archive maps them to a verified employee directory and corporate email addresses.
- Deletion tracking. A deleted message leaves the API immediately, and a quarterly bulk export cannot recover it.
Point-in-time exports leave gaps where deleted and modified messages used to be.
A schedule that works
When you manage backups through custom exporters and file storage, run a consistent schedule:
- Run export jobs daily through cron or a container task. Frequent runs shorten the window in which a deletion goes unrecorded.
- Download and verify message attachments on every run. Store the files on immutable object storage with versioning enabled.
- Diff each new export against the previous data set to catch edits, deletions, and metadata changes.
- Move archives out of your Discord infrastructure into secure cloud storage inside your primary legal jurisdiction.
Moving past static exports
Export scripts need maintenance as platform APIs change, and the file dumps they produce leave your history spread across drives, which makes both legal discovery and daily search slow.
Connecting your server to CommunicationOS keeps an index of your public channels, private discussions, and threads that updates as messages arrive. Discord sits in the same search as WhatsApp, Telegram, Slack, and the rest of the 19 networks, with one identity per person across them. What you have already extracted is not wasted. Upload the JSON that DiscordChatExporter writes, or the archive a data request returns, and those messages join the same index as the ones arriving live, with the forum and thread structure kept. When a legal or operations team wants offline records, you produce a structured data export. The pricing plans show what each tier covers.
Related guides
Trengo alternatives in 2026: what the six real options cost
Trengo charges EUR 299 a month on annual billing for 10 users and 6,000 conversations a year. Here is the arithmetic on Trengo, Front, Intercom, Zendesk, Respond.io and CommunicationOS for a 15-person team over 12 months.
How to Export WhatsApp Chat History in 2026, and What the Export Leaves Out
The ways to get WhatsApp messages out of the app: the per-chat export, the message cap that truncates long threads without warning, why Google Drive and iCloud backups are not exports, and why the Business API has no history to give you.
What a shared inbox costs when conversations are metered
Conversation metering ties your invoice to customer behaviour. How the 24-hour and 7-day windows work, three worked cost scenarios on Trengo's published rates, and a ten-point audit of any vendor pricing page.