# How to Build an AI Social Media Agent (2026) · PostLake > Markdown version of https://postlake.dev/guides/ai-social-media-agent . The canonical page for humans. > PostLake is the social media API for AI agents: https://postlake.dev/llms.txt Build an *AI agent* that runs your socials. Most agent demos die on platform integrations. PostLake is the hands: one publish call. One analytics shape, MCP if you want zero wrapper code. Your model still decides what to say, PostLake only executes and reports. **In short:** Give the agent publish + analytics (API tools or MCP). It posts everywhere, reads one metric shape, and retries safely with idempotency keys. ## When this guide is for you The product is the agent: it should decide, publish, measure, and improve without you maintaining platform APIs. ## Before you start Same setup the [docs quickstart](https://docs.postlake.dev/quickstart.md) uses. Do this once: - Sign up at [app.postlake.dev](https://app.postlake.dev) and verify your email (unlocks free credits). - On **Channels**, create a **profile** (e.g. `my-brand`) and connect at least one account. **Bluesky is the fastest first channel**: no app review. Instagram/TikTok/Facebook need each platform's review before API posting. - Account menu → **API Keys** → create a key (`sk_live_…`). It shows once; treat it like a password. ## Two tools: publish and learn The agent needs hands (publish) and feedback (analytics). Both match the docs. Same auth. Same profile. Same response discipline: ``` import os, uuid, requests HEADERS = {"Authorization": f"Bearer {os.environ['POSTLAKE_API_KEY']}"} def publish(text: str) -> dict: """Publish to every account on the profile. Always check targets[].""" return requests.post( "https://api.postlake.dev/v1/posts", headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())}, json={"text": text, "profile": "my-brand"}, ).json() def performance(period: str = "30d") -> dict: """Same metric names on every network, agent-friendly.""" return requests.get( f"https://api.postlake.dev/v1/analytics?period={period}", headers=HEADERS, ).json() # Or skip wrappers: connect https://api.postlake.dev/mcp (OAuth). ``` ## Step by step 1. Connect the accounts the agent may manage under one profile (Bluesky first for a fast smoke test). 1. Expose **publish** (`POST /v1/posts`) and **performance** (`GET /v1/analytics`), or use MCP tools for both. 1. Prompt the agent to treat `partial` as mixed results and to never claim success without reading `targets`. 1. Close the loop: after a few days, call analytics and let the model adjust format/network mix. 1. For calendars, add `scheduledAt` (UTC, or a local time plus `timezone`) so the agent can plan ahead without a second product. ## Read the response (don't skip this) You get one `Post` with an overall `state` and a `targets[]` array. One entry per account. **Always check each target**; partial success is normal. ``` { "id": "post_a1b2c3", "state": "partial", "targets": [ { "platform": "bluesky", "state": "published", "url": "https://bsky.app/…" }, { "platform": "linkedin", "state": "failed", "error": { "type": "invalid_request", "message": "…", "retryable": false } } ] } ``` - `published`: every target succeeded. `partial`: some published, some failed. `failed`: none published. `processing`: still going (async networks like TikTok). - **You're only charged for targets that actually publish.** Failed targets cost nothing. - Send an `Idempotency-Key` header on writes so a retry never double-posts. - Full detail: [Publishing](https://docs.postlake.dev/publishing) · [Errors & retries](https://docs.postlake.dev/errors) ## Where the post goes Same rules as the docs. Pick one addressing style: - **By profile**: `"profile": "my-brand"` posts to every account under that profile (the name on Channels). - **Filter networks**: `"platforms": ["bluesky", "linkedin"]` narrows that set. It is a **filter, not a selector**: if you have two Pinterest boards, both match `pinterest`. - **By account id**: `"accounts": ["acc_…"]` for exact channels (copy an id on Channels, or `GET /v1/social-accounts`). - One profile and you omit `profile`? PostLake uses that profile. Multiple profiles and you omit it? You'll get an error that names them. See [Publishing: where to post](https://docs.postlake.dev/publishing). ## Do more (same API) - **Schedule**: add `scheduledAt` as UTC (trailing `Z`), or a naive local time plus `timezone` (IANA, e.g. `Europe/London`). Credits charge when it fires. [Scheduling docs](https://docs.postlake.dev/scheduling) · [scheduling guide](/guides/schedule-posts-api). - **Media**: `POST /v1/media`, then pass the `med_…` id in `media`. [Media docs](https://docs.postlake.dev/media). - **Per-network caption**: `textOverrides` (e.g. shorter text for X). **Per-network options**: `platformOptions` (Pinterest `boardId`, TikTok privacy, …). Live option lists: `GET /v1/platforms/{platform}`. - **Validate first**: `POST /v1/posts/validate` runs the same checks without publishing (free). - Call `GET /v1/platforms` (or MCP equivalent) before posting so the agent respects length/media rules. - Use `POST /v1/posts/validate` on drafts, free. Same checks as publish. ## Pitfalls specific to this path - Pasting a long-lived key into the prompt is unsafe, prefer MCP OAuth or a server-side tool runner. - New UUID every blind retry of the *same* user ask can still double-post; key by conversation/message id when you can. - Analytics is free; publishing spends credits (X costs more). Budget in the agent policy. Want zero wrapper code? Connect the hosted [MCP server](https://docs.postlake.dev/mcp) (`https://api.postlake.dev/mcp`) over OAuth. Same accounts and responses as this API path. [Agents overview](/agents/). ## Common questions ### How do I build an AI agent that posts to social media? Give it PostLake publish (POST /v1/posts) and analytics (GET /v1/analytics), or connect MCP. The agent never talks to nine platform APIs, only one normalised contract. ### What stops an AI agent from double-posting? Idempotency-Key on each logical publish. Retries with the same key return the original Post instead of creating another. ### How does the agent know what to post next? GET /v1/analytics returns the same metric names across networks. The agent compares without a translation layer. See the analytics guide and docs. ### MCP or REST? MCP (https://api.postlake.dev/mcp) is best when the host supports OAuth tools. REST tools are fine inside your own backend agent runtime. ## Go deeper in the docs These guides stay short on purpose. Canonical behaviour lives here: [Docs ### Quickstart Account, first channel, first post, MCP or API.](https://docs.postlake.dev/quickstart) [Docs ### Publishing Request body, addressing. Partial success, lifecycle.](https://docs.postlake.dev/publishing) [Docs ### Scheduling UTC or local time plus timezone, edit/cancel, fire-time credits.](https://docs.postlake.dev/scheduling) [Docs ### Platforms Limits, media rules, live platformOptions.](https://docs.postlake.dev/platforms) Also: [Media](https://docs.postlake.dev/media) · [Errors](https://docs.postlake.dev/errors) · [MCP](https://docs.postlake.dev/mcp) · [Analytics](https://docs.postlake.dev/analytics) ## Related guides - [Post with LangChain](https://postlake.dev/guides/langchain.md) - [Post with CrewAI](https://postlake.dev/guides/crewai.md) - [Cross-platform analytics](https://postlake.dev/guides/social-media-analytics-api.md) - [Post everywhere at once](https://postlake.dev/guides/post-to-all-social-media-at-once.md) - [Social media API for AI agents](https://postlake.dev/guides/social-media-api-for-ai-agents.md) [All guides](https://postlake.dev/guides/index.md) · [Full docs](https://docs.postlake.dev/) · [llms.txt](https://postlake.dev/llms.txt) Plain markdown for agents: https://postlake.dev/guides/ai-social-media-agent.md · https://postlake.dev/llms.txt