Paginated lists

Cursor-paginated lists: limit from 1 to 100, an opaque cursor, the data, nextCursor and hasMore envelope, the Link rel="next" header, full loops.

api.memojin.com/v1 · Contract 1.0.0-beta.2 · Beta

Sections · Pagination

Lists (contents, folders, memocards, questions, spaces, study sessions) are cursor-paginated. Every page answers { data, nextCursor, hasMore }.

Parameters

  • limit: items per page, 1 to 100 (20 by default).
  • cursor: the nextCursor of the previous page; omitted for the first page.

The page

GET/v1/contents/{contentId}/memocardsScope memocards:read
Request
curl "https://api.memojin.com/v1/contents/3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87/memocards?limit=20" \
  -H "Authorization: Bearer $MEMOJIN_API_KEY"
Response 200
{
  "data": [
    {
      "id": "c5e8a1b2-3d4f-4a6b-9c8d-7e6f5a4b3c21",
      "contentId": "3f1c2b0e-8d4a-4f7e-9a51-3c2d1e0f9b87",
      "front": "Where does photosynthesis take place?",
      "back": "In the chloroplasts, mainly in the leaves.",
      "explanation": null,
      "retention": 0.82,
      "lastReviewedAt": "2026-10-03T18:12:00.000Z",
      "createdAt": "2026-10-01T14:05:00.000Z",
      "updatedAt": "2026-10-01T14:05:00.000Z"
    }
  ],
  "nextCursor": null,
  "hasMore": false
}

hasMore is true while another page follows; nextCursor is then to be passed as cursor (it is null on the last page). The same address is in the Link: <…>; rel="next" header.

Rules

  • A cursor is opaque: do not decode it, do not build one.
  • It is short-lived and tied to its limit: keep the same limit from one page to the next, and do not store a cursor for later.
  • An unreadable or expired cursor, or one obtained with another limit, answers 400 invalid_cursor: start again from the first page.
  • GET /v1/search is not paginated: it returns at most limit results, in results.

Reading a whole list

// Every memocard of a content, 100 per page
const headers = { Authorization: `Bearer ${process.env.MEMOJIN_API_KEY}` };
const cards = [];
let cursor = null;
do {
  const url = new URL(`https://api.memojin.com/v1/contents/${contentId}/memocards`);
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const res = await fetch(url, { headers });
  if (!res.ok) throw new Error((await res.json()).error.code);
  const page = await res.json();
  cards.push(...page.data);
  cursor = page.hasMore ? page.nextCursor : null;
} while (cursor);

The same loop in Python

import os
import requests

headers = {"Authorization": f"Bearer {os.environ['MEMOJIN_API_KEY']}"}
cards, cursor = [], None
while True:
    params = {"limit": 100, **({"cursor": cursor} if cursor else {})}
    res = requests.get(f"https://api.memojin.com/v1/contents/{content_id}/memocards", params=params, headers=headers, timeout=30)
    res.raise_for_status()
    page = res.json()
    cards.extend(page["data"])
    if not page["hasMore"]:
        break
    cursor = page["nextCursor"]