Library · Content studio: design, voice, video, social

Social media automation: Instagram, Telegram, content on autopilot

Builder65 minUpdated: October 2026
54 of 105 in the library

Module: Trending cases 2026 | Time: ~25 min theory + 40 min practice


The gist

Social media content eats many hours a week for most business owners. Claude Code can automate writing posts, scheduling them, replying to comments and running analytics. Instagram through its API (or browser automation), Telegram through its Bot API: all of it fits into a single content pipeline.

🎨 Picture this: social media automation is like a drip irrigation system in a garden. Watering by hand takes 2 hours every day. Lay the pipes and set a timer, and it runs on its own. You check once a week: is it growing, any disease? Your main job is thinking about strategy, not watering.


Key concepts

  • Instagram Graph API: Meta's official API (requires a professional Instagram account; check Meta's documentation for the sign-in method)
  • Browser automation: Playwright drives a browser the way a person would
  • Postiz / Blotato: specialized services for scheduling posts through an API
  • Content pipeline: generate → schedule → publish → analyze
  • Rate limiting: Instagram blocks aggressive automation hard
  • Terms of Service: the official API is safe; scraping is a gray area

Theory

Instagram: two ways to automate

🎨 Picture this: Instagram is like a club with security at the door. The official entrance (the Meta API) means ID, dress code and a line, but it's legit. The back door (browser automation) is faster, but the bouncer can throw you out if he notices.

Path 1: Meta Graph API (official)

Requirements (the list changes; check Meta's documentation for the current one):

  • A professional Instagram account (Business, not personal)
  • A Meta developer account and an app
  • Passing App Review (to publish posts)
  • Depending on the sign-in method, you may need a Facebook Page linked to the Instagram account
bash
# requests is enough for the examples below
pip install requests
python
import requests
import os

INSTAGRAM_BUSINESS_ACCOUNT_ID = os.environ["INSTAGRAM_ACCOUNT_ID"]
ACCESS_TOKEN = os.environ["META_ACCESS_TOKEN"]

def publish_instagram_post(image_url: str, caption: str) -> dict:
    """
    Publishes a post to Instagram through the Meta Graph API.
    image_url must be a publicly accessible URL (not a local file!)
    Replace the API version (v20.0 in the example) with the current one from Meta's documentation.
    """
    base_url = f"https://graph.facebook.com/v20.0/{INSTAGRAM_BUSINESS_ACCOUNT_ID}"
    
    # Step 1: Create a media container
    container_response = requests.post(
        f"{base_url}/media",
        params={
            "image_url": image_url,  # Public URL of the image
            "caption": caption,
            "access_token": ACCESS_TOKEN,
        }
    )
    container_id = container_response.json().get("id")
    
    if not container_id:
        return {"error": container_response.json()}
    
    # Step 2: Publish the container
    publish_response = requests.post(
        f"{base_url}/media_publish",
        params={
            "creation_id": container_id,
            "access_token": ACCESS_TOKEN,
        }
    )
    
    return publish_response.json()

def get_instagram_insights(post_id: str) -> dict:
    """Get the stats for a post"""
    response = requests.get(
        f"https://graph.facebook.com/v20.0/{post_id}/insights",
        params={
            "metric": "reach,impressions,likes,comments,shares,saves",
            "access_token": ACCESS_TOKEN,
        }
    )
    return response.json()

def schedule_post(image_url: str, caption: str, publish_time: str) -> dict:
    """
    Schedule a post (a rough sketch: check Meta's documentation to see whether the Instagram API
    supports scheduled publishing. The reliable option: store the post yourself and call publish on a schedule).
    publish_time: ISO 8601 format (for example "2026-12-15T10:00:00+0000")
    """
    base_url = f"https://graph.facebook.com/v20.0/{INSTAGRAM_BUSINESS_ACCOUNT_ID}"
    
    # Convert the time to a Unix timestamp
    from datetime import datetime
    scheduled_ts = int(datetime.fromisoformat(publish_time).timestamp())
    
    response = requests.post(
        f"{base_url}/media",
        params={
            "image_url": image_url,
            "caption": caption,
            "scheduled_publish_time": scheduled_ts,
            "is_carousel_item": False,
            "access_token": ACCESS_TOKEN,
        }
    )
    return response.json()

Meta Graph API limits (as of October 2026; check the documentation):

  • There's a limit on posts published through the API per rolling 24 hours per account; the current number is in Meta's documentation (you can ask the API for your current remaining quota)
  • Reels and Stories are published as separate media types with their own format requirements
  • App Review can take weeks, so build that time into your plan

Path 2: Browser automation with Playwright

Works without the official API. Claude Code drives a browser the way a person would.

🎨 Picture this: Playwright is like a remote-controlled robot that opens a browser, clicks buttons and fills in fields exactly like a real person. Instagram sees "a regular user," but that's not what it is.

python
from playwright.async_api import async_playwright
import asyncio
import anthropic
import os

claude = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

async def generate_and_post_to_instagram(topic: str):
    """
    IMPORTANT: This is a demo example. Browser automation
    violates Instagram's Terms of Service. Use at your own risk
    and only for educational purposes.
    """
    
    # Step 1: Generate the content with Claude
    response = claude.messages.create(
        model="claude-sonnet-5-5",  # model name as of October 2026; current ones: the What's current page
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""Write an Instagram post on the topic: {topic}
            
            Requirements:
            - 150-220 characters (before hashtags)
            - Emoji for visual separation
            - 12-15 hashtags at the end
            - Tone: friendly, expert, not salesy
            - Language: English"""
        }]
    )
    
    post_text = "".join(b.text for b in response.content if b.type == "text")
    print(f"Generated text:\n{post_text}")
    
    # Step 2: (Optional) Open a browser to publish
    # async with async_playwright() as p:
    #     browser = await p.chromium.launch(headless=False)
    #     page = await browser.new_page()
    #     await page.goto("https://www.instagram.com")
    #     # ... further actions
    
    return post_text

# Run it
asyncio.run(generate_and_post_to_instagram("an overview of the real estate market in Ecuador"))

Risks of browser automation on Instagram:

  • Account ban (temporary or permanent)
  • CAPTCHA blocks
  • IP blocks
  • A Terms of Service violation → losing the account

Recommendation: for a serious business, use only the official Meta API or specialized SaaS tools.


Instead of automating Instagram directly, use specialized tools that already have official integrations with the Meta API.

🎨 Picture this: Postiz/Blotato are like an ad placement agency. You pay a small fee, but you skip the Meta API paperwork, you don't risk your account, and you get ready-made infrastructure.

Postiz:

Parameter Value
Open Source ✅ (self-hosted for free, but you need your own server)
Hosted version Paid plans by number of channels; prices on the Postiz website
API ✅ (for integrating with Claude)
Supported platforms Instagram, Twitter/X, LinkedIn, TikTok, YouTube, Telegram
Scheduling ✅ Visual calendar
Analytics ✅ Basic
python
import requests
import os

POSTIZ_API_KEY = os.environ["POSTIZ_API_KEY"]
POSTIZ_BASE_URL = "https://your-postiz-instance.com/api"

def create_scheduled_post(content: str, media_urls: list, 
                          publish_at: str, platforms: list) -> dict:
    """Create a scheduled post through the Postiz API"""
    
    response = requests.post(
        f"{POSTIZ_BASE_URL}/posts",
        headers={
            "Authorization": f"Bearer {POSTIZ_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "content": content,
            "media": media_urls,
            "publishAt": publish_at,
            "platforms": platforms,  # ["instagram", "telegram", "linkedin"]
        }
    )
    
    return response.json()

Blotato:

  • Specializes in Instagram + TikTok
  • An API for automating publishing
  • Paid plans; current prices on the service's website

The full content pipeline: from idea to published post

🎨 Picture this: a content pipeline is like a pizza factory. Dough (the idea) → sauce (Claude's text) → toppings (the image) → oven (the scheduler) → customer (your followers). Automatically, every day, without you.

python
#!/usr/bin/env python3
# content-pipeline.py: the full content pipeline

import anthropic
import requests
import json
import os
from datetime import datetime, timedelta
from typing import List, Dict

claude = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

def generate_weekly_content_plan(brand_context: str, topics: List[str]) -> List[Dict]:
    """
    Generates a weekly content plan with Claude.
    Returns a list of posts with text, hashtags and publishing time.
    """
    response = claude.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=3000,
        messages=[{
            "role": "user",
            "content": f"""Create a weekly content plan for an Instagram account.

Brand context: {brand_context}

Topics for this week: {', '.join(topics)}

For each of the 7 days, create:
1. A post (150-200 words + 15 hashtags)
2. A Stories idea (1-2 sentences)
3. The best time to publish (keep in mind the audience is in the US, Eastern Time)

Response format: a JSON list of objects {{
  "day": "Monday",
  "topic": "...",
  "post_text": "...",
  "hashtags": ["...", ...],
  "stories_idea": "...",
  "publish_time": "HH:MM"
}}

Return only valid JSON."""
        }]
    )
    
    # Parse Claude's JSON response
    content_plan = json.loads("".join(b.text for b in response.content if b.type == "text"))
    return content_plan

def schedule_posts_to_postiz(content_plan: List[Dict], 
                              platform: str = "instagram") -> List[Dict]:
    """Send the post plan to Postiz for scheduling"""
    
    POSTIZ_API_KEY = os.environ["POSTIZ_API_KEY"]
    scheduled_posts = []
    
    # Work out dates starting from next Monday
    today = datetime.now()
    days_ahead = 7 - today.weekday()  # until next Monday
    start_date = today + timedelta(days=days_ahead)
    
    for i, post in enumerate(content_plan):
        post_date = start_date + timedelta(days=i)
        post_text = f"{post['post_text']}\n\n{' '.join(post['hashtags'])}"
        
        # Publishing time
        hour, minute = post['publish_time'].split(':')
        publish_at = post_date.replace(
            hour=int(hour), minute=int(minute), second=0
        ).isoformat()
        
        result = requests.post(
            "https://your-postiz.com/api/posts",
            headers={"Authorization": f"Bearer {POSTIZ_API_KEY}"},
            json={
                "content": post_text,
                "publishAt": publish_at,
                "platforms": [platform],
            }
        )
        
        scheduled_posts.append({
            "day": post["day"],
            "status": "scheduled" if result.status_code == 200 else "error",
            "publish_at": publish_at,
        })
        
        print(f"✅ {post['day']}: scheduled for {publish_at}")
    
    return scheduled_posts

def analyze_performance(platform_stats: Dict) -> str:
    """Analyze the stats with Claude and get recommendations"""
    
    response = claude.messages.create(
        model="claude-haiku-4-5",  # Haiku is faster and cheaper for analysis
        max_tokens=800,
        messages=[{
            "role": "user",
            "content": f"""Analyze the Instagram account's stats for last week:

{json.dumps(platform_stats, ensure_ascii=False, indent=2)}

Give me:
1. The top 3 posts (by engagement rate)
2. What worked well (2-3 points)
3. What to improve next week (2-3 points)
4. The recommended publishing time

Keep it short, in bullet points."""
        }]
    )
    
    return "".join(b.text for b in response.content if b.type == "text")

# Example usage
if __name__ == "__main__":
    # Brand context
    BRAND = """
    Acme Realty is a real estate agency in Ecuador.
    We help Americans find a home and invest.
    Tone: a friendly expert, not a salesperson.
    Audience: Americans aged 35-55 considering a move or an investment.
    """
    
    TOPICS = [
        "Life in Cuenca: what surprised us",
        "What it costs to rent an apartment: a price overview",
        "Top 5 neighborhoods for investment",
        "Documents a foreigner needs to buy property",
        "A client's story: they moved here from Chicago",
        "The legal fine print of a deal",
        "Weekly market overview",
    ]
    
    print("🚀 Generating the weekly content plan...")
    plan = generate_weekly_content_plan(BRAND, TOPICS)
    
    print("\n📅 Scheduling posts in Postiz...")
    scheduled = schedule_posts_to_postiz(plan)
    
    print("\n✅ Content plan created!")
    for post in scheduled:
        print(f"  {post['day']}: {post['publish_at']} — {post['status']}")

A Telegram channel: full automation

In the US, Telegram is less common than Instagram or Facebook, but it's popular in many communities abroad and it's the simplest channel to automate: an official Bot API with none of Meta's paperwork. Discord and Slack bots work on a similar principle.

python
import asyncio
import os
from telegram import Bot
import anthropic
import schedule
import time

BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
CHANNEL_ID = "@acme_realty"  # or a numeric ID

bot = Bot(token=BOT_TOKEN)
claude = anthropic.Anthropic()

async def post_daily_content():
    """Automatic daily posting to a Telegram channel"""
    
    # Get the day's post
    trending_response = claude.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=1000,
        messages=[{
            "role": "user",
            "content": """Write a post for a Telegram channel about real estate in Ecuador.
            
            Format:
            - A headline with an emoji
            - 2-3 paragraphs of content
            - A "What you need to know" list (3-5 points)
            - A CTA (call to action)
            - 800 characters max
            
            Topic: a timely tip on buying or renting property in Ecuador"""
        }]
    )
    
    post_text = "".join(b.text for b in trending_response.content if b.type == "text")
    
    await bot.send_message(
        chat_id=CHANNEL_ID,
        text=post_text,
        parse_mode="Markdown"
    )
    
    print(f"✅ Published: {time.strftime('%Y-%m-%d %H:%M')}")

# Publishing schedule
def run_scheduler():
    schedule.every().day.at("09:00").do(
        lambda: asyncio.run(post_daily_content())
    )
    schedule.every().day.at("18:00").do(
        lambda: asyncio.run(post_daily_content())
    )
    
    while True:
        schedule.run_pending()
        time.sleep(60)

if __name__ == "__main__":
    run_scheduler()

Replying to comments: automation + human oversight

🎨 Picture this: automated comment replies are like the phone menu at a call center. Simple questions ("how much is it?", "how do I reach you?") get answered automatically. Complicated or negative ones get passed to a real person.

python
def classify_and_respond_to_comment(comment: str, post_context: str) -> dict:
    """
    Classifies a comment and drafts a reply.
    Returns: an auto-reply or a flag for manual handling.
    """
    
    response = claude.messages.create(
        model="claude-haiku-4-5",  # Haiku is fast and cheap for classification
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": f"""A comment on a post about real estate in Ecuador:
"{comment}"

Post context: {post_context}

Tasks:
1. Classify it: positive/neutral/negative/spam/question
2. If question or positive/neutral, give a short reply (100 characters max)
3. If negative or spam, answer "MANUAL_REVIEW"

Format: JSON {{
  "type": "...",
  "auto_reply": "..." or "MANUAL_REVIEW",
  "confidence": 0.0-1.0
}}"""
        }]
    )
    
    return json.loads("".join(b.text for b in response.content if b.type == "text"))

# Process comments
comments = [
    "How much is a 2-bedroom apartment in Cuenca?",
    "Thanks, really helpful!",
    "These people are scammers! Don't trust them!",
    "Buy my services at the link in my bio",
]

for comment in comments:
    result = classify_and_respond_to_comment(
        comment, 
        "A post about housing costs in Cuenca"
    )
    
    if result["auto_reply"] == "MANUAL_REVIEW":
        print(f"⚠️ MANUAL REVIEW: {comment[:50]}...")
    else:
        print(f"✅ Auto-reply: {result['auto_reply']}")

Rules for safe automation

  1. Never automate 100%: some of your content should be live and irregular
  2. Pace yourself: don't publish several posts back to back; leave gaps between them
  3. Watch your engagement metrics: a drop in engagement rate can mean you've been flagged as a bot
  4. Store tokens safely: only in environment variables
  5. Review content before it goes out: especially at the beginning
  6. Check versions: model names and API versions change; current ones: What's current

Practice

  1. Create a Telegram bot with BotFather and set up a channel (or a Discord/Slack bot if that's where your audience is)
  2. Write a script: generate a post with Claude → publish it to the channel
  3. Add a schedule: schedule.every().day.at("09:00").do(...)
  4. Install Postiz (self-hosted with Docker, or the paid version)
  5. Run content-pipeline.py to generate a weekly plan

Tools and resources


Key takeaways

Instagram: the official Meta Graph API (requires a Business account) is safe. Browser automation is faster but risks getting the account banned. Postiz/Blotato are the happy medium.

Telegram is the simplest channel to automate. A Bot API with no paperwork, instant publishing, full control.

The content pipeline: generate with Claude → schedule with Postiz → analyze → adjust the strategy. Once it's set up it runs on a schedule, but you still need to check the results with your own eyes regularly.


Next lesson

→ Manus AI: autonomous agents for multi-hour tasks

The mark stays in this browser only and is never sent anywhere. My progress