Pagination & filtering

List endpoints return a collection of resources for your organization under a consistent envelope. This page documents the response shape these endpoints return, the ordering you can rely on, the paging scheme each uses, and the filters each one supports.

Two paging styles

GET /posts and GET /social-accounts use keyset (cursor) pagination: pass limit (default 25, max 100) and follow the next_cursor in each response. GET /media uses offset pagination (limit + offset) and returns a pagination block with the total count. All list calls return 200 with a top-level data array (empty as { "data": [] }, never a 404).

The list envelope

Every list endpoint wraps its results in a top-level data array. Each element is the resource object for that endpoint. Cursor-paginated endpoints add a next_cursor field; the offset-paginated media endpoint adds a pagination object.

200 OK (keyset)
{
  "data": [
    { "id": "…", "created_at": "2026-06-14T12:20:00.000Z" },
    { "id": "…", "created_at": "2026-06-13T18:05:00.000Z" }
  ],
  "next_cursor": "WyIyMDI2LTA2LTEzVDE4OjA1OjAwLjAwMFoiLCIuLi4iXQ"
}

When there are no more rows, next_cursor is null. The cursor is opaque and signed— it encodes the last row’s (created_at, id) as a base64url payload with an HMAC signature appended (payload.signature), so it cannot be forged or hand-constructed. Treat it as a black box: pass it back verbatim as ?cursor=… and never parse, edit, or build one yourself. A malformed, unsigned, or tampered cursor returns 400 (Invalid cursor); its internal format is not part of the API contract and may change.

Ordering

Cursor-paginated lists are ordered by (created_at desc, id desc) — newest first, with id as a stable tiebreaker so rows with identical timestamps page deterministically. GET /media is ordered by created_at desc.

List social accounts

GET /social-accounts — keyset pagination.

Returns connected accounts for your organization, newest first, excluding disconnected ones. All filters below combine with AND.

Query parameterTypeRequiredDescription
limitintegerNoPage size, clamped to 1100 (default 25).
cursorstringNoOpaque keyset cursor from a previous response’s next_cursor.
iduuidNoReturn only the account with this id.
tenant_idstringNoOnly return accounts associated with this end tenant.
platformenumNoOnly return accounts for this platform (e.g. x, linkedin, youtube).
usernamestringNoExact-match account username.
network_unique_idstringNoExact-match platform-side account id (also accepted as networkUniqueId).
curl
# First page (default 25)
curl "https://api.postfuze.com/api/v1/social-accounts" \
  -H "Authorization: Bearer sk_live_…"

# Filter by platform and tenant, larger page
curl "https://api.postfuze.com/api/v1/social-accounts?platform=x&tenant_id=tenant_42&limit=100" \
  -H "Authorization: Bearer sk_live_…"

# Next page
curl "https://api.postfuze.com/api/v1/social-accounts?cursor=WyIyMDI2…" \
  -H "Authorization: Bearer sk_live_…"
200 OK
{
  "data": [
    {
      "id": "a1b2c3d4-0000-4a9f-9b1c-2e7d6f0a1234",
      "platform": "x",
      "tenant_id": "tenant_42",
      "network_unique_id": "1834567890",
      "username": "acme",
      "display_name": "Acme Inc.",
      "status": "active",
      "created_at": "2026-06-01T09:30:00.000Z",
      "updated_at": "2026-06-14T12:00:00.000Z"
    }
  ],
  "next_cursor": null
}

See Social Accounts for the full account object.

List posts

GET /posts — keyset pagination.

Returns your posts, newest first, excluding deleted ones. Each element is a summary object — id, status, is_draft, scheduled_at, published_at, and created_at. To get the full container hierarchy and per-account targets for a post, retrieve it by id.

Query parameterTypeDescription
limitintegerPage size, clamped to 1100 (default 25).
cursorstringOpaque keyset cursor from a previous response’s next_cursor.
statusenumOne of draft, scheduled, queued, publishing, published, partial, failed, canceled.
platformenumPosts with at least one target on this platform.
social_account_iduuidPosts targeting this connected account.
created_after / created_beforeISO 8601Half-open [after, before) range on created_at.
scheduled_after / scheduled_beforeISO 8601Half-open [after, before) range on scheduled_at.
curl
# Scheduled posts for one account, scheduled in a window
curl "https://api.postfuze.com/api/v1/posts?status=scheduled&social_account_id=a1b2c3d4-…&scheduled_after=2026-06-15T00:00:00Z&scheduled_before=2026-06-22T00:00:00Z" \
  -H "Authorization: Bearer sk_live_…"
200 OK
{
  "data": [
    {
      "id": "p1a2b3c4-0000-4a9f-9b1c-2e7d6f0a1234",
      "status": "scheduled",
      "is_draft": false,
      "scheduled_at": "2026-06-16T15:00:00.000Z",
      "published_at": null,
      "created_at": "2026-06-14T12:20:00.000Z"
    }
  ],
  "next_cursor": "WyIyMDI2…"
}

See Post lifecycle for what each status means.

List media

GET /media — offset pagination.

Returns your uploaded media assets, newest first. Unlike the cursor-paginated endpoints, media paging uses limit + offset and returns a pagination object carrying the page parameters and the total matching count.

Query parameterTypeDescription
limitintegerPage size, clamped to 1100 (default 25).
offsetintegerNumber of items to skip (default 0).
kindenumOne of image, video, gif, document, audio.
statusenumMedia status filter (e.g. ready, pending_upload).
200 OK
{
  "data": [ { "id": "…", "kind": "image", "status": "ready", "created_at": "2026-06-14T12:20:00.000Z" } ],
  "pagination": { "limit": 25, "offset": 0, "total": 137 }
}

See Media for the full media object.

Other list endpoints

GET /social-networks returns your configured BYOK OAuth apps under a data array, newest first. See Social Networks for its object shape.

Reading every page

For the cursor-paginated endpoints, keep calling with the previous response’s next_cursor until it comes back null. The example below walks every page of GET /posts.

page-through-posts.ts
type PostSummary = { id: string; status: string; created_at: string };

async function* allPosts(params: Record<string, string> = {}): AsyncGenerator<PostSummary> {
  let cursor: string | null = null;
  do {
    const qs = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) });
    const res = await fetch(`https://api.postfuze.com/api/v1/posts?${qs}`, {
      headers: { Authorization: `Bearer ${process.env.POSTFUZE_API_KEY}` },
    });
    if (!res.ok) throw new Error(`List failed: ${res.status}`);
    const body = (await res.json()) as { data: PostSummary[]; next_cursor: string | null };
    yield* body.data;
    cursor = body.next_cursor;
  } while (cursor);
}

let n = 0;
for await (const _post of allPosts({ status: 'published' })) n += 1;
console.log(`Loaded ${n} published posts`);

Offset paging for media

For GET /media, increment offset by your limit until offset + data.length reaches pagination.total.