> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scrape.st/llms.txt
> Use this file to discover all available pages before exploring further.

# Discord

> Monitor Discord servers for real-time messages and token mentions

Track Discord servers in real-time. Capture messages, token mentions, and embeds across the server's channels — then backfill historical data on demand.

## What You Can Do

| Capability          | Endpoint                   | Description                               |
| ------------------- | -------------------------- | ----------------------------------------- |
| **Track Server**    | `POST /track/discord`      | Monitor a Discord server for new messages |
| **Get Source**      | `GET /track/discord`       | Retrieve a tracked Discord source         |
| **Delete Tracking** | `DELETE /track/discord`    | Stop tracking a Discord source            |
| **Backfill**        | `PUT /backfill`            | Trigger historical message backfill       |
| **History**         | `GET /account/history/:id` | Retrieve backfilled historical messages   |

## Track a Discord Server

You track a Discord **server** by passing a **server invite** as the `sid`. Scrapest resolves the invite to the underlying server (guild) and monitors messages across its channels.

The `sid` accepts any of these forms:

* A bare invite code — `abc123`
* A short URL — `discord.gg/abc123`
* A full URL — `https://discord.com/invite/abc123`

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://scrape.st/track/discord \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"sid": "discord.gg/abc123"}'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch("https://scrape.st/track/discord", {
    method: "POST",
    headers: {
      "x-api-key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ sid: "discord.gg/abc123" }),
  });
  const data = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      "https://scrape.st/track/discord",
      headers={"x-api-key": "YOUR_API_KEY"},
      json={"sid": "discord.gg/abc123"},
  )
  print(res.json())
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "data": {
    "id": "src_789",
    "source": "DISCORD",
    "externalId": "1234567890123456789",
    "name": "Alpha Callers"
  },
  "message": "Now tracking 1234567890123456789 on discord"
}
```

<Note>
  The `externalId` returned is the **server (guild) ID** the invite resolved to
  — not a channel ID. Use it (or the internal `id`) as the `eid`/`iid` when you
  later call `DELETE /track/discord`. Invite resolution fails with `Invalid or
      expired Discord invite` if the invite is dead, so prefer a non-expiring
  invite.
</Note>

### How Joining Works (Human-in-the-Loop)

Scrapest monitors servers using real Discord accounts. To avoid the rate-limiting and bans that automated joins trigger, **joining and leaving a server is a human-in-the-loop step** — it is not automatic.

Your `POST` (or `DELETE`) call succeeds immediately and the source is recorded. If Scrapest isn't already monitoring that server, the join is handed off to a human-in-the-loop review before the account joins.

Because joining is gated on that review, expect a short delay between creating the tracking and the first messages arriving for a server Scrapest hasn't joined before. Servers Scrapest is already in start streaming immediately. Leaving is only requested once no other API key is still tracking that server.

You're notified of the outcome on your **admin dashboard** — and via **Telegram** if you've opted in to Telegram notifications:

* **Tracking is live** — the account joined the server and messages are now streaming.
* **Could not be set up** — the server couldn't be joined, so the tracking you created is automatically removed. Check the invite is valid and try again.

## Token & Cashtag Monitoring

Scrapest automatically detects token mentions and cashtags in Discord messages — `$BTC`, `$SOL`, `$ETH`, and any custom token symbol. This works the same way as [X token search](/sources/x#search-posts), applied to your tracked Discord sources.

Messages containing token references are flagged in the webhook payload for easy filtering.

## Backfill History

Retrieve historical messages from tracked Discord servers.

<Note>
  In the examples below, `src_789` is the **internal ID** (the Discord source
  cuid) of the server whose historical data you want to pull. This is the same
  `id` returned when you created the tracking via `POST /track/discord`.
</Note>

```bash theme={null}
# Trigger backfill for a Discord source
curl -X PUT https://scrape.st/backfill \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id": "src_789"}'

# Check backfill status
curl -X GET "https://scrape.st/backfill/status?id=src_789" \
  -H "x-api-key: YOUR_API_KEY"

# Retrieve historical messages
curl -X GET "https://scrape.st/account/history/src_789?limit=50" \
  -H "x-api-key: YOUR_API_KEY"
```

## Webhook Payload

When a tracked Discord server receives a message in any of its channels, your webhook receives a `SourceEvent` object. This is the exact shape sent via `JSON.stringify` to your webhook URL:

```json theme={null}
{
  "mid": "1234567890123456789",
  "sid": "9876543210987654321",
  "source": "discord",
  "timestamp": 1712072400000,
  "content": "New $ETH listing coming to Uniswap 🔥",
  "images": ["https://cdn.discordapp.com/attachments/xxx/chart.png"],
  "payload": {
    "id": "1234567890123456789",
    "content": "New $ETH listing coming to Uniswap 🔥",
    "images": ["https://cdn.discordapp.com/attachments/xxx/chart.png"],
    "author": {
      "id": "111222333444555666",
      "username": "alpha_caller",
      "globalName": "Alpha Caller"
    }
  }
}
```

| Field       | Type   | Description                                        |
| ----------- | ------ | -------------------------------------------------- |
| `mid`       | string | Message ID — the Discord message ID                |
| `sid`       | string | Source ID — the Discord **server (guild) ID**      |
| `source`    | string | `"discord"`                                        |
| `timestamp` | number | Unix timestamp (ms) of the message's creation time |
| `content`   | string | Message text                                       |
| `images`    | array  | Image attachment + embed image URLs                |
| `payload`   | object | Flattened `discord.py` `Message` object            |

<Note>
  `payload` is a dynamically flattened representation of the underlying
  `discord.py-self` `Message` — all object IDs are stringified and nested
  objects (author, channel, etc.) are expanded inline. `payload.author` is
  always normalized to include `id`, `username`, and `globalName`. The message's
  channel is available on the nested `channel` object within the payload.
</Note>

## Delivery Methods

Discord data is delivered through all the same channels as X:

* **[Webhooks](/rest-api-reference/create-webhook)** — Push to your endpoint
* **[WebSocket](/streams/websocket-connections)** — Real-time bidirectional stream
* **[SSE](/streams/public-sse)** — Server-sent events with auto-reconnect

## API Reference

All tracking endpoints use `/track/:source` where `:source` is `x`, `telegram`, or `discord`.

* [Create Tracking](/rest-api-reference/create-tracking) — `POST /track/discord`
* [List Tracked Sources](/rest-api-reference/list-tracking) — `GET /track/discord`
* [Delete Tracking](/rest-api-reference/delete-tracking) — `DELETE /track/discord`
* [Trigger Backfill](/rest-api-reference/backfill/trigger) — `PUT /backfill`
* [Backfill Status](/rest-api-reference/backfill/status) — `GET /backfill/status`
* [Get History](/rest-api-reference/backfill/get-history) — `GET /account/history/:id`
