Layers

GET /v1/projects/:projectId/scheduled-posts

List scheduled posts on a project. Filter by status, window, or account.

View as Markdown
GET/v1/projects/:projectId/scheduled-posts
Phase 1stable
Auth
Bearer
Scope
publish:read

Enumerate scheduled posts on a project. Useful for populating a queue view in your UI, batch-polling terminal states after a bulk schedule, or reconciling against your own post ledger.

Paginates by cursor. Default sort is scheduledFor descending (most-recent-first), which matches a "recent activity" view - use since/until to window a queue.

Path
  • projectId
    stringrequired
    Project whose posts you want.
Query
  • status
    string | string[]optional
    One of queued, publishing, draft, published, failed, canceled. Repeat the parameter (e.g. `?status=queued&status=publishing`) to filter on multiple statuses.
  • socialAccountId
    stringoptional
    Scope to one connected account.
  • since
    string (ISO 8601, UTC Z)optional
    Inclusive lower bound on scheduledFor.
  • until
    string (ISO 8601, UTC Z)optional
    Inclusive upper bound on scheduledFor.
  • cursor
    stringoptional
    Opaque pagination cursor from a previous response.
  • limit
    integeroptionaldefault: 50
    Page size, 1–100.

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/scheduled-posts?status=queued&status=publishing&limit=100" \
  -H "Authorization: Bearer lp_..."
async function* walkAllPending(projectId: string) {
  let cursor: string | undefined;
  do {
    const page = await layers.publishing.listScheduledPosts({
      projectId,
      status: ["queued", "publishing"],
      limit: 100,
      cursor,
    });
    yield* page.items;
    cursor = page.nextCursor ?? undefined;
  } while (cursor);
}
def walk_all_pending(project_id: str):
    cursor = None
    while True:
        page = layers.publishing.list_scheduled_posts(
            project_id=project_id,
            status=["queued", "publishing"],
            limit=100,
            cursor=cursor,
        )
        yield from page["items"]
        cursor = page.get("nextCursor")
        if not cursor:
            return

Response

200Page of posts
{
  "items": [
    {
      "id": "sp_4c8e7d2f-9a1b-4c3d-8e7f-2a1b3c4d5e60",
      "containerId": "cnt_8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999",
      "socialAccountId": "sa_71b2a4e5-8c3f-4d1a-9e7b-2c5d8f0a1b22",
      "platform": "tiktok",
      "mode": "publish",
      "status": "queued",
      "scheduledFor": "2026-04-19T14:00:00Z",
      "attemptedAt": null,
      "publishedAt": null,
      "externalUrl": null
    },
    {
      "id": "sp_7061bc99-4b63-4afb-96ad-408140f4187e",
      "containerId": "cnt_9a102945-52bb-4e92-aee3-96ebc7bf09b7",
      "socialAccountId": "sa_67857146-69a8-4e23-94cb-499e34ae43e5",
      "platform": "instagram",
      "mode": "publish",
      "status": "published",
      "scheduledFor": "2026-04-18T16:00:00Z",
      "attemptedAt": "2026-04-18T16:00:11Z",
      "publishedAt": "2026-04-18T16:00:29Z",
      "externalUrl": "https://www.instagram.com/p/DYgPEhiCeSU/"
    }
  ],
  "nextCursor": "cur_01HXZP8K9M4N5P6QRSTU"
}

externalUrl for Instagram rows. When platform: "instagram" and status: "published", externalUrl carries the Graph API permalink (instagram.com/p/<shortcode> or instagram.com/reel/<shortcode>). In rare network-blip cases the publisher's optional ?fields=permalink follow-up fetch fails and externalUrl lands null even though externalId + publishedAt are populated. TikTok rows always have externalUrl populated when externalId is — no equivalent edge case. See GET /v1/scheduled-posts/:id.

Errors

StatusCodeWhen
422VALIDATIONsince > until, limit out of range, bad status value.
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks publish:read.
404NOT_FOUNDProject not in your organization.

See also

On this page