Python Examples

Complete Python code examples for using the Boring API.

Installation

Install required library:

pip install requests

Basic Setup

import requests
import os

# Configuration
API_URL = "https://boring.aiagent-me.com/v2/posts"
API_KEY = os.environ.get("BORING_API_KEY")

# Headers
HEADERS = {
    "boring-api-key": API_KEY,
    "Content-Type": "application/json"
}

Simple Publishing Function

def publish_post(account_id, platform, text, media_urls=None):
    """
    Publish a post to social media

    Args:
        account_id (str): Account ID from dashboard
        platform (str): "facebook", "instagram", or "threads"
        text (str|list): Post text or list of strings for thread
        media_urls (list): List of media URLs (optional)

    Returns:
        dict: API response
    """
    if media_urls is None:
        media_urls = []

    data = {
        "post": {
            "accountId": account_id,
            "content": {
                "text": text,
                "mediaUrls": media_urls,
                "platform": platform
            },
            "target": {
                "targetType": platform
            }
        }
    }

    response = requests.post(API_URL, headers=HEADERS, json=data)
    return response.json()

Facebook Examples

Text-Only Post

result = publish_post(
    account_id="your-facebook-account-id",
    platform="facebook",
    text="Hello from Boring API! πŸš€"
)

if result["success"]:
    print(f"Published! Post ID: {result['data']['post_id']}")
else:
    print(f"Error: {result['message']}")

Single Photo

result = publish_post(
    account_id="your-facebook-account-id",
    platform="facebook",
    text="Check out our new product!",
    media_urls=["https://example.com/product.jpg"]
)

Photo Album

result = publish_post(
    account_id="your-facebook-account-id",
    platform="facebook",
    text="Our best moments from the event! πŸ“Έ",
    media_urls=[
        "https://example.com/event1.jpg",
        "https://example.com/event2.jpg",
        "https://example.com/event3.jpg",
        "https://example.com/event4.jpg"
    ]
)

Video Post

result = publish_post(
    account_id="your-facebook-account-id",
    platform="facebook",
    text="Watch our product demo! πŸŽ₯",
    media_urls=["https://example.com/demo.mp4"]
)

Instagram Examples

Single Photo

result = publish_post(
    account_id="your-instagram-account-id",
    platform="instagram",
    text="Beautiful sunset πŸŒ…\n\n#photography #nature #sunset",
    media_urls=["https://example.com/sunset.jpg"]
)

Carousel

result = publish_post(
    account_id="your-instagram-account-id",
    platform="instagram",
    text="Swipe to see our new collection! ➑️\n\n#fashion #style #newcollection",
    media_urls=[
        "https://example.com/item1.jpg",
        "https://example.com/item2.jpg",
        "https://example.com/item3.jpg"
    ]
)

Reels (Video)

result = publish_post(
    account_id="your-instagram-account-id",
    platform="instagram",
    text="Quick tutorial! πŸŽ₯\n\n#tutorial #howto #tips",
    media_urls=["https://example.com/tutorial.mp4"]
)

Threads Examples

Text-Only

result = publish_post(
    account_id="your-threads-account-id",
    platform="threads",
    text="Just shipped a new feature! πŸš€"
)

Single Photo

result = publish_post(
    account_id="your-threads-account-id",
    platform="threads",
    text="Morning vibes β˜•",
    media_urls=["https://example.com/morning.jpg"]
)

Carousel

result = publish_post(
    account_id="your-threads-account-id",
    platform="threads",
    text="Our journey in photos πŸ“Έ",
    media_urls=[
        "https://example.com/photo1.jpg",
        "https://example.com/photo2.jpg",
        "https://example.com/photo3.jpg",
        "https://example.com/photo4.jpg",
        "https://example.com/photo5.jpg"
    ]
)

Long-Form Thread

# Note: text is an ARRAY of strings
thread_content = [
    "🧡 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! πŸ‘‡"
]

result = publish_post(
    account_id="your-threads-account-id",
    platform="threads",
    text=thread_content  # Array creates a thread!
)

if result["success"]:
    print(f"Thread published! {result['data']['thread_count']} posts")
    print(f"Post IDs: {result['data']['post_ids']}")

Advanced Examples

Cross-Platform Publishing

def cross_platform_publish(accounts, text, media_urls):
    """
    Publish to multiple platforms simultaneously

    Args:
        accounts (dict): Map of platform to account_id
        text (str): Post text
        media_urls (list): Media URLs

    Returns:
        dict: Results for each platform
    """
    results = {}

    for platform, account_id in accounts.items():
        print(f"Publishing to {platform}...")

        # Adapt content for each platform
        adapted_text = adapt_content(text, platform)
        adapted_media = adapt_media(media_urls, platform)

        result = publish_post(
            account_id=account_id,
            platform=platform,
            text=adapted_text,
            media_urls=adapted_media
        )

        results[platform] = result

        # Log result
        if result["success"]:
            print(f"βœ“ {platform}: Success")
        else:
            print(f"βœ— {platform}: {result['message']}")

    return results

# Usage
accounts = {
    "facebook": "fb-account-id",
    "instagram": "ig-account-id",
    "threads": "threads-account-id"
}

results = cross_platform_publish(
    accounts=accounts,
    text="Check out our new product launch!",
    media_urls=["https://example.com/product.jpg"]
)

Content Adaptation

def adapt_content(text, platform):
    """Adapt text content for each platform"""

    if platform == "instagram":
        # Add hashtags for Instagram
        return f"{text}\n\n#product #launch #new"

    elif platform == "threads":
        # Keep it conversational for Threads
        return f"{text} What do you think? πŸ’­"

    else:  # facebook
        # Keep original for Facebook
        return text

def adapt_media(media_urls, platform):
    """Adapt media for each platform"""

    if platform == "instagram":
        # Instagram requires at least one media
        if not media_urls:
            raise ValueError("Instagram requires media")

    # Threads supports up to 20 images
    if platform == "threads":
        return media_urls[:20]

    # Facebook/Instagram support up to 10
    return media_urls[:10]

Error Handling

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def publish_with_error_handling(account_id, platform, text, media_urls=None):
    """Publish with comprehensive error handling"""

    try:
        logger.info(f"Publishing to {platform} (account: {account_id})")

        result = publish_post(account_id, platform, text, media_urls)

        if result["success"]:
            logger.info(f"Success! Post ID: {result['data']['post_id']}")
            return result

        # Handle specific errors
        error_code = result.get("error")

        if error_code == "TokenExpired":
            logger.error("Token expired. Please reconnect account.")
            notify_admin("Token expired", account_id)

        elif error_code == "MediaDownloadFailed":
            logger.error("Media download failed. Check URLs.")
            # Try without media
            logger.info("Retrying without media...")
            return publish_post(account_id, platform, text, [])

        elif error_code == "RateLimitExceeded":
            retry_after = result.get("retry_after", 3600)
            logger.warning(f"Rate limited. Retry in {retry_after}s")

        else:
            logger.error(f"Error: {result['message']}")

        return result

    except requests.exceptions.Timeout:
        logger.error("Request timed out")
        return {"success": False, "error": "Timeout"}

    except requests.exceptions.RequestException as e:
        logger.error(f"Request failed: {e}")
        return {"success": False, "error": "NetworkError"}

    except Exception as e:
        logger.exception(f"Unexpected error: {e}")
        return {"success": False, "error": "UnexpectedException"}

def notify_admin(subject, details):
    """Send notification to admin"""
    # Implement your notification logic
    print(f"ADMIN ALERT: {subject} - {details}")

Retry Logic

import time

def publish_with_retry(account_id, platform, text, media_urls=None, max_retries=3):
    """Publish with automatic retry on failure"""

    retryable_errors = [
        "RateLimitExceeded",
        "ServiceUnavailable",
        "Timeout",
        "InternalServerError"
    ]

    for attempt in range(max_retries):
        try:
            logger.info(f"Attempt {attempt + 1}/{max_retries}")

            result = publish_post(account_id, platform, text, media_urls)

            if result["success"]:
                return result

            error_code = result.get("error")

            # Don't retry non-retryable errors
            if error_code not in retryable_errors:
                logger.warning(f"Non-retryable error: {error_code}")
                return result

            # Calculate wait time
            if error_code == "RateLimitExceeded":
                wait_time = result.get("retry_after", 3600)
            else:
                wait_time = 2 ** attempt  # Exponential backoff

            logger.info(f"Retrying in {wait_time}s...")
            time.sleep(wait_time)

        except Exception as e:
            if attempt == max_retries - 1:
                raise
            logger.warning(f"Attempt failed: {e}")
            time.sleep(2 ** attempt)

    return {"success": False, "error": "MaxRetriesExceeded"}

Batch Publishing

def batch_publish(posts, delay=2):
    """
    Publish multiple posts with rate limiting

    Args:
        posts (list): List of post dictionaries
        delay (int): Seconds to wait between posts

    Returns:
        list: Results for each post
    """
    results = []

    for i, post in enumerate(posts):
        logger.info(f"Publishing post {i+1}/{len(posts)}")

        result = publish_with_retry(**post)
        results.append({
            "index": i,
            "post": post,
            "result": result
        })

        # Rate limiting
        if i < len(posts) - 1:
            time.sleep(delay)

    # Summary
    successes = sum(1 for r in results if r["result"]["success"])
    failures = len(results) - successes

    logger.info(f"Batch complete: {successes} succeeded, {failures} failed")

    return results

# Usage
posts = [
    {
        "account_id": "fb-account-id",
        "platform": "facebook",
        "text": "Post 1",
        "media_urls": []
    },
    {
        "account_id": "ig-account-id",
        "platform": "instagram",
        "text": "Post 2",
        "media_urls": ["https://example.com/image.jpg"]
    },
    {
        "account_id": "threads-account-id",
        "platform": "threads",
        "text": "Post 3",
        "media_urls": []
    }
]

results = batch_publish(posts)

Thread Generator

def split_into_thread(long_text, max_chars=450):
    """
    Split long text into thread posts

    Args:
        long_text (str): Long text to split
        max_chars (int): Max characters per post (default 450)

    Returns:
        list: List of post strings
    """
    sentences = long_text.split('. ')
    posts = []
    current_post = ""

    for sentence in sentences:
        # Check if adding sentence would exceed limit
        if len(current_post) + len(sentence) + 2 <= max_chars:
            current_post += sentence + '. '
        else:
            # Start new post
            if current_post:
                posts.append(current_post.strip())
            current_post = sentence + '. '

    # Add last post
    if current_post:
        posts.append(current_post.strip())

    return posts

# Usage
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.
"""

thread_posts = split_into_thread(article)

result = publish_post(
    account_id="threads-account-id",
    platform="threads",
    text=thread_posts  # Array creates thread
)

Configuration Management

import json

class BoringAPI:
    """Boring API client with configuration management"""

    def __init__(self, api_key=None, accounts_config=None):
        self.api_key = api_key or os.environ.get("BORING_API_KEY")
        self.api_url = "https://boring.aiagent-me.com/v2/posts"

        # Load accounts from config file
        if accounts_config:
            with open(accounts_config) as f:
                self.accounts = json.load(f)
        else:
            self.accounts = {}

        self.headers = {
            "boring-api-key": self.api_key,
            "Content-Type": "application/json"
        }

    def publish(self, account_name, text, media_urls=None):
        """
        Publish using account name from config

        Args:
            account_name (str): Account name from config
            text (str|list): Post text
            media_urls (list): Media URLs

        Returns:
            dict: API response
        """
        if account_name not in self.accounts:
            raise ValueError(f"Account '{account_name}' not found in config")

        account = self.accounts[account_name]

        return publish_post(
            account_id=account["id"],
            platform=account["platform"],
            text=text,
            media_urls=media_urls or []
        )

# 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
api = BoringAPI(accounts_config="accounts.json")

api.publish(
    account_name="production_fb",
    text="Hello from Boring API!",
    media_urls=[]
)

Complete Example

Full script with all features:

#!/usr/bin/env python3
"""
Boring API Publishing Script
"""

import requests
import os
import logging
import time
from typing import List, Dict, Optional

# Configuration
API_URL = "https://boring.aiagent-me.com/v2/posts"
API_KEY = os.environ.get("BORING_API_KEY")

# Logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def publish_post(
    account_id: str,
    platform: str,
    text: str,
    media_urls: Optional[List[str]] = None
) -> Dict:
    """Publish a post to social media"""

    headers = {
        "boring-api-key": API_KEY,
        "Content-Type": "application/json"
    }

    data = {
        "post": {
            "accountId": account_id,
            "content": {
                "text": text,
                "mediaUrls": media_urls or [],
                "platform": platform
            },
            "target": {
                "targetType": platform
            }
        }
    }

    response = requests.post(API_URL, headers=headers, json=data)
    return response.json()

def main():
    """Main function"""

    # Example: Publish to Facebook
    logger.info("Publishing to Facebook...")

    result = publish_post(
        account_id=os.environ.get("FB_ACCOUNT_ID"),
        platform="facebook",
        text="Hello from Boring API! πŸš€",
        media_urls=[]
    )

    if result["success"]:
        logger.info(f"Success! Post ID: {result['data']['post_id']}")
    else:
        logger.error(f"Failed: {result['message']}")

if __name__ == "__main__":
    main()

Next Steps