# How to Schedule Social Media Posts via API (2026) · PostLake > Markdown version of https://postlake.dev/guides/schedule-posts-api . The canonical page for humans. > PostLake is the social media API for AI agents: https://postlake.dev/llms.txt Schedule social posts with one *API* call. Scheduling is one field on the publish call, not a second product. Same addressing and media as an immediate post. Pass timezone (IANA) with a naive local time, or send UTC with a trailing Z. Invalid media is refused when you schedule, not at fire time. The dashboard has a calendar of the queue. For the agent loop (draft a week, approve, then fire), see the content calendar guide. **In short:** Add scheduledAt (UTC with a trailing Z, or a naive local time plus timezone) to the same /v1/posts body. Credits charge when it fires. Invalid media is refused at create. Edit/cancel while state is scheduled. ## When this guide is for you You want calendar-style publishing from code or an agent without standing up a separate scheduler product. ## 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. ## Schedule a post Same call you'd use to publish now, plus `scheduledAt` (UTC, or a local time with `timezone`): ``` curl -X POST https://api.postlake.dev/v1/posts \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: friday-recap-001" \ -d '{ "text": "Friday recap", "profile": "my-brand", "scheduledAt": "2026-08-13T07:00:00", "timezone": "Europe/London" }' # Response state should be "scheduled" until fire time. ``` ## Step by step 1. Build the same body you'd use to publish now (`text` + `profile` / `accounts`). 1. Add `scheduledAt`. UTC with a trailing `Z`, or a naive local time together with `timezone` (IANA, e.g. `Europe/London`). 1. Call `POST /v1/posts/validate` first for TikTok photo posts (JPEG/WebP, max 1080×1920; PNG is converted to JPEG with a warning). 1. Expect `state: "scheduled"`. At fire time it moves to `processing` then `published` / `partial` / `failed`. 1. Before it fires: `PATCH /v1/posts/{id}` to edit, `DELETE /v1/posts/{id}` to cancel. 1. Subscribe to webhooks (or poll) for fire-time results instead of assuming success at schedule time. ## 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) - **Edit / cancel**: while `state` is `scheduled`: `PATCH /v1/posts/{id}` or `DELETE /v1/posts/{id}`. [Scheduling docs](https://docs.postlake.dev/scheduling). - **Media & overrides**: same as an immediate post: `media`, `textOverrides`, `platformOptions`. - **Notify on fire**: subscribe to [webhooks](https://docs.postlake.dev/webhooks) instead of polling. - **Validate first**: `POST /v1/posts/validate` (free) before you queue a week of content. ## Pitfalls specific to this path - A naive datetime without `timezone` is a 400. Sending local time as UTC (trailing Z, no timezone) still fires at the wrong hour. - Credits charge when it **publishes**, not when you schedule. A cancel before fire costs nothing. - Don't treat schedule-time 200 as "live on social", wait for fire-time target states. Prefer an assistant over Schedule posts via API code? Point it at [MCP](https://docs.postlake.dev/mcp) (`https://api.postlake.dev/mcp`). Prefer the dashboard? Use [Quickstart → No code](https://docs.postlake.dev/quickstart). ## Common questions ### How do I schedule a social media post with an API? POST /v1/posts with scheduledAt. Send UTC with a trailing Z, or a naive local time plus timezone (IANA, e.g. Europe/London). PostLake queues it and publishes at that time. See docs → Scheduling. ### What time format does scheduledAt use? ISO-8601. UTC with a trailing Z, or a naive local time together with timezone. Unparseable values and naive times without a zone get a clear 400. ### Can I cancel or change a scheduled post? Yes, while state is scheduled: PATCH /v1/posts/{id} to edit, DELETE /v1/posts/{id} to cancel. ### Do I need Postiz or another calendar app for this? No. scheduledAt plus timezone is the calendar. The dashboard has a month view of the queue. If an agent is filling the week, use drafts so nothing ships until you approve. Postiz is the right pick only if you want to live in a drag-and-drop calendar UI or self-host the app. ### Can an AI agent fill the calendar? Yes. Point it at https://api.postlake.dev/mcp and ask it to schedule the week. Walkthrough: the agent content calendar guide. ## 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 everywhere at once](https://postlake.dev/guides/post-to-all-social-media-at-once.md) - [Post with n8n](https://postlake.dev/guides/n8n.md) - [Post with Python](https://postlake.dev/guides/python.md) - [Build an AI social agent](https://postlake.dev/guides/ai-social-media-agent.md) - [Agent content calendar](https://postlake.dev/guides/ai-content-calendar.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/schedule-posts-api.md · https://postlake.dev/llms.txt