DocumentationDirect messages
How to read and send direct messages with an API
Read an account's inbox, open one conversation, and answer it or start a new one. BuzzTake uses the account's own messages page, so the messages come from the account itself.
You need a BuzzTake API key and the id of an active account on a platform with direct messages. Threads and YouTube have none.
1. List the conversations
POST/v1/accounts/{id}/actions
list_conversations reads the inbox, newest first. limit is 20 by default and at most 100. Reads cost 4 credits and are charged only when they succeed.
curl https://api.buzztake.com/v1/accounts/$ACCOUNT_ID/actions \
-H "Authorization: Bearer $BUZZTAKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "list_conversations", "limit": 20, "wait": 60 }'{
"type": "list_conversations",
"status": "succeeded",
"result": {
"type": "conversations",
"items": [
{
"url": "https://www.instagram.com/direct/t/1101.../",
"id": "1101...",
"title": "Jamie",
"last_message": "Is there a student discount?",
"updated_at": "2026-10-30T07:41:00Z",
"unread": true
}
]
},
"credits": 4
}Each conversation has url, id, title, last_message, updated_at and unread. Any of them is null when the page does not show it.
2. Read one conversation
Pass the conversation's url or id in conversation. A handle such as @jamie.codes opens the conversation with that person.
curl https://api.buzztake.com/v1/accounts/$ACCOUNT_ID/actions \
-H "Authorization: Bearer $BUZZTAKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "read_conversation",
"conversation": "https://www.instagram.com/direct/t/1101.../",
"limit": 20,
"wait": 60
}'{
"result": {
"type": "messages",
"items": [
{
"id": "aWdf...",
"sender": "jamie.codes",
"from_me": false,
"text": "Is there a student discount?",
"sent_at": "2026-10-30T07:41:00Z",
"media_urls": []
}
]
}
}from_me is true for the account's own messages. limit keeps the newest messages that the page has loaded.
3. Reply in the conversation
send_message takes to and text, and media where the platform allows it. Send the conversation's URL in to to answer in that conversation. A message costs 10 credits, charged when it starts.
curl
curl https://api.buzztake.com/v1/accounts/$ACCOUNT_ID/actions \
-H "Authorization: Bearer $BUZZTAKE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dm-1101-2026-10-30" \
-d '{
"type": "send_message",
"to": "https://www.instagram.com/direct/t/1101.../",
"text": "Yes: 50% off with a school email. Reply with yours and I will set it up.",
"wait": 60
}'JavaScript
const api = "https://api.buzztake.com/v1";
const accountId = process.env.ACCOUNT_ID;
const headers = {
Authorization: `Bearer ${process.env.BUZZTAKE_API_KEY}`,
"Content-Type": "application/json",
};
async function run(body, idempotencyKey) {
const res = await fetch(`${api}/accounts/${accountId}/actions`, {
method: "POST",
headers: idempotencyKey ? { ...headers, "Idempotency-Key": idempotencyKey } : headers,
body: JSON.stringify({ ...body, wait: 90 }),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const action = await res.json();
if (action.status !== "succeeded") {
throw new Error(`${body.type} ${action.status}: ${action.error?.message ?? ""}`);
}
return action.result;
}
// Your code or your model decides the answer. Return null to skip the conversation.
function draftReply(messages) {
const question = messages.find((message) => !message.from_me && /discount/i.test(message.text ?? ""));
return question ? "Yes: 50% off with a school email." : null;
}
const { items: conversations } = await run({ type: "list_conversations", limit: 20 });
for (const conversation of conversations.filter((item) => item.unread)) {
const ref = conversation.url ?? conversation.id;
const { items: messages } = await run({ type: "read_conversation", conversation: ref, limit: 20 });
const text = draftReply(messages);
if (!text) continue;
const sent = await run({ type: "send_message", to: ref, text }, `dm-${conversation.id}-${conversation.updated_at}`);
console.log(conversation.title, "->", sent.url ?? "sent");
}Python
import os
import re
import requests
api = "https://api.buzztake.com/v1"
account_id = os.environ["ACCOUNT_ID"]
headers = {"Authorization": f"Bearer {os.environ['BUZZTAKE_API_KEY']}"}
def run(body, idempotency_key=None):
extra = {"Idempotency-Key": idempotency_key} if idempotency_key else {}
resp = requests.post(
f"{api}/accounts/{account_id}/actions",
headers={**headers, **extra},
json={**body, "wait": 90},
timeout=100,
)
if not resp.ok:
raise SystemExit(f"{resp.status_code}: {resp.text}")
action = resp.json()
if action["status"] != "succeeded":
raise SystemExit(f"{body['type']} {action['status']}: {action.get('error')}")
return action["result"]
def draft_reply(messages):
"""Your code or your model decides the answer. Return None to skip the conversation."""
for message in messages:
if not message["from_me"] and re.search("discount", message["text"] or "", re.I):
return "Yes: 50% off with a school email."
return None
conversations = run({"type": "list_conversations", "limit": 20})["items"]
for conversation in [c for c in conversations if c["unread"]]:
ref = conversation["url"] or conversation["id"]
messages = run({"type": "read_conversation", "conversation": ref, "limit": 20})["items"]
text = draft_reply(messages)
if not text:
continue
key = f"dm-{conversation['id']}-{conversation['updated_at']}"
sent = run({"type": "send_message", "to": ref, "text": text}, key)
print(conversation["title"], "->", sent["url"] or "sent")The scripts read each unread conversation and send an answer when draftReply returns one. The Idempotency-Key includes the conversation's updated_at, so a second run over the same unread message sends nothing new.
list_conversations, read_conversation and send_message. An agent can draft each answer
and send it only after you confirm.
4. Start a new conversation
To message someone the account has no conversation with, send their handle in to, with @ in front. Do not send a profile URL: BuzzTake reads every URL in to as a conversation, and profile URLs fail on X, Instagram, Facebook, LinkedIn and Bluesky. The @ also matters for handles with a hyphen, such as @jamie-doe, which BuzzTake would otherwise read as a conversation id.
curl https://api.buzztake.com/v1/accounts/$ACCOUNT_ID/actions \
-H "Authorization: Bearer $BUZZTAKE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: welcome-jamie.codes" \
-d '{ "type": "send_message", "to": "@jamie.codes", "text": "Thanks for the follow. Questions are welcome here.", "wait": 60 }'| Platform | What to expect |
|---|---|
| X | From the profile's Message button. Fails when the person does not take messages from the account. |
| New conversations, text and media. | |
| Messenger, from the profile's Message button. | |
| Text only. People outside the account's network can need InMail, which is LinkedIn Premium. | |
| Bluesky | Text. Media in a message is dropped. |
| TikTok, Pinterest, Reddit, Telegram | Text only. On Reddit, messages go to people, not communities. |
| Mastodon | A private mention: a direct post to the person. |
| Threads, YouTube | No direct messages. The actions answer 422 unsupported. |
Common errors
- 422 invalid_input
- "send_message needs
text,media, or both" - 422 unsupported
- "Threads accounts cannot send_message. They can: ..."
- Action error rejected
- "the platform rejected the action: this account does not take messages from you" on X or Bluesky, or "messaging this member needs InMail (LinkedIn Premium)" on LinkedIn.
- Action error invalid_input
- "invalid input: LinkedIn messages take text only here". TikTok, Pinterest, Reddit and Telegram refuse media the same way. Remove
media; the credits are refunded. - A passcode or PIN prompt
- X Chat can ask for the account's 4-digit chat passcode. The action then waits with status
needs_input: send the passcode withanswer_challenge(MCP) orPOST /v1/actions/{id}/answer, and the action goes on. Messenger can ask for the chat history PIN: open a sign-in link and enter it once in the live browser, then send again.
See also
- The send_message action
- The list_conversations action
- How to read and reply to comments on your posts
- How to monitor mentions of your brand and reply
- How to retry API calls without posting twice
To run this on your own accounts, create a BuzzTake account, then add an account and an API key.