# Discord Server Scraper — Messages, Members & Channels (`khadinakbar/discord-server-scraper`) Actor

Export a Discord server via your bot token: messages, members, channels, roles & full structure. HTTP-only, no proxy. MCP-ready Apify Actor.

- **URL**: https://apify.com/khadinakbar/discord-server-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Social media, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 member scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Discord Server Scraper

Export an entire Discord server with your own **bot token** — messages, members, channels, roles and full structure — into one clean dataset. HTTP-only, no browser, no proxy. **MCP-ready** for Claude, ChatGPT and other AI agents.

### What it does

Point it at a Discord **server (guild) ID** plus a **bot token** for a bot that is already a member of that server, and it exports any combination of:

| Resource | What you get |
|---|---|
| **Server** | Name, description, member & online counts, owner, features, verification level, boost tier, vanity URL, icon/banner, creation date |
| **Channels & threads** | Full channel/category/forum/thread tree — name, type, position, parent, topic, NSFW flag, slow-mode |
| **Roles** | Name, color, position, hoist/mention/managed flags, permission bitfield |
| **Members** | Username, global & display name, nickname, roles, join date, boost-since, bot flag, avatar (needs the *Server Members Intent*) |
| **Messages** | Author, content, timestamp, edits, reactions, attachments, embeds, reply-to, pinned — per channel, newest first |

Every record carries a `recordType` field (`server`, `channel`, `role`, `member`, `message`) so you can split one dataset into five tables.

### When to use it

- **Community managers & server owners** — export/backup/audit your own server.
- **Analytics** — member growth, role distribution, most-active channels.
- **Migration** — snapshot a server before a restructure or move.
- **Research & moderation** — pull message history for a channel into a spreadsheet or LLM.

> This is the **authenticated, deep-export** companion to [Discord All-in-One Scraper](https://apify.com/khadinakbar/discord-all-in-one-scraper), which resolves invites and searches public directories **without** a token. Use that one for public recon; use this one to export a server your bot can access.

### Setup — get a bot token (2 minutes)

1. Open the [Discord Developer Portal](https://discord.com/developers/applications) → **New Application**.
2. **Bot** tab → **Reset Token** → copy the token. Paste it into the **Discord bot token** field here (stored encrypted, masked in logs).
3. To scrape **members**, enable **Privileged Gateway Intents → Server Members Intent** on that same Bot tab.
4. **OAuth2 → URL Generator** → scope `bot`, permissions *View Channels* + *Read Message History* → open the URL → add the bot to your server.
5. In Discord, enable **Settings → Advanced → Developer Mode**, then right-click the server icon → **Copy Server ID**. Paste it into **Server (guild) ID**.

The bot must be a **member** of the server — Discord exposes no way to read a server a bot has not joined.

### Output

Each dataset item is a flat JSON record. Example **member** and **message** records:

```json
{
  "recordType": "member",
  "userId": "80351110224678912",
  "username": "nelly",
  "globalName": "Nelly",
  "displayName": "Nelly",
  "isBot": false,
  "roleIds": ["41771983423143936"],
  "joinedAt": "2024-03-04T18:22:11.000Z",
  "avatarUrl": "https://cdn.discordapp.com/avatars/80351110224678912/8342.png",
  "serverId": "613425648685547541"
}
```

```json
{
  "recordType": "message",
  "id": "1099999999999999999",
  "channelId": "974519864045756446",
  "channelName": "general",
  "authorUsername": "nelly",
  "content": "ship it",
  "createdAt": "2026-02-01T12:00:00.000Z",
  "reactions": [{ "emoji": "rocket", "count": 5 }],
  "attachmentUrls": [],
  "replyToId": null,
  "serverId": "613425648685547541"
}
```

### Pricing — Pay Per Event

| Event | Price | When |
|---|---|---|
| Actor start | $0.00005 | Once per run (per GB RAM) |
| **Member scraped** | **$0.003** | Per member returned |
| **Message scraped** | **$0.002** | Per message returned |

Server, channel and role records are **free** — you only pay for members and messages. A run that exports 1,000 members and 2,000 messages costs about **$7** (1000 x $0.003 + 2000 x $0.002). Set `maxMembers` / `maxMessagesPerChannel` to cap spend; the actor prints the worst-case cost before charging and stops cleanly if you set a run cost limit.

### Input reference

| Field | Type | Default | Notes |
|---|---|---|---|
| `botToken` | string (secret) | — | Discord **bot** token. Required. |
| `serverId` | string | — | Numeric guild ID. Required. |
| `resources` | array | all five | `server`, `channels`, `roles`, `members`, `messages` |
| `channelIds` | array | all readable | Restrict messages to these channel IDs |
| `maxMessagesPerChannel` | integer | 100 | 0 to skip messages; max 10000 |
| `maxMembers` | integer | 1000 | 0 to skip members; max 100000 |
| `messagesNewerThan` | string | — | ISO date; only newer messages |
| `includeThreads` | boolean | true | Include active threads |

### Use from code

**JavaScript (apify-client):**

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('khadinakbar/discord-server-scraper').call({
    botToken: 'YOUR_DISCORD_BOT_TOKEN',
    serverId: '613425648685547541',
    resources: ['members', 'channels'],
    maxMembers: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.recordType === 'member'));
```

**Python (apify-client):**

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("khadinakbar/discord-server-scraper").call(run_input={
    "botToken": "YOUR_DISCORD_BOT_TOKEN",
    "serverId": "613425648685547541",
    "resources": ["messages"],
    "maxMessagesPerChannel": 200,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["recordType"], item.get("content"))
```

### MCP / AI agents

Exposed as `apify--discord-server-scraper` in the [Apify MCP server](https://mcp.apify.com). An agent passes `serverId` + `botToken`, picks `resources`, and receives structured JSON it can reason over — member rosters, message history, channel maps.

### FAQ

**Do I need a bot token?** Yes. Discord blocks anonymous reads of server members and messages. For token-free public data (invite resolution, directory search, member counts) use [Discord All-in-One Scraper](https://apify.com/khadinakbar/discord-all-in-one-scraper) instead.

**Members come back empty / skipped.** Enable the **Server Members Intent** on your bot (Developer Portal → Bot → Privileged Gateway Intents). The actor reports this as a warning and still returns everything else.

**A channel returned no messages.** The bot lacks *Read Message History* there. The channel is skipped with a warning; other channels continue.

**Can it scrape a server my bot is not in?** No. Add the bot to the server first.

**Is this a self-bot?** No. It uses a proper **Bot** token via Discord's official REST API. Never use a user token — that violates Discord's Terms of Service.

### Legal

Use only with bot tokens you control and on servers you are authorized to access. You are responsible for complying with [Discord's Terms of Service](https://discord.com/terms), [Developer Terms](https://discord.com/developers/docs/policies-and-agreements/developer-terms-of-service), and applicable privacy laws (GDPR/CCPA) when handling member and message data. This actor does not bypass any access control — it only reads what the supplied bot is already permitted to see. Not affiliated with Discord Inc.

# Actor input Schema

## `botToken` (type: `string`):

Your Discord bot token (Developer Portal -> Applications -> your app -> Bot -> Reset Token). The bot must already be a member of the target server. REQUIRED. Example format: 'MTA1Nz...long.string'. Stored encrypted and masked in logs. This is a Bot token, NOT a user token (self-bot scraping violates Discord ToS) and NOT the application Client Secret.

## `serverId` (type: `string`):

The Discord server (guild) ID to export. Enable Developer Mode in Discord (Settings -> Advanced), then right-click the server icon -> Copy Server ID. REQUIRED. Example: '613425648685547541'. The bot named in botToken must be a member of this server. NOT a channel ID, NOT an invite code or URL.

## `resources` (type: `array`):

Which data types to export from the server. 'server' returns the server profile (name, member count, owner, features, boosts). 'channels' returns the full channel/category/thread tree. 'roles' returns the role list. 'members' returns the member roster (needs the SERVER MEMBERS INTENT enabled on the bot). 'messages' returns channel message history. Defaults to all five. Charged events fire only for members and messages.

## `channelIds` (type: `array`):

Restrict message scraping to these channel IDs. Right-click a channel -> Copy Channel ID (Developer Mode on). Example: \['974519864045756446']. Leave empty to scrape messages from every text and announcement channel the bot can read. Only used when 'messages' is in the resources list. NOT server IDs.

## `maxMessagesPerChannel` (type: `integer`):

Upper bound on messages fetched from each channel, newest first. Each message returned costs one 'message-scraped' event. Keep small on first runs to verify output before scaling. Set 0 to skip message bodies entirely. Defaults to 100. Max 10000.

## `maxMembers` (type: `integer`):

Upper bound on members fetched from the roster. Each member returned costs one 'member-scraped' event. Discord pages members 1000 at a time. Set 0 to skip the roster. Defaults to 1000. Max 100000.

## `messagesNewerThan` (type: `string`):

ISO 8601 date or datetime; messages older than this are skipped. Example: '2026-01-01' or '2026-01-01T00:00:00Z'. Because messages are fetched newest-first, this also stops pagination early once older messages are reached. Leave empty for no lower time bound. Only used when 'messages' is scraped.

## `includeThreads` (type: `boolean`):

When true (default), active threads are listed as channels and, when scraping messages, their messages are included too. Disable to scrape only top-level text channels. Archived threads are not fetched in this version.

## Actor input object example

```json
{
  "serverId": "613425648685547541",
  "resources": [
    "server",
    "channels",
    "roles",
    "members",
    "messages"
  ],
  "maxMessagesPerChannel": 100,
  "maxMembers": 1000,
  "includeThreads": true
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "serverId": "613425648685547541",
    "resources": [
        "server",
        "channels",
        "roles",
        "members",
        "messages"
    ],
    "maxMessagesPerChannel": 100,
    "maxMembers": 1000,
    "includeThreads": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/discord-server-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "serverId": "613425648685547541",
    "resources": [
        "server",
        "channels",
        "roles",
        "members",
        "messages",
    ],
    "maxMessagesPerChannel": 100,
    "maxMembers": 1000,
    "includeThreads": True,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/discord-server-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "serverId": "613425648685547541",
  "resources": [
    "server",
    "channels",
    "roles",
    "members",
    "messages"
  ],
  "maxMessagesPerChannel": 100,
  "maxMembers": 1000,
  "includeThreads": true
}' |
apify call khadinakbar/discord-server-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/discord-server-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/oPWwqhqx8nv1Tuery/builds/uXhgJ8IFXUntEHIEu/openapi.json
