Mastodon API - Post to Fediverse via REST API
Post to Mastodon and the Fediverse programmatically using the Publora REST API. A simpler alternative to the official Mastodon API, Megalodon, or direct ActivityPub integration.
Mastodon API Overview
Publora provides a unified REST API for publishing text posts and media content to Mastodon and the fediverse. Posts are published to the mastodon.social instance. No need to manage Mastodon OAuth flows, handle ActivityPub protocols, or set up your own Mastodon application.
Why Use Publora Instead of Mastodon API / Megalodon?
| Feature | Publora API | Mastodon API |
|---|---|---|
| Authentication | Single API key | OAuth 2.0 per instance |
| Instance support | mastodon.social | Any instance |
| Multi-platform | Post to 10 platforms | Mastodon/Fediverse only |
| Setup time | 5 minutes | Varies by instance |
| Media handling | Automatic | Manual upload |
| Federation | Automatic | Automatic |
Keywords: Mastodon API, Mastodon posting API, Fediverse API, ActivityPub API, post to Mastodon programmatically, Mastodon REST API, Mastodon developer API, Mastodon bot API, Mastodon automation API, toot API, Mastodon status API, decentralized social API
Platform ID Format
mastodon-{accountId}Where {accountId} is your Mastodon account ID assigned during account connection through the Publora dashboard.
Requirements
- A Mastodon account on the mastodon.social instance connected through the Publora dashboard (OAuth scopes requested:
read,write,push) - API key from Publora
Supported Content
| Type | Supported | Limits |
|---|---|---|
| Text | Yes | 500 characters |
| Images | Yes | JPEG, PNG, GIF, WebP, HEIF, HEIC, AVIF; up to 4 per post |
| Videos | Yes | MP4, WebM, MOV formats |
| Visibility | Yes | Public by default |
Visibility
Posts on Mastodon are set to public visibility by default when posted through Publora. This means they will appear on your profile, in your followers' home timelines, and on the public federated timeline.
Examples
Post a Text Update
JavaScript (fetch)
const response = await fetch('https://api.publora.com/api/v1/create-post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
content: 'Hello fediverse! We just shipped a major update to our open-source project. Check out the changelog at https://example.com/changelog #opensource #fediverse',
platforms: ['mastodon-109876543210']
})
});
const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Python (requests)
import requests
response = requests.post(
'https://api.publora.com/api/v1/create-post',
headers={
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
json={
'content': 'Hello fediverse! We just shipped a major update to our open-source project. Check out the changelog at https://example.com/changelog #opensource #fediverse',
'platforms': ['mastodon-109876543210']
}
)
data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }cURL
curl -X POST https://api.publora.com/api/v1/create-post \
-H "Content-Type: application/json" \
-H "x-publora-key: YOUR_API_KEY" \
-d '{
"content": "Hello fediverse! We just shipped a major update to our open-source project. Check out the changelog at https://example.com/changelog #opensource #fediverse",
"platforms": ["mastodon-109876543210"]
}'
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Node.js (axios)
const axios = require('axios');
const response = await axios.post('https://api.publora.com/api/v1/create-post', {
content: 'Hello fediverse! We just shipped a major update to our open-source project. Check out the changelog at https://example.com/changelog #opensource #fediverse',
platforms: ['mastodon-109876543210']
}, {
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
}
});
console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Post with Media
JavaScript (fetch)
const response = await fetch('https://api.publora.com/api/v1/create-post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
content: 'New feature alert: our dashboard now supports dark mode! Here is a side-by-side comparison. #ui #darkmode',
platforms: ['mastodon-109876543210']
})
});
const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Note: To attach media to a Mastodon post, first create the post, then upload media using the media upload workflow with the returned
postGroupId.
Python (requests)
import requests
response = requests.post(
'https://api.publora.com/api/v1/create-post',
headers={
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
json={
'content': 'New feature alert: our dashboard now supports dark mode! Here is a side-by-side comparison. #ui #darkmode',
'platforms': ['mastodon-109876543210']
}
)
data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }cURL
curl -X POST https://api.publora.com/api/v1/create-post \
-H "Content-Type: application/json" \
-H "x-publora-key: YOUR_API_KEY" \
-d '{
"content": "New feature alert: our dashboard now supports dark mode! Here is a side-by-side comparison. #ui #darkmode",
"platforms": ["mastodon-109876543210"]
}'
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Node.js (axios)
const axios = require('axios');
const response = await axios.post('https://api.publora.com/api/v1/create-post', {
content: 'New feature alert: our dashboard now supports dark mode! Here is a side-by-side comparison. #ui #darkmode',
platforms: ['mastodon-109876543210']
}, {
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
}
});
console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Post with a Video
JavaScript (fetch)
const response = await fetch('https://api.publora.com/api/v1/create-post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
content: 'Quick demo of our new real-time collaboration feature. Multiple users editing the same document simultaneously!',
platforms: ['mastodon-109876543210']
})
});
const data = await response.json();
console.log(data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Python (requests)
import requests
response = requests.post(
'https://api.publora.com/api/v1/create-post',
headers={
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
},
json={
'content': 'Quick demo of our new real-time collaboration feature. Multiple users editing the same document simultaneously!',
'platforms': ['mastodon-109876543210']
}
)
data = response.json()
print(data)
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }cURL
curl -X POST https://api.publora.com/api/v1/create-post \
-H "Content-Type: application/json" \
-H "x-publora-key: YOUR_API_KEY" \
-d '{
"content": "Quick demo of our new real-time collaboration feature. Multiple users editing the same document simultaneously!",
"platforms": ["mastodon-109876543210"]
}'
# Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Node.js (axios)
const axios = require('axios');
const response = await axios.post('https://api.publora.com/api/v1/create-post', {
content: 'Quick demo of our new real-time collaboration feature. Multiple users editing the same document simultaneously!',
platforms: ['mastodon-109876543210']
}, {
headers: {
'Content-Type': 'application/json',
'x-publora-key': 'YOUR_API_KEY'
}
});
console.log(response.data);
// Response: { "success": true, "postGroupId": "abc123...", "scheduledTime": null }Platform Quirks
- mastodon.social only: New connections are limited to the mastodon.social instance (the OAuth flow uses a hardcoded instance URL). The test-connection validator attempts to extract the instance URL from the connection's
profileUrlfield, butprofileUrlis never set during Mastodon connection creation, so it always falls back to mastodon.social. Support for other instances may be added in the future. - Public by default: All posts made through Publora are published with public visibility. They will appear on the federated timeline.
- Up to 4 images: A maximum of 4 images can be attached to a single post. Publora enforces this limit at scheduling time via
postValidationService.jsand will reject posts that exceed it before they reach the Mastodon API. - Image formats: Publora's validator accepts JPEG, PNG, GIF, WebP, HEIF, HEIC, and AVIF for Mastodon. The publisher passes supported media through rather than converting it.
- MP4, WebM, and MOV for videos: Mastodon accepts MP4, WebM, and MOV video formats. Publora accepts all three as input, but the scheduler currently reports the MIME type as
video/mp4to Mastodon regardless of the actual format. MP4 uploads work correctly; WebM and MOV files may experience processing issues due to the mismatched MIME type. - 500-character limit: Mastodon enforces a strict 500-character limit. Publora will return an error if your content exceeds this. Mastodon and Meta Threads do not auto-thread in Publora; only X/Twitter threading is currently enabled.
- Hashtags: Hashtags in Mastodon are part of the post body and count toward the character limit. They become clickable and searchable on the platform.
- Content warnings: Mastodon supports content warnings (CW), but this feature is not currently available through the Publora API.
- Federation delay: Because Mastodon is federated, posts may take a few seconds to propagate to other instances in the fediverse.
API Limits
Text Limits
| Element | Limit |
|---|---|
| Post body | 500 characters (instance-configurable, some allow 5,000+) |
| Media description (alt text) | 1,500 characters |
Media Limits
| Media Type | Max Size | Max Count | Supported Formats |
|---|---|---|---|
| Images | 16 MB | 4 per post | JPEG, PNG, GIF, WebP, HEIF, HEIC, AVIF |
| Videos | ~99 MB | 1 per post | MP4, WebM, MOV |
| Video Constraint | Limit |
|---|---|
| Duration | 86,400 seconds (24 hours) |
Rate Limits
| Limit Type | Value |
|---|---|
| Media uploads | 30 per 30 minutes |
| API requests | 300 per 5 minutes |
Additional Notes
- Character limits vary by Mastodon instance; mastodon.social uses 500 characters, but some instances allow 5,000+
- Publora currently connects to mastodon.social only (posting is hardcoded to this instance)
- Mastodon and Meta Threads do not support auto-threading in the current Publora capability set; X/Twitter threading remains enabled
- Max image count (4) and video count (1) limits are enforced by Publora at scheduling time via
postValidationService.js
What you can't do through the REST API
- Set media descriptions (alt text): The Mastodon publisher forwards
file.descriptionwhen present, but the API media model has no persisteddescriptionfield. The current REST upload flows therefore cannot supply it.
Publora is built by Creative Content Crafts, Inc. Need AI-powered content creation for LinkedIn, Threads, and X? Try Co.Actor — the best AI service for authentic thought leadership at scale.