DWEEB
Developer guide · Python

Send a Discord Webhook with Python

A Discord webhook is a URL that accepts an HTTP POST, so Python needs nothing more than the requests library to send a rich message — no bot, no gateway connection, no token. This guide gives you a working script and the three details that decide whether it works.

A working script

Install requests, paste your webhook URL, and run it. The payload is an ordinary Python dictionary; json=payload serialises it and sets the Content-Type header for you. If you do not have a URL yet, the webhook setup guide takes about a minute.

# Components V2 webhook message — designed in DWEEB (https://dweeb.faizo.net).
# pip install requests
import requests

WEBHOOK_URL = "https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN"

payload = {
    "components": [
        {
            "type": 17,
            "accent_color": 5793266,
            "components": [
                {
                    "type": 10,
                    "content": "# Server update\nEverything you need in one place.",
                },
                {
                    "type": 14,
                    "divider": True,
                    "spacing": 1,
                },
                {
                    "type": 1,
                    "components": [
                        {
                            "type": 2,
                            "style": 5,
                            "label": "Read the guide",
                            "url": "https://example.com/update",
                        },
                    ],
                },
            ],
        },
    ],
    "flags": 32768,
}

# with_components=true is required, or Discord silently drops the components.
response = requests.post(
    WEBHOOK_URL,
    params={"with_components": "true", "wait": "true"},
    json=payload,
    timeout=10,
)
response.raise_for_status()

The three details that decide whether it works

Treat the URL as a password: anyone who has it can post to your channel. Read it from an environment variable rather than committing it, and see the webhook security guide if one has already leaked.

  • with_components=true in the query string. A webhook created in Server Settings ignores the components array unless the request opts in. Discord still answers with a success status, so the symptom is a message that arrives empty or not at all.
  • flags: 32768 in the payload. That is the Components V2 flag, 1 << 15. With it set, content and embeds are not accepted in the same payload — visible text goes into type 10 Text Display components.
  • wait=true if you want the message back. Without it Discord answers 204 with no body. With it you get the message object, including the id you need to edit the message later.

Uploading a file with the message

To show a local image or attach a document, send a multipart request instead of JSON. The payload moves into a form field called payload_json, each file goes in a part named files[0], files[1] and so on, and an attachments array in the payload maps each part to the filename its component refers to with attachment://.

Open a message that uses an uploaded image or a File component in DWEEB and the Code tab writes this variant for you, with one files entry per upload and the matching attachments index.

Rate limits and retries

Discord limits how fast one webhook may post. When you exceed it the response is HTTP 429 with a JSON body whose retry_after field says how many seconds to wait. response.raise_for_status() turns that into an exception; a script that posts in a loop should catch it, sleep for retry_after, and send the same request again rather than dropping the message.

The exact numbers, and the size and component ceilings that apply to the payload itself, are in the webhook limits guide.

When the request is rejected

  • 400 with a field path in the body: the payload broke one of Discord's rules. The path names the component, for example components.0.components.2. The error guide explains how to read it.
  • 401 or 404: the URL is wrong, or the webhook was deleted. Create a new one.
  • A success status but nothing in the channel: with_components=true is missing.
  • Buttons with a custom_id are refused: only link buttons work on a webhook your own application does not own, because nothing could receive the click.

Writing a bot instead?

If the message is sent by a bot rather than a webhook URL, use the library's own classes: see Components V2 in discord.py. The component tree is identical; only the way it is sent differs.

Either way, you do not have to write the payload by hand. Build the message in DWEEB's visual editor, check it in the live preview, and the code generator exports it as this exact script.

Put the guide into practice

Open the exact workflow in DWEEB. Nothing posts until you review and confirm it.

Build the message and export the Python script →

Primary sources

Keep learning