Tutorial
How to Scrape Discord Server Data
Learn how to collect Discord server data including messages, members, and channels using Python bots and the Discord API.
Discord servers contain valuable data for community analysis, market research, and trend monitoring. The most reliable approach uses the official Discord API through a bot account.
Setting Up a Discord Bot
First, create a bot at https://discord.com/developers/applications:
- Create a new application
- Go to the Bot section and create a bot
- Enable the "Message Content Intent" under Privileged Gateway Intents
- Generate an invite link with appropriate permissions
- Invite the bot to your target server
pip install discord.py
Scraping Messages with discord.py
import discord
import json
import asyncio
from datetime import datetime, timedelta
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f"Bot connected as {client.user}")
guild = client.guilds[0] # First server the bot is in
messages_data = []
for channel in guild.text_channels:
try:
async for message in channel.history(limit=1000):
messages_data.append({
"channel": channel.name,
"author": str(message.author),
"content": message.content,
"timestamp": str(message.created_at),
"reactions": [str(r.emoji) for r in message.reactions]
})
except discord.Forbidden:
print(f"No access to #{channel.name}")
with open("discord_messages.json", "w") as f:
json.dump(messages_data, f, indent=2)
print(f"Scraped {len(messages_data)} messages")
await client.close()
client.run("YOUR_BOT_TOKEN")
Scraping Server Members
@client.event
async def on_ready():
guild = client.guilds[0]
members = []
async for member in guild.fetch_members(limit=None):
members.append({
"id": member.id,
"name": str(member),
"joined": str(member.joined_at),
"roles": [role.name for role in member.roles],
"bot": member.bot
})
print(f"Found {len(members)} members")
await client.close()
Scraping Specific Channels by Date Range
from datetime import datetime, timezone
async def scrape_channel_range(channel, start_date, end_date):
messages = []
async for message in channel.history(
after=start_date,
before=end_date,
oldest_first=True
):
messages.append({
"content": message.content,
"author": str(message.author),
"timestamp": str(message.created_at),
"attachments": [a.url for a in message.attachments],
"embeds": [e.to_dict() for e in message.embeds]
})
return messages
# Usage in on_ready:
# start = datetime(2026, 1, 1, tzinfo=timezone.utc)
# end = datetime(2026, 4, 1, tzinfo=timezone.utc)
# data = await scrape_channel_range(channel, start, end)
Rate Limits and Best Practices
Discord enforces strict rate limits:
- 50 requests per second per bot globally
- 5 messages per 5 seconds per channel for sending
- History fetches are limited to 100 messages per API call
Best practices:
- Add delays between channel scrapes (
await asyncio.sleep(1)) - Use
afterandbeforeparameters for incremental scraping - Store progress to resume if interrupted
- Only collect data from servers where you have proper authorization
Important Legal Note
Only scrape Discord servers where you have permission or where you operate the server. Scraping private servers without consent violates Discord's Terms of Service and may violate privacy laws.