Layers

GET /v1/projects/:projectId/social-accounts

List connected and leased accounts on a project.

View as Markdown
GET/v1/projects/:projectId/social-accounts
Phase 1stable
Auth
Bearer
Scope
social:read

List every social account attached to the project - OAuth-connected accounts from your end-customers and leased TikTok accounts provisioned by Layers. Both appear in the same list with a leased boolean so you can tell them apart.

Use this endpoint before scheduling a post - its socialAccountId values are what you pass into POST /v1/content/:id/schedule targets.

Path
  • projectId
    stringrequired
    Project whose accounts you want.
Query
  • platform
    stringoptional
    Filter to a single platform.
    One of: tiktok, instagram
  • status
    stringoptional
    Filter by connection state.
    One of: connected, reauth_required, disconnected
  • leased
    booleanoptional
    true returns only leased accounts, false excludes them.
  • limit
    numberoptional
    Page size. Defaults to 50, capped at 200.
  • cursor
    stringoptional
    Pagination token from the previous response's `nextCursor`. Pass back verbatim — it's the `connectedAt` of the last item on the previous page.

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/social-accounts?platform=tiktok&status=connected" \
  -H "Authorization: Bearer lp_..."
const { items } = await layers.social.listAccounts({
  projectId: "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  platform: "tiktok",
  status: "connected",
});

const publishable = items.filter((a) => a.status === "connected");
accounts = layers.social.list_accounts(
    project_id="prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
    platform="tiktok",
    status="connected",
)
publishable = [a for a in accounts["items"] if a["status"] == "connected"]

Response

200Account list
{
  "items": [
    {
      "socialAccountId": "sa_9c1e42a0-b7f3-4e5d-a2c1-8b4f5e6c7d8e",
      "platform": "tiktok",
      "handle": "acmecoffee",
      "avatarUrl": "https://media.meetsift.com/tiktok/acmecoffee/avatar.jpg",
      "status": "connected",
      "leased": false,
      "connectedAt": "2026-04-18T19:06:42Z",
      "tokenExpiresAt": "2026-05-18T19:06:42Z"
    },
    {
      "socialAccountId": "sa_9c1e42a0-b7f3-4e5d-a2c1-8b4f5e6c7d8e",
      "platform": "tiktok",
      "handle": "coffee.reviews.daily",
      "avatarUrl": "https://media.meetsift.com/tiktok/coffee.reviews.daily/avatar.jpg",
      "status": "connected",
      "leased": true,
      "connectedAt": "2026-04-12T08:00:00Z",
      "tokenExpiresAt": null
    }
  ],
  "nextCursor": null
}

Pagination

Results are ordered by connectedAt descending. To fetch the next page, pass the previous response's nextCursor back as the cursor query param:

let cursor: string | null = null;
const all: SocialAccount[] = [];

do {
  const url = new URL(`https://api.layers.com/v1/projects/${projectId}/social-accounts`);
  if (cursor) url.searchParams.set("cursor", cursor);
  const { items, nextCursor } = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.LAYERS_API_KEY!}` },
  }).then(r => r.json());
  all.push(...items);
  cursor = nextCursor;
} while (cursor);

nextCursor is null when there's nothing more to fetch. Don't compute or modify the cursor — it's an opaque server-issued value (currently the connectedAt of the last item on the page, but treat it as opaque so it can evolve).

Status values

statusMeaningWhat to do
connectedToken valid, scheduling allowed.Normal operation.
reauth_requiredToken expired or revoked upstream.Create a reauth URL and send the user through consent again.
disconnectedYou or the user called DELETE, or a leased account was released. Scheduled posts were canceled. (The list endpoint excludes soft-deleted rows by default - disconnected accounts surface only via the DELETE response or audit log.)Reconnect via oauth-url if the user wants back in.

Errors

StatusCodeWhen
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks social:read.
404NOT_FOUNDProject not in your organization.
429RATE_LIMITEDRead budget exhausted.

See also

On this page