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 only contains messages sent by your own account. To export an entire server channel with conversations from all members, you must use an open-source backup tool or deploy a custom bot with specific permissions. Compliance exports require bots configured with the Message Content intent and channel history access. Exporters missing these permissions will skip message text, threads, and uploaded attachments.
Method 1: Discord's data request
Discord provides a built-in tool under account privacy settings to comply with data privacy regulations like GDPR. This feature packages your personal 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 specific data packages you want to include, then confirm your request.
package/
├── account/
├── activity/
├── messages/
│ ├── c1049281048201/
│ │ └── channel.json
│ │ └── messages.csv
│ └── index.json
└── servers/
Discord takes time to assemble this package. The system sends an email to your registered address containing a download link. Discord states that it can take up to 30 days, though small accounts often see the email inside a week. Plan around the 30 day figure if a deadline is attached.
The primary limitation of this download is its scope. The messages/ folder contains CSV files for every channel you have typed in. These CSVs list only the messages sent by your account. The package leaves out all context, incoming questions from customers, and responses from other team members. It is not a server backup tool.
Method 2: DiscordChatExporter
DiscordChatExporter is an open-source community tool designed to extract complete server channel logs into multiple formats. It provides both a graphical user interface and a command-line interface.
The utility reads channels through the Discord API and writes the data to HTML, JSON, CSV, or plain text files. The HTML export renders an offline interface that replicates Discord's visual style, including theme colours, user roles, markdown formatting, and image embeds.
To run an automated export using the command-line interface, download the executable for your operating system and execute the export command:
./DiscordChatExporter.Cli export \
-t "YOUR_BOT_TOKEN" \
-c 982347109283471029 \
-f Json \
-o "./exports/support-channel.json" \
--media \
--reuse-media
This command queries the channel ID 982347109283471029, outputs the chat history into a structured JSON file, and downloads all attached files into a local folder.
DiscordChatExporter accepts two types of authorization tokens: bot tokens and personal user tokens. Using a personal account user token to run automated exports violates Discord Terms of Service. Automating user tokens can result in permanent account termination. Always create an authorised bot account inside the Discord Developer Portal and add that bot to your server when pulling business records.
Method 3: your own bot
Building your own export bot gives you control over rate limits, data schemas, and destination storage.
A custom bot requires two administrative permissions inside every target channel:
View Channelto see the channel in the server hierarchy.Read Message Historyto pull past messages rather than just streaming new events.
You must also enable the Message Content Intent inside the Discord Developer Portal under the Bot tab. Message Content is a privileged intent. Once your bot sits in 100 servers or more, it has to pass Discord's verification and get the intent approved before it can read message bodies at all.
Discord caps the messages endpoint at 100 messages per request. To pull full channel logs, write a loop that queries the API using the before parameter, setting it to the oldest snowflake ID received in 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 response headers X-RateLimit-Remaining and X-RateLimit-Reset-After provide the exact pacing required by Discord's edge routers. Tracking these headers prevents your scraper from triggering 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 channel override, View Channel |
| Export active and archived threads | Read Message History, Send Messages in Threads for joining |
| Download images and document attachments | Outbound HTTP client pipeline to fetch attachments.url |
| Read direct messages between members | Impossible via bot API; bots can only read their own DMs |
Threads, forums and the pieces exporters miss
A standard loop through the channel list does not produce a complete server export. Discord splits conversation data across several different internal models.
Public threads, private threads, and forum channel posts are independent objects attached to parent channels. When a bot calls the /channels/{channel_id}/messages endpoint on a parent channel, the API returns only top-level messages. It omits all conversation threads spawned from those messages.
Forum channels contain no top-level chat messages at all. Every entry in a forum is a thread object. An exporter must call /channels/{channel_id}/threads/archived/public and /guilds/{guild_id}/threads/active to find thread IDs before fetching their message contents.
Voice channels also contain text message backlogs. These text containers share the voice channel ID and require the same message pagination calls as standard text channels. If your backup scripts only enumerate channels where type == 0 (Guild Text), you will miss all conversations held in voice text windows and forum threads.
For companies managing operations across multiple collaboration tools, see our walkthrough on exporting Slack messages on free plans.
How long a full server export actually takes
The messages endpoint returns 100 objects per call. A channel with 400,000 messages requires 4,000 sequential API requests. You cannot split a single channel across parallel workers because each page request requires the oldest snowflake ID from the previous payload to set the before parameter.
Speed gains come from parallelising across different channels. You can run workers on separate channel IDs simultaneously, bounded by your global bot rate limit.
Downloading media assets consumes most of your total export time. A five-year-old support channel contains far more gigabytes of PNG screenshots, log dumps, and PDF receipts than raw text data. Each attachment requires a separate HTTP GET request to Discord's content delivery network.
Consider a mid-sized server holding 40 active channels and roughly two million total messages:
- Fetching the text records requires 20,000 sequential API requests across your worker pool.
- A bot's global ceiling is 50 requests a second, but the per-route bucket on the messages endpoint is far tighter, so plan on a fraction of that. Text pagination for the whole server lands inside an hour.
- If those two million messages contain 150,000 image and document attachments averaging 500 KB each, your pipeline must download 75 GB of binary data.
- Over a standard 100 Mbps downstream pipe, pulling those media assets takes two to four hours, depending on CDN response latencies and connection pooling.
Expect the first historical export to run for several hours end to end, with the media download owning most of it.
Subsequent runs take much less time. Once your database stores the newest snowflake ID from each channel, your next cron job queries only the messages sent with IDs higher than that saved cursor using the after parameter. An incremental nightly job processes only a few hundred records and finishes in minutes. Nightly jobs keep your egress bandwidth low and capture records before users delete them.
Reading the export once you have it
A raw directory of JSON files is difficult to query during audits. Running grep across hundreds of megabytes of JSON text works for finding isolated keywords, but it fails when your legal team asks which agent approved a specific contract concession last March. To answer that question, you must resolve author IDs to human names and reconstruct threaded replies.
Teams handle raw export files through three practical steps:
- Ingest the data into SQLite, DuckDB, or an Elasticsearch index. Flat files do not support relational lookups between parent channels and child threads.
- Resolve user snowflake IDs against a server member directory. Discord JSON payloads list the author ID as an integer. Your pipeline must join that integer against a cached user table to display real names, current server nicknames, and corporate email addresses.
- Update the attachment URLs in your records to reference your local storage buckets rather than Discord CDN links, which expire and break over time.
Discord snowflake IDs encode the creation timestamp directly within the 64-bit integer. You can sort message records chronologically and filter broad date ranges using the numerical snowflake values alone. This avoids the CPU overhead of parsing ISO 8601 date strings when indexing millions of rows in your analytical store.
What a compliance team actually needs
Saving a JSON dump to local storage does not fulfill corporate compliance, SOC 2, or legal hold obligations.
Discord API Message Snowflake (e.g. 1198249102938102)
│
├── Must map to: Alexandre V. ([email protected])
├── Must verify: Attachment hash matches disk copy
└── Must record: Edit history prior to deletion
A compliant communications archive must address specific technical criteria:
- Thread completeness. The archive must link forum posts and nested conversation threads to their parent records so that contextual meaning remains intact.
- Attachment custody. Files uploaded to Discord route through content delivery networks with expiring signatures. An export script must fetch every file payload at runtime and store the asset alongside the message metadata.
- Modification history. The Discord API returns only the current state of a message. If a team member alters a message, historical exports show only the edited text. A compliance pipeline must log the edit events as they occur.
- Identity mapping. Discord identifiers are arbitrary snowflake integers and display nicknames. An enterprise archive must map these IDs to verified employee directories and corporate email addresses.
- Deletion tracking. When a user deletes a message in Discord, that object disappears from the API immediately. An annual or quarterly bulk export cannot recover deleted records.
Compliance audits require unbroken records of communication. Point-in-time exports leave gaps where deleted or modified messages existed.
A schedule that works
If you manage backups through custom exporters and file storage, run a consistent maintenance schedule:
- Run export jobs daily via scheduled cron jobs or container tasks. Frequent runs minimize the window for unrecorded message deletions.
- Download and verify message attachments during every run. Store these files on immutable object storage with versioning enabled.
- Diff every new export against your previous data sets to catch message modifications, deletions, and metadata shifts.
- Move archives out of your Discord infrastructure into secure cloud storage located within your primary legal jurisdiction.
Moving past static exports
Export scripts and local JSON files require constant maintenance as platform APIs change. File dumps leave your operational history fragmented across different drives, making legal discovery and day-to-day search slow and complicated.
Connecting your server to CommunicationOS maintains an indexed, real-time record of all your public channels, private discussions, and threads. Your Discord communication stays searchable alongside WhatsApp, Telegram, Slack, and fifteen other networks with unified identity mapping. When your legal or operations teams need offline records, you can produce structured data exports instantly. Check our pricing plans to set up automated archiving for your team.
Related guides
How to Export WhatsApp Chat History in 2026: Every Method, and What You Lose
Every way to get WhatsApp messages out of the app: the per-chat export, the 40,000 message ceiling, why Google Drive and iCloud backups are not exports, and what the Business API will never give you back.
9 min read
ComparisonsTrengo alternatives in 2026: what the six real options cost
Trengo charges EUR 299 a month 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.
9 min read
PricingWhat 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, and a ten-step audit of any vendor pricing page.
9 min read