# Connect Social Accounts to Your App (2026) · PostLake > Markdown version of https://postlake.dev/guides/let-users-connect-social-accounts . The canonical page for humans. > PostLake is the social media API for AI agents: https://postlake.dev/llms.txt Let your users connect their *social accounts*. Upload-Post and Ayrshare call this a JWT or user profile link. You create a bucket per customer, hand them a signed URL, they approve access on the network's screen, then your backend publishes. PostLake's version is POST /v1/connect-link with profile and returnUrl. The hosted page and the OAuth consent still say PostLake unless you bring your own platform app credentials (BYOK). That BYOK step is white-label OAuth consent, not a separate product. Do not mint POST /v1/app-link for end users: that is a 12-hour dashboard session for you, not a connect flow for them. **In short:** One PostLake workspace, one profile per end user, a signed connect link with returnUrl, then publish with acc_ ids from your backend. Rate limits are per profile, with a workspace ceiling. BYOK is how the OAuth consent screen shows your app name. ## When this guide is for you You are building a product whose users connect their own social accounts, and you want PostLake to run OAuth, publishing, and analytics instead of holding nine platform apps yourself. ## 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. - You do not connect your customers' networks in **Channels**. Each customer connects through the signed link you mint. ## Working example One PostLake key on your backend. One profile per customer. Then this: ``` import { PostLake } from "postlake"; const pl = new PostLake(process.env.POSTLAKE_API_KEY); // 1. One profile per end user (the tenant bucket). const profile = await pl.profiles.create({ name: "user-" + userId }); // 2. Mint a 30-minute connect link. After OAuth they land on returnUrl. const { url } = await pl.connectLink({ profile: profile.username, returnUrl: "https://yourapp.example/social-connected", platforms: ["instagram", "tiktok", "linkedin"], }); // Send `url` to the browser. Never send your API key. // 3. On returnUrl, read connect_status=success|cancelled|error. // Then list that profile's channels and store the acc_ ids. const { data: accounts } = await pl.socialAccounts.list({ profile: profile.username }); // 4. Publish from your backend. Credits meter successful publishes. const post = await pl.posts.create({ text: "Shipped in our app", accounts: accounts.map((a) => a.id), }); // 5. Analytics and disconnect stay scoped to the same profile. await pl.analytics.get({ period: "30d", profile: profile.username }); await pl.socialAccounts.disconnect(accounts[0].id); ``` ## Step by step 1. Keep one PostLake account for your company. Create a `profile` per end user. That is the isolation boundary. 1. From your backend, `POST /v1/connect-link` with an existing `profile` and a `returnUrl` (https, or http on localhost). An unknown profile returns 404 rather than creating an unscoped flow. Optionally pass `platforms` so the hosted page only shows the networks you support. 1. Open the returned 30-minute URL in the user's browser. PostLake returns them to your app with `connect_status=success|cancelled|error`, plus `platform` and `profile`. Error outcomes also include a stable `error_code` for your UI and logs. 1. Subscribe to `account.connected` and `account.disconnected`. List `GET /v1/social-accounts?profile=` and store the `acc_…` ids against that user. 1. Publish with `POST /v1/posts` and `accounts: [acc_…]`. Read `GET /v1/analytics?profile=` so one customer's numbers do not mix with another's. 1. When they disconnect in your UI, `DELETE /v1/social-accounts/{id}`. Do not hand them `POST /v1/app-link`. 1. Read `GET /v1/me/limits` for both envelopes. Free and credit packs share 120 API calls and 12 publishes per minute per profile, with a workspace ceiling of 2,400 API calls and 180 publishes per minute. Paid plans raise both. Pass `profile` (or post with that profile's `acc_` ids) so one customer cannot spend another customer's headroom. Networks still apply their own caps. ## 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). - SaaS overview: [SaaS products](/use-cases/saas-products). - White-label consent (your app name on Instagram, TikTok, LinkedIn): [store your own platform credentials](https://docs.postlake.dev/api/post-credentials). ## Pitfalls specific to this path - A connect link without `profile` can show every channel in your workspace after OAuth. Always mint one scoped to that user. - Rate limits are per profile and per workspace. Credits are per PostLake account. Pass profile on list, publish, and analytics. Networks still refuse with rate_limited when TikTok, Instagram or others throttle the channel. - Managed keys mean the OAuth consent screen names PostLake. White-label consent (your app name on Instagram, TikTok, LinkedIn) is BYOK: store credentials with `POST /v1/credentials`. - The hosted connect page is PostLake-branded. BYOK changes the network consent screen, not that page. Prefer an assistant over the 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 ### Is this a white-label social media API? The publishing API is yours to wrap. Your users stay in your product except for two screens: PostLake's hosted connect page, and the network's OAuth consent. White-label consent (Instagram showing your app name, not PostLake) needs your own platform credentials via POST /v1/credentials. The hosted connect page stays PostLake-branded either way. ### Can my users connect social accounts without seeing PostLake? They will see PostLake's hosted connect page, then the network's own consent screen. The consent screen shows your app name only if you bring your own platform credentials (BYOK). There is no separate publishable key to embed in a browser. ### Are connected accounts unlimited on PAYG? Yes. Credit packs do not change the plan. Connections are unlimited on Free and every paid plan. Credits meter successful publishes, not connections. ### Are rate limits per user or per my PostLake account? Both. GET /v1/me/limits returns workspace apiPerMinute and postsPerMinute, plus rateLimits.profile for one Channels profile. Free and credit-pack accounts get 120 API calls and 12 publishes per minute per profile, and a workspace ceiling of 2,400 / 180. Pass profile on reads and publishes so users are isolated. Ayrshare's 300 calls / 5 minutes is the same idea, per profile. ### Is analytics included if we only buy credit packs? Yes. Analytics is not gated by plan. Pass profile on GET /v1/analytics so each customer's rollup stays in their bucket. ## 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 Node.js](https://postlake.dev/guides/nodejs.md) - [Post with Python](https://postlake.dev/guides/python.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/let-users-connect-social-accounts.md · https://postlake.dev/llms.txt