Reddit APIReddit DMREST APITutorialPRAW Alternative

How to Send a Reddit DM via REST API in 2026 (with Code)

Send Reddit DMs via REST API. Bearer token, JSON body, $0.025 per call. Working code in curl, Python, and Node.js. PRAW alternative for AI agents.

RedditAPI·
Reddit DM API tutorial showing a bearer-token REST request with code in curl, Python, and Node.js

To send a Reddit DM via REST API, POST to https://api.redditapis.com/api/reddit/dm with a JSON body of {to, subject, text} and an Authorization: Bearer <key> header. Each call costs $0.025. Sign up for a $0.50 free credit. No developer-app review, no OAuth dance, no 4-week approval queue.

This guide covers the two distinct Reddit DM surfaces, working code in three languages, the full error table, deliverability rules, and how to wire the endpoint as a tool for an AI agent or MCP server. Every claim links to its source.

The two Reddit DM surfaces, and why the difference matters

Reddit has two distinct direct-message surfaces. Mixing them up is the #1 reason developer DM scripts return INVALID_USER or 400 missing field. The active r/redditdev thread "Are the new API endpoints for chat available yet?" walks through this confusion in detail.

r/redditdev·u/mgsecure

Are the new API endpoints for chat available yet?

With the change to modmail replies being sent as chat, I have an application that no longer works. The basic function of the app is: • Have the user authenticate (with description of what is going to happen) • Fetch…

828
Open on Reddit
Surface What it is Reddit endpoint Lands in PRAW supports?
Legacy private message Subject + body, persisted POST /api/compose (OAuth, privatemessages scope) Inbox under "Messages" Yes, redditor.message(subject, body)
Reddit chat DM Real-time, subjectless Internal websocket endpoint, not part of the documented OAuth API (Reddit Data API surface) Chat panel No, PRAW has no chat support (see the r/redditdev "Chat API" thread)

The third-party redditapis.com REST endpoint targets the chat DM surface. That is the surface most 2026 outreach, lead-gen, and AI-agent workflows want, because it is what real Reddit users now reach for first. Legacy PMs still work but they sit in a less-checked inbox.

If you need the legacy PM specifically (compliance archive, ToS-bound communication, audit trail), use PRAW or the official Reddit Developer Platform directly. For everything else, the REST path below is faster to ship and cheaper to operate.

Three paths to send a Reddit DM

Path Auth model Setup time Cost per DM Best for
PRAW + Reddit OAuth Your real Reddit account, privatemessages scope 30 min, then 2-4 weeks if commercial $0, personal account use Personal scripts, academic research
Reddit Commercial Data API App review + commercial agreement 2-4 weeks approval Bundled into $12K/year minimum Enterprises that already cleared procurement
redditapis.com REST Bearer token + your own Reddit session 30 seconds, signup only $0.025 per DM AI agents, outreach pipelines, indie products

The REST path is what the rest of this guide covers. See the alternatives page for the full matrix and the cost calculator for a plug-in-your-volume estimate. The r/redditdev community has been actively discussing this exact tradeoff since the Nov-2025 Responsible Builder Policy tightening.

The reason a third-party REST path for Reddit exists at all traces back to the 2023 API repricing. Apollo founder Christian Selig posted the call notes after Reddit set the new rate at $12,000 per 50 million requests. Apollo, which ran 7 billion requests a month, was now looking at $1.7M a month, or roughly $20M a year. The post was viewed over a million times. Apollo shut down on June 30, 2023. The pricing stayed. Builders who still need programmatic Reddit access now route through endpoints like the one this guide covers.

Christian Selig

Christian Selig

@ChristianSelig

Just got off a call with Reddit about the API and new pricing. Bad news unless I come up with 20 million dollars (not joking). Appreciate boosts. https://t.co/FliuNCinpZ

Quick start: send your first Reddit DM in 60 seconds

Sign up at /signup, grab your API key from the dashboard, and run this curl:

curl -X POST https://api.redditapis.com/api/reddit/dm \
  -H "Authorization: Bearer $REDDITAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "spez",
    "subject": "Hello from the API",
    "text": "Sent via redditapis.com in one HTTP call."
  }'

You will see a 200 response with the message ID. The full endpoint reference, including the cookie and CSRF fields needed for chat-DM delivery, lives at docs.redditapis.com/docs/dm/dm. The /api/reddit/login endpoint returns those session fields when you authenticate a Reddit account once via docs.redditapis.com/docs/auth/login.

Code in three languages

Python with requests

import os
import requests

API_KEY = os.environ["REDDITAPI_KEY"]
BASE = "https://api.redditapis.com"

def send_reddit_dm(to: str, subject: str, text: str):
    response = requests.post(
        f"{BASE}/api/reddit/dm",
        json={"to": to, "subject": subject, "text": text},
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=15,
    )
    response.raise_for_status()
    return response.json()

result = send_reddit_dm("spez", "Hello", "Sent from Python.")
print(result)

Node.js with fetch

const send_reddit_dm = async (to, subject, text) => {
  const response = await fetch("https://api.redditapis.com/api/reddit/dm", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.REDDITAPI_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ to, subject, text }),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
};

const result = await send_reddit_dm("spez", "Hello", "Sent from Node.");
console.log(result);

curl in a bash loop

for handle in alice bob carol; do
  curl -s -X POST https://api.redditapis.com/api/reddit/dm \
    -H "Authorization: Bearer $REDDITAPI_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"to\":\"$handle\",\"subject\":\"Hi\",\"text\":\"Hi $handle, sent via API.\"}"
  sleep 30
done

The 30-second spacing is deliverability hygiene, not a rate-limit requirement. See the deliverability section below.

Start building with RedditAPI

Reads $0.002, votes $0.005, writes $0.012, DMs $0.025. $0.50 free credits.

Authentication: how the bearer token works

Two credential layers stack here. Keep them separate in your head.

Layer 1: redditapis.com bearer token. Sign up at /signup, generate an API key, set Authorization: Bearer <key> on every request. This proves the request comes from a paying redditapis.com account. The key never expires unless you rotate it.

Layer 2: Reddit-side session. The chat DM endpoint needs a logged-in Reddit user session to actually deliver. You authenticate once via POST /api/reddit/login (see docs.redditapis.com/docs/auth/login) with a username, password, and optional 2FA token. The response returns the cookies, loid, and CSRF token your DM calls then include in the body. You bring the Reddit credentials; redditapis.com handles the cookie machinery so you do not have to parse session headers yourself.

This is the customer-brings-own-credentials model. The customer is the user-of-record on Reddit's side; redditapis.com is the REST broker. No Reddit accounts are stored on redditapis.com infrastructure.

Pricing: what each Reddit DM actually costs

Four call types, four prices:

Call Price
Any GET (reads, listings, search, user profile) $0.002
POST /vote $0.005
Write POST (comments, login, profile updates) $0.012
POST /dm* (DM send, threads, messages) $0.025
/account/* (balance, payment history) Free

A 1,000-DM campaign costs $25.00 in API fees. Compare this to:

  • Reddit Commercial Data API: $12,000 per year minimum plus roughly $0.024 per call.
  • Apify Reddit Chat Sender: per-event billing with a setup fee per run, plus residential proxy markup.

The interactive calculator does the math for your exact mix of reads, comments, votes, and DMs.

Error codes you will hit, and how to handle each

Status Cause Fix
400 Missing to, subject, text, or required session field Validate the request body before sending
401 Missing or malformed Authorization header Check Authorization: Bearer <key> is set
403 Invalid or revoked bearer token Rotate the key in the dashboard
429 RATELIMIT Per-account pacing limit Pause the loop, then resume from the same account; see pacing notes
502 INVALID_USER Recipient does not exist, blocked you, or restricts DMs to friends Skip the recipient, log and move on
502 BAD_GATEWAY Upstream Reddit timeout Retry with exponential backoff (this is safe, no duplicate side effect)
500 Generic server error Retry once, then escalate

A 429 is not retry-without-changes territory; pause the loop and let the per-account pacing window reset before resuming. A 502 BAD_GATEWAY is safe to retry with backoff because no DM was delivered.

Production patterns: batch send with exponential backoff

import os
import time
import requests
from requests.exceptions import HTTPError

API_KEY = os.environ["REDDITAPI_KEY"]
BASE = "https://api.redditapis.com"

def send_with_retry(to: str, subject: str, text: str, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            response = requests.post(
                f"{BASE}/api/reddit/dm",
                json={"to": to, "subject": subject, "text": text},
                headers={"Authorization": f"Bearer {API_KEY}"},
                timeout=15,
            )
            response.raise_for_status()
            return response.json()
        except HTTPError as e:
            status = e.response.status_code
            if status in (502, 503, 504) and attempt < max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            if status == 429:
                # Pacing window hit. Pause and let it reset before resuming.
                raise
            raise

recipients = [("alice", "Hi alice"), ("bob", "Hi bob"), ("carol", "Hi carol")]
for handle, body in recipients:
    try:
        send_with_retry(handle, "Hello", body)
        time.sleep(30)  # deliverability spacing
    except HTTPError as e:
        print(f"skipped {handle}: {e.response.status_code}")

Three patterns to internalize: backoff only on 5xx transient errors, never retry on 429 (pause and let the pacing window reset), and space deliverability-sensitive calls comfortably apart.

The cheapest Reddit API. Try it free.

Reads from $0.002 per call. $0.50 free credits. No credit card required.

Deliverability: what makes Reddit accounts ready for outreach

A few characteristics consistently separate accounts that send reliably from accounts that don't. Treat this as a positive checklist for account-quality, not as a how-to-game-the-system guide. The full operational playbook lives in the DM endpoint reference for logged-in customers.

  1. Real participation history. Accounts that have been used as accounts (commenting, voting, subscribing to subreddits, occasionally posting) send reliably. The history doesn't need to be huge; it does need to be real.
  2. Healthy karma mix. Accounts with both comment karma and link karma read as participating members of communities. Either alone is thinner signal.
  3. Varied message bodies. A template with several spintax slots, or per-recipient text from a small language model, works far better than identical bodies across many sends. A mid-tier LLM can rewrite the same 80-word pitch into hundreds of unique variants in under a minute.
  4. Sensible pacing. Match the rhythm of an active human account. The endpoint-specific pacing recommendations live in the docs.
  5. Context-match the recipient. Pitching a crypto product to r/PowerWashingPorn subscribers reads as off-context. A simple recipient classifier (last few subreddits commented, comment-karma distribution) keeps your outreach landing in front of people who are actually interested.

The canonical r/redditdev conversation on the wider DM context is worth reading before any commercial workload.

"Is it safe to send DMs via Reddit API to users who opt-in? I have a list of users who explicitly subscribed to receive updates, and I want to know if there's a clean ToS-compliant way to do this at any kind of volume."

Read the full thread on r/redditdev: "Is it safe to send DMs via Reddit API to users who opt-in" for the community's answers on opt-in lists, suppression files, and pacing recommendations. The earlier r/redditdev thread "How to send private message usage Reddit API" covers the PRAW-specific failure modes for the same workflow.

The community consensus across those threads and a half-dozen sibling discussions: opt-in delivery against an audience that asked to hear from you is consistently the highest-ROI shape.

The redditapis.com REST endpoint is a credential broker. Compliance with Reddit's User Agreement and platform policy lives with you, the customer. RedditAPI is an independent third-party service and is not affiliated with Reddit, Inc.

Sending Reddit DMs from an AI agent or MCP server

The REST shape of POST /api/reddit/dm maps directly to an MCP tool definition:

{
  "name": "send_reddit_dm",
  "description": "Send a private message to a Reddit user. Cost: $0.025 per call.",
  "input_schema": {
    "type": "object",
    "properties": {
      "to": {
        "type": "string",
        "description": "Recipient Reddit username, no u/ prefix"
      },
      "subject": {
        "type": "string",
        "description": "Subject line, 1-100 chars"
      },
      "text": { "type": "string", "description": "Message body, 1-10000 chars" }
    },
    "required": ["to", "subject", "text"]
  }
}

Wire that into the Anthropic tool-use API, the OpenAI function-calling API, the Vercel AI SDK, or any LangChain agent and the model can send DMs the same way it calls any other tool. Pair it with send_reddit_comment (see docs.redditapis.com/docs/write/comment) and get_subreddit_posts and you have a complete read-write-respond agent surface for Reddit, billed per call.

If the tool-definition shape above is new ground, the Anthropic engineering team walked through the Model Context Protocol fundamentals (input schema, server bindings, transport) in their MCP overview, which is the canonical pattern for wiring any HTTP endpoint into a Claude-driven agent loop:

A concrete Anthropic tool-use call site looks like this:

import os, anthropic, requests

API_KEY = os.environ["REDDITAPI_KEY"]

def execute_send_reddit_dm(args: dict) -> dict:
    r = requests.post(
        "https://api.redditapis.com/api/reddit/dm",
        json={"to": args["to"], "subject": args["subject"], "text": args["text"]},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    return r.json() if r.ok else {"error": r.text, "status": r.status_code}

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=[{"name": "send_reddit_dm", "description": "Send a Reddit DM. $0.025 per call.",
            "input_schema": {"type": "object", "properties": {
                "to": {"type": "string"}, "subject": {"type": "string"}, "text": {"type": "string"}},
                "required": ["to", "subject", "text"]}}],
    messages=[{"role": "user", "content": "DM u/spez a thank-you for the API."}],
)
# Inspect response.content for tool_use blocks, run execute_send_reddit_dm, return tool_result

The same shape ports to OpenAI's function-calling, the Vercel AI SDK, LangChain's Tool interface, and any MCP server that exposes a tools/call handler. Budget your agent's per-task cost by counting tool invocations: 10 DMs equals $0.25, 100 DMs equals $2.50, 1,000 DMs equals $25.

The r/redditdev community has been working through the same pattern for PRAW users:

"Send messages to chat with reddit praw, has anyone figured out the bridge from PRAW's legacy message API to the new chat surface? Trying to wire this into an agent."

See the r/redditdev thread "Send messages to chat with reddit praw" for the community attempts, and the r/SaaS "Automated Hyper-personalized DMs on Reddit" thread for a builder shipping the agent end-to-end.

For accepted-answer-on-record on the underlying Reddit-API DM mechanics, the canonical Stack Overflow Q&A "Can the REDDIT Api be used to send a PM to another user" carries the legacy-PM details.

This pairs naturally with the existing Reddit API in Python tutorial which covers the read endpoints the same agent will need.

Migrating from PRAW

If you already have a PRAW-based script using redditor.message(subject, body), the migration is a 10-line shim. Replace this:

import praw
reddit = praw.Reddit(client_id="...", client_secret="...", username="...", password="...", user_agent="...")
reddit.redditor("spez").message(subject="Hi", message="Sent from PRAW.")

with this:

import os, requests
requests.post(
    "https://api.redditapis.com/api/reddit/dm",
    json={"to": "spez", "subject": "Hi", "text": "Sent from REST."},
    headers={"Authorization": f"Bearer {os.environ['REDDITAPI_KEY']}"},
)

PRAW handles the legacy PM endpoint. The REST endpoint handles the chat DM. If you genuinely need both surfaces in one workflow, run both in parallel, since they are different message types landing in different Reddit-side inboxes.

Where to go next

Sign up at /signup for a $0.50 free credit. From there, every $1 in credit covers 40 DMs at the current $0.025 rate. Credits never expire.

Frequently asked questions.

Yes, two paths exist. The legacy path is the OAuth-based /api/compose endpoint, which sends a subject and body private message into the Reddit inbox. PRAW wraps this via redditor.message(subject, body). The newer path is the Reddit chat DM, which the legacy endpoint does not produce. To send a chat DM via REST you can use a third-party Reddit API like redditapis.com /api/reddit/dm, which exposes the chat endpoint behind a bearer token and a customer-supplied credential set at $0.025 per call.

Reddit's general API ceiling is roughly 100 queries per minute for OAuth-authenticated apps. The DM-specific limit is stricter and not officially documented. Pacing recommendations and per-account tuning live in our [DM endpoint reference](https://docs.redditapis.com/docs/dm/dm). The redditapis.com REST endpoint does not add its own per-minute cap on top of Reddit's.

Yes. The REST shape of POST /api/reddit/dm maps cleanly to an MCP tool definition. A tool named send_reddit_dm with input schema {to: string, subject: string, text: string} and bound credentials gives an agent direct send capability. The same shape works inside LangChain, the Vercel AI SDK, the OpenAI tool-calling API, and the Anthropic tool-use API. See the [DM endpoint reference](https://docs.redditapis.com/docs/dm/dm) for the canonical request shape.

Three options serve this audience. PRAW for small-volume sends where you operate your own credential pool. The redditapis.com pay-per-call endpoint at $0.025 per DM for medium-volume sends where you want REST shape and bring your own credentials. Apify's Reddit chat sender for high-volume sends where you want residential proxies bundled in. Choice depends on per-account-day volume, deliverability tolerance, and whether you want to run the credential pool yourself.

Three things matter most: a real activity history (the account participates in subreddits before it sends DMs), varied message bodies (a template with spintax slots, or per-recipient text from a small language model), and recipients matched to context (people active in subreddits where the topic fits). The [DM endpoint reference at docs.redditapis.com/docs/dm/dm](https://docs.redditapis.com/docs/dm/dm) has the full deliverability playbook, including pacing recommendations and account-quality checks.

Similar reads.

More guides on the Reddit API, scraping, pricing, and MCP servers.