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.

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.
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…
| 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
@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.
- 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.
- Healthy karma mix. Accounts with both comment karma and link karma read as participating members of communities. Either alone is thinner signal.
- 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.
- Sensible pacing. Match the rhythm of an active human account. The endpoint-specific pacing recommendations live in the docs.
- 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
- DM endpoint reference for the full request and response shape
- DM threads reference to list your inbox conversations
- DM messages reference to fetch messages by
room_idwith cursor pagination - Login endpoint for the session-cookie flow
- Pricing for the full endpoint cost table
- Reddit API alternatives to compare RedditAPI to PRAW, Apify, and the Reddit Commercial Data API
- Reddit API in Python tutorial for the read endpoints this same workflow will need
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.



