JavaScript Examples
Complete JavaScript and Node.js code examples for using the Boring API.
Installation
Install required packages:
npm install axios
# or
npm install node-fetch
Basic Setup (Node.js)
Using Axios
const axios = require('axios');
// Configuration
const API_URL = "https://boring.aiagent-me.com/v2/posts";
const API_KEY = process.env.BORING_API_KEY;
// Headers
const headers = {
"boring-api-key": API_KEY,
"Content-Type": "application/json"
};
Using Fetch (Node.js 18+)
// Configuration
const API_URL = "https://boring.aiagent-me.com/v2/posts";
const API_KEY = process.env.BORING_API_KEY;
const headers = {
"boring-api-key": API_KEY,
"Content-Type": "application/json"
};
Simple Publishing Function
Axios Version
async function publishPost(accountId, platform, text, mediaUrls = []) {
const data = {
post: {
accountId: accountId,
content: {
text: text,
mediaUrls: mediaUrls,
platform: platform
},
target: {
targetType: platform
}
}
};
try {
const response = await axios.post(API_URL, data, { headers });
return response.data;
} catch (error) {
console.error("API Error:", error.response?.data || error.message);
throw error;
}
}
Fetch Version
async function publishPost(accountId, platform, text, mediaUrls = []) {
const data = {
post: {
accountId: accountId,
content: {
text: text,
mediaUrls: mediaUrls,
platform: platform
},
target: {
targetType: platform
}
}
};
const response = await fetch(API_URL, {
method: "POST",
headers: headers,
body: JSON.stringify(data)
});
const result = await response.json();
if (!result.success) {
throw new Error(result.message);
}
return result;
}
Facebook Examples
Text-Only Post
const result = await publishPost(
"your-facebook-account-id",
"facebook",
"Hello from Boring API! π"
);
console.log(`Published! Post ID: ${result.data.post_id}`);
Single Photo
const result = await publishPost(
"your-facebook-account-id",
"facebook",
"Check out our new product!",
["https://example.com/product.jpg"]
);
Photo Album
const result = await publishPost(
"your-facebook-account-id",
"facebook",
"Our best moments from the event! πΈ",
[
"https://example.com/event1.jpg",
"https://example.com/event2.jpg",
"https://example.com/event3.jpg",
"https://example.com/event4.jpg"
]
);
Video Post
const result = await publishPost(
"your-facebook-account-id",
"facebook",
"Watch our product demo! π₯",
["https://example.com/demo.mp4"]
);
Instagram Examples
Single Photo
const result = await publishPost(
"your-instagram-account-id",
"instagram",
"Beautiful sunset π
\n\n#photography #nature #sunset",
["https://example.com/sunset.jpg"]
);
Carousel
const result = await publishPost(
"your-instagram-account-id",
"instagram",
"Swipe to see our new collection! β‘οΈ\n\n#fashion #style #newcollection",
[
"https://example.com/item1.jpg",
"https://example.com/item2.jpg",
"https://example.com/item3.jpg"
]
);
Reels
const result = await publishPost(
"your-instagram-account-id",
"instagram",
"Quick tutorial! π₯\n\n#tutorial #howto #tips",
["https://example.com/tutorial.mp4"]
);
Threads Examples
Text-Only
const result = await publishPost(
"your-threads-account-id",
"threads",
"Just shipped a new feature! π"
);
Long-Form Thread
const threadContent = [
"π§΅ Let me tell you about building our API (thread)",
"It started with a simple idea: make social media publishing easier.",
"We researched all the pain points developers face.",
"Token management, platform differences, complex APIs - all frustrating.",
"So we built Boring: one unified API for all platforms.",
"Today we support Facebook, Instagram, and Threads!",
"What platform should we add next? Let me know! π"
];
const result = await publishPost(
"your-threads-account-id",
"threads",
threadContent // Array creates a thread!
);
console.log(`Thread published! ${result.data.thread_count} posts`);
console.log(`Post IDs: ${result.data.post_ids.join(', ')}`);
Advanced Examples
Cross-Platform Publishing
async function crossPlatformPublish(accounts, text, mediaUrls) {
const results = {};
for (const [platform, accountId] of Object.entries(accounts)) {
console.log(`Publishing to ${platform}...`);
try {
// Adapt content for each platform
const adaptedText = adaptContent(text, platform);
const adaptedMedia = adaptMedia(mediaUrls, platform);
const result = await publishPost(
accountId,
platform,
adaptedText,
adaptedMedia
);
results[platform] = {
success: true,
data: result.data
};
console.log(`β ${platform}: Success`);
} catch (error) {
results[platform] = {
success: false,
error: error.message
};
console.error(`β ${platform}: ${error.message}`);
}
}
return results;
}
// Usage
const accounts = {
facebook: "fb-account-id",
instagram: "ig-account-id",
threads: "threads-account-id"
};
const results = await crossPlatformPublish(
accounts,
"Check out our new product launch!",
["https://example.com/product.jpg"]
);
Content Adaptation
function adaptContent(text, platform) {
if (platform === "instagram") {
return `${text}\n\n#product #launch #new`;
} else if (platform === "threads") {
return `${text} What do you think? π`;
} else {
return text;
}
}
function adaptMedia(mediaUrls, platform) {
if (platform === "instagram" && mediaUrls.length === 0) {
throw new Error("Instagram requires media");
}
// Threads supports up to 20, others up to 10
const limit = platform === "threads" ? 20 : 10;
return mediaUrls.slice(0, limit);
}
Error Handling
async function publishWithErrorHandling(accountId, platform, text, mediaUrls = []) {
try {
console.log(`Publishing to ${platform}...`);
const result = await publishPost(accountId, platform, text, mediaUrls);
console.log(`Success! Post ID: ${result.data.post_id}`);
return result;
} catch (error) {
const errorData = error.response?.data;
if (!errorData) {
console.error("Network error:", error.message);
return { success: false, error: "NetworkError" };
}
const errorCode = errorData.error;
switch (errorCode) {
case "TokenExpired":
console.error("Token expired. Please reconnect account.");
await notifyAdmin("Token expired", accountId);
break;
case "MediaDownloadFailed":
console.error("Media download failed. Retrying without media...");
return await publishPost(accountId, platform, text, []);
case "RateLimitExceeded":
const retryAfter = errorData.retry_after || 3600;
console.warn(`Rate limited. Retry in ${retryAfter}s`);
break;
default:
console.error(`Error: ${errorData.message}`);
}
return errorData;
}
}
async function notifyAdmin(subject, details) {
console.log(`ADMIN ALERT: ${subject} - ${details}`);
// Implement your notification logic
}
Retry Logic
async function publishWithRetry(accountId, platform, text, mediaUrls = [], maxRetries = 3) {
const retryableErrors = [
"RateLimitExceeded",
"ServiceUnavailable",
"Timeout",
"InternalServerError"
];
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
console.log(`Attempt ${attempt + 1}/${maxRetries}`);
const result = await publishPost(accountId, platform, text, mediaUrls);
return result;
} catch (error) {
const errorData = error.response?.data;
const errorCode = errorData?.error;
// Don't retry non-retryable errors
if (!retryableErrors.includes(errorCode)) {
console.warn(`Non-retryable error: ${errorCode}`);
throw error;
}
// Calculate wait time
let waitTime;
if (errorCode === "RateLimitExceeded") {
waitTime = errorData.retry_after || 3600;
} else {
waitTime = Math.pow(2, attempt); // Exponential backoff
}
console.log(`Retrying in ${waitTime}s...`);
await sleep(waitTime * 1000);
}
}
throw new Error("Max retries exceeded");
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Batch Publishing
async function batchPublish(posts, delay = 2000) {
const results = [];
for (let i = 0; i < posts.length; i++) {
console.log(`Publishing post ${i + 1}/${posts.length}`);
try {
const result = await publishWithRetry(
posts[i].accountId,
posts[i].platform,
posts[i].text,
posts[i].mediaUrls
);
results.push({
index: i,
post: posts[i],
result: result
});
} catch (error) {
results.push({
index: i,
post: posts[i],
result: { success: false, error: error.message }
});
}
// Rate limiting
if (i < posts.length - 1) {
await sleep(delay);
}
}
// Summary
const successes = results.filter(r => r.result.success).length;
const failures = results.length - successes;
console.log(`Batch complete: ${successes} succeeded, ${failures} failed`);
return results;
}
// Usage
const posts = [
{
accountId: "fb-account-id",
platform: "facebook",
text: "Post 1",
mediaUrls: []
},
{
accountId: "ig-account-id",
platform: "instagram",
text: "Post 2",
mediaUrls: ["https://example.com/image.jpg"]
},
{
accountId: "threads-account-id",
platform: "threads",
text: "Post 3",
mediaUrls: []
}
];
const results = await batchPublish(posts);
Thread Generator
function splitIntoThread(longText, maxChars = 450) {
const sentences = longText.split('. ');
const posts = [];
let currentPost = "";
for (const sentence of sentences) {
if (currentPost.length + sentence.length + 2 <= maxChars) {
currentPost += sentence + '. ';
} else {
if (currentPost) {
posts.push(currentPost.trim());
}
currentPost = sentence + '. ';
}
}
if (currentPost) {
posts.push(currentPost.trim());
}
return posts;
}
// Usage
const article = `
This is a very long article about our product launch.
It contains multiple sentences and paragraphs.
We want to share it as a thread on Threads.
Each post should be under 450 characters.
This function will automatically split it for us.
`;
const threadPosts = splitIntoThread(article);
const result = await publishPost(
"threads-account-id",
"threads",
threadPosts // Array creates thread
);
Configuration Class
const fs = require('fs').promises;
class BoringAPI {
constructor(apiKey, accountsConfigPath) {
this.apiKey = apiKey || process.env.BORING_API_KEY;
this.apiUrl = "https://boring.aiagent-me.com/v2/posts";
this.accountsConfigPath = accountsConfigPath;
this.accounts = null;
this.headers = {
"boring-api-key": this.apiKey,
"Content-Type": "application/json"
};
}
async loadAccounts() {
if (this.accountsConfigPath) {
const data = await fs.readFile(this.accountsConfigPath, 'utf8');
this.accounts = JSON.parse(data);
}
}
async publish(accountName, text, mediaUrls = []) {
if (!this.accounts) {
await this.loadAccounts();
}
if (!this.accounts[accountName]) {
throw new Error(`Account '${accountName}' not found in config`);
}
const account = this.accounts[accountName];
return await publishPost(
account.id,
account.platform,
text,
mediaUrls
);
}
}
// accounts.json
{
"production_fb": {
"id": "fb-account-id",
"platform": "facebook"
},
"production_ig": {
"id": "ig-account-id",
"platform": "instagram"
},
"production_threads": {
"id": "threads-account-id",
"platform": "threads"
}
}
// Usage
const api = new BoringAPI(null, "accounts.json");
await api.publish(
"production_fb",
"Hello from Boring API!",
[]
);
Browser (Frontend) Example
<!DOCTYPE html>
<html>
<head>
<title>Boring API Test</title>
</head>
<body>
<h1>Publish to Social Media</h1>
<form id="publishForm">
<label>Account ID:</label>
<input type="text" id="accountId" required><br>
<label>Platform:</label>
<select id="platform">
<option value="facebook">Facebook</option>
<option value="instagram">Instagram</option>
<option value="threads">Threads</option>
</select><br>
<label>Text:</label>
<textarea id="text" rows="4" required></textarea><br>
<label>Media URLs (one per line):</label>
<textarea id="mediaUrls" rows="3"></textarea><br>
<button type="submit">Publish</button>
</form>
<div id="result"></div>
<script>
const API_URL = "https://boring.aiagent-me.com/v2/posts";
const API_KEY = "YOUR_API_KEY_HERE"; // WARNING: Don't expose in production!
document.getElementById('publishForm').addEventListener('submit', async (e) => {
e.preventDefault();
const accountId = document.getElementById('accountId').value;
const platform = document.getElementById('platform').value;
const text = document.getElementById('text').value;
const mediaUrlsText = document.getElementById('mediaUrls').value;
const mediaUrls = mediaUrlsText
.split('\n')
.map(url => url.trim())
.filter(url => url);
const data = {
post: {
accountId: accountId,
content: {
text: text,
mediaUrls: mediaUrls,
platform: platform
},
target: {
targetType: platform
}
}
};
try {
const response = await fetch(API_URL, {
method: "POST",
headers: {
"boring-api-key": API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
document.getElementById('result').innerHTML =
`<div style="color: green;">Success! Post ID: ${result.data.post_id}</div>`;
} else {
document.getElementById('result').innerHTML =
`<div style="color: red;">Error: ${result.message}</div>`;
}
} catch (error) {
document.getElementById('result').innerHTML =
`<div style="color: red;">Network error: ${error.message}</div>`;
}
});
</script>
</body>
</html>
Note: Never expose your API key in frontend code in production! Use a backend proxy instead.
Complete Example (Node.js)
#!/usr/bin/env node
/**
* Boring API Publishing Script
*/
const axios = require('axios');
// Configuration
const API_URL = "https://boring.aiagent-me.com/v2/posts";
const API_KEY = process.env.BORING_API_KEY;
const headers = {
"boring-api-key": API_KEY,
"Content-Type": "application/json"
};
async function publishPost(accountId, platform, text, mediaUrls = []) {
const data = {
post: {
accountId,
content: {
text,
mediaUrls,
platform
},
target: {
targetType: platform
}
}
};
const response = await axios.post(API_URL, data, { headers });
return response.data;
}
async function main() {
console.log("Publishing to Facebook...");
const result = await publishPost(
process.env.FB_ACCOUNT_ID,
"facebook",
"Hello from Boring API! π",
[]
);
if (result.success) {
console.log(`Success! Post ID: ${result.data.post_id}`);
} else {
console.error(`Failed: ${result.message}`);
}
}
main().catch(console.error);
Next Steps
- Python Examples - Python code examples
- cURL Examples - Command-line examples
- Use Cases - Real-world scenarios
- API Reference - Complete API docs