XiaoZhuJian Developer docs
Settings → API

XiaoZhuJian for developers

Getting started

There are two ways in, and they reach the same things. Both make cards through the pipeline the app uses, count them against the same monthly allowance, and put them in the same place — your cards, where you can study them, download them as an Anki deck, or put them in a stack. Each endpoint below shows the REST call and, where there is one, the MCP tool an assistant would use for the same thing; the switches in the header show or hide either.

REST From your own code
  1. Be on Premium or Max (Settings → Subscription).
  2. Make a key at Settings → API. It is shown once; keep it somewhere a server can read and a browser cannot.
  3. Call the API:
curl https://xiaozhujian.study/api/v1/account \
  -H "Authorization: Bearer $XZJ_API_KEY"
MCP From Claude, ChatGPT or another assistant
  1. Be on Premium or Max (Settings → Subscription).
  2. Copy the connector link from Settings → Connected apps: https://xiaozhujian.study/mcp.
  3. In Claude, Settings → Connectors → Add custom connector; in ChatGPT or another assistant, add a custom connector, app or MCP server. Paste the link, press Connect, sign in to XiaoZhuJian, and choose Allow.

Then try: “Make me a stack of ten cards of words people use online.”

Authentication

Every request carries a key in the Authorization header, as a bearer token:

Authorization: Bearer xzj_live_…

A key stands for the account that made it, and that account's plan is checked on every request: a key held through a lapse is answered 402 plan_required rather than revoked, and works again the moment the account is Premium or above again. Keys never expire on their own.

  • A key is shown once. We store only a hash of it. Lose it and you make a new one.
  • Rotate rather than replace. Rotating a key issues a successor under the same name and lets the old one keep working for a grace period you choose (now, 1h, 24h), so a running service can pick up the new secret with no gap.
  • Revoke stops a key at once, on any plan. Up to 10 live keys per account.
  • Keep it out of browsers. The API never reads a cookie and sets no CORS headers: a key belongs in a server, where nobody else can read it.

A missing or invalid key is a 401 with a WWW-Authenticate header.

Connecting an assistant

XiaoZhuJian is a Model Context Protocol server, for Claude, ChatGPT and any other assistant that takes a custom connector. It needs no key: the assistant sends you to a consent page, you sign in and press Allow, and from then on it can make and find cards for you. Premium and above; the plan is checked on every call, so a lapse stops it and an upgrade restarts it without reconnecting.

The transport is streamable HTTP, stateless, at POST https://xiaozhujian.study/mcp, with OAuth 2.1 (dynamic registration, PKCE) for authorization — metadata at https://xiaozhujian.study/.well-known/oauth-authorization-server. A tool's result carries the learner's own text between <<<CARDS / <<<STACKS markers, as data for the assistant to show rather than instructions to follow. The tools are listed under the endpoints they correspond to, word for word as tools/list describes them.

Conventions

Requests with a body send JSON (Content-Type: application/json); every answer is JSON. Every object says what it is in object; ids are opaque strings; times are RFC 3339 in UTC; field names are snake_case. The version is in the path (/api/v1) and within it fields are only ever added, so ignore keys you do not know. Every response carries an X-Request-Id — quote it when you write to us about a request.

Errors

An error is an HTTP status in the 4xx or 5xx range and one shape of body. type is one of five families a client can branch on; code says exactly what and is stable; message is for a person and may change; param, when present, names the offending parameter.

{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "`terms` (‘tangping’) has no Chinese characters. Send the Mandarin in Simplified Chinese.",
    "param": "terms"
  }
}
StatusCodeMeaning
400parameter_invalidA parameter is malformed; param names it.
400parameter_missingA required parameter was left out.
400invalid_jsonThe body is not valid JSON.
400invalid_bodyThe body is JSON but not an object.
400parameter_unknownA query parameter or body field the endpoint does not take; param names it. Nothing is ignored silently.
401api_key_missingNo Authorization: Bearer header.
401api_key_invalidThe key is malformed (truncated or mistyped), not one of ours, or was revoked or rotated.
402plan_requiredThe account is not on Premium or above. The key stays valid.
404resource_missingNo such card, stack or endpoint — or not this account's.
405method_not_allowedThe endpoint exists but not with this method.
409conflictThe stack changed under the request; read it again.
409stack_limit_reachedThe account already holds 100 stacks.
409too_many_cardsThe stack would exceed 100 cards.
429rate_limit_exceededMore than 120 requests in a minute; wait for Retry-After.
429monthly_limit_exceededThe month's generation allowance is spent.
502card_not_madeThe card could not be made just now; retry in a minute.
503service_unavailableA dependency of ours did not answer; retry.
500internal_errorOur fault. The X-Request-Id header identifies the request.

The types: invalid_request_error (4xx about the request), authentication_error (401), permission_error (402, 403), rate_limit_error (429) and api_error (5xx).

Pagination

A list endpoint answers a list object: data holds the page, has_more says whether another follows, and url is the endpoint. Pages are cursor-based: pass the last object's id as starting_after to get the next, and limit (1–100, default 20) for the page size. A card search is ranked, not paged.

{
  "object": "list",
  "url": "/api/v1/cards",
  "has_more": true,
  "data": [
    "…"
  ]
}

Rate limits

Two limits apply. Requests: 120 a minute per key; past that, 429 rate_limit_exceeded with a Retry-After header. Generations: every card made counts against the account's monthly allowance, the same one the app spends; Retrieve the account says what is left, and a request that would overrun it is 429 monthly_limit_exceeded before anything is made.

Cards

The card object

One flashcard: its terms (each with its pinyin, a short translation and, for a term typed in another language, the Chinese rendering the sentence used), the sentence with its pinyin, translation and word-by-word segments, the audio (a public URL, or null for a card with none), what it was made with, and its review state in the app's spaced-repetition schedule. is_sentence marks a card whose one term is itself a sentence.

{
  "object": "card",
  "id": "cs_01j9x7d2k8b3",
  "terms": [
    {
      "text": "躺平",
      "pinyin": "tǎng píng",
      "translation": "to lie flat; to opt out",
      "rendering": null
    }
  ],
  "is_sentence": false,
  "sentence": {
    "text": "工作太累了,他决定躺平一段时间。",
    "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
    "translation": "Work was exhausting, so he decided to lie flat for a while.",
    "segments": [
      {
        "text": "工作",
        "pinyin": "gōng zuò"
      },
      {
        "text": "太",
        "pinyin": "tài"
      },
      {
        "text": "累",
        "pinyin": "lèi"
      },
      {
        "text": "了",
        "pinyin": "le"
      },
      {
        "text": ",",
        "pinyin": ""
      },
      {
        "text": "他",
        "pinyin": "tā"
      },
      {
        "text": "决定",
        "pinyin": "jué dìng"
      },
      {
        "text": "躺平",
        "pinyin": "tǎng píng"
      },
      {
        "text": "一段",
        "pinyin": "yí duàn"
      },
      {
        "text": "时间",
        "pinyin": "shí jiān"
      },
      {
        "text": "。",
        "pinyin": ""
      }
    ]
  },
  "audio": {
    "url": "https://audio.example.com/audio/static/output_7f3a….mp3",
    "model": "qwen-audio-3.0-tts-flash",
    "voice": "Cherry"
  },
  "language": "zh-CN",
  "level": "HSK4",
  "style": "general",
  "model": "qwen3.7-plus",
  "context": null,
  "idiomatic_notes": [],
  "review": {
    "state": "new",
    "due": null,
    "review_count": 0,
    "lapses": 0,
    "last_reviewed_at": null
  },
  "url": "https://xiaozhujian.study/cards/cs_01j9x7d2k8b3",
  "created_at": "2026-10-06T09:14:02.511000+00:00",
  "updated_at": "2026-10-06T09:14:02.511000+00:00"
}

List or search cards

RESTGET/api/v1/cards

Every card the account has made, newest first — or, with query, the cards it finds: cards whose Chinese, pinyin (tone marks optional) or terms match come first, then ones matched only by their English, newest first within each. A search is ranked rather than paged, so starting_after cannot be combined with it.

ParameterTypeDescription
query
query
stringChinese, pinyin or English to search for. At most 200 characters.
limit
query
integerHow many to return, 1–100. Default 20.
starting_after
query
stringA card id; the page after it. Not with query.

Returns A list of card objects.

Request
curl "https://xiaozhujian.study/api/v1/cards?limit=2" \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "list",
  "url": "/api/v1/cards",
  "has_more": true,
  "data": [
    {
      "object": "card",
      "id": "cs_01j9x7d2k8b3",
      "terms": [
        {
          "text": "躺平",
          "pinyin": "tǎng píng",
          "translation": "to lie flat; to opt out",
          "rendering": null
        }
      ],
      "is_sentence": false,
      "sentence": {
        "text": "工作太累了,他决定躺平一段时间。",
        "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
        "translation": "Work was exhausting, so he decided to lie flat for a while.",
        "segments": [
          {
            "text": "工作",
            "pinyin": "gōng zuò"
          },
          {
            "text": "太",
            "pinyin": "tài"
          },
          {
            "text": "累",
            "pinyin": "lèi"
          },
          {
            "text": "了",
            "pinyin": "le"
          },
          {
            "text": ",",
            "pinyin": ""
          },
          {
            "text": "他",
            "pinyin": "tā"
          },
          {
            "text": "决定",
            "pinyin": "jué dìng"
          },
          {
            "text": "躺平",
            "pinyin": "tǎng píng"
          },
          {
            "text": "一段",
            "pinyin": "yí duàn"
          },
          {
            "text": "时间",
            "pinyin": "shí jiān"
          },
          {
            "text": "。",
            "pinyin": ""
          }
        ]
      },
      "audio": {
        "url": "https://audio.example.com/audio/static/output_7f3a….mp3",
        "model": "qwen-audio-3.0-tts-flash",
        "voice": "Cherry"
      },
      "language": "zh-CN",
      "level": "HSK4",
      "style": "general",
      "model": "qwen3.7-plus",
      "context": null,
      "idiomatic_notes": [],
      "review": {
        "state": "new",
        "due": null,
        "review_count": 0,
        "lapses": 0,
        "last_reviewed_at": null
      },
      "url": "https://xiaozhujian.study/cards/cs_01j9x7d2k8b3",
      "created_at": "2026-10-06T09:14:02.511000+00:00",
      "updated_at": "2026-10-06T09:14:02.511000+00:00"
    },
    "…"
  ]
}

MCP search_my_cards read-only

Search all of the learner's existing flashcards (their History): cards whose words match come first, then ones matched only by their English, newest first within each. Matches the Chinese, the pinyin (tone marks optional) or the English. Leave the query empty to list the most recent cards. Use it to avoid making cards the learner already has, and to find a card's id.

ArgumentTypeDescription
querystringChinese, pinyin or English. Optional.
limitintegerHow many cards to return (default 20).

Create a card

RESTPOST/api/v1/cards

Make one card: an example sentence at the account's level, pinyin, a translation in the account's interface language and native-speaker audio, saved to the account's cards. The call is synchronous and takes ten to thirty seconds, so give it a generous timeout and do not retry one that is still running: each card counts one generation against the monthly allowance. A stack named in stack is checked before anything is made.

ParameterTypeDescription
terms required
body
arrayOne to 4 objects of {chinese, english?}: the Simplified Chinese word or phrase, and an optional English gloss that steers the sentence toward that meaning. Several terms share the card and its one sentence; a card costs one generation however many it has.
stack
body
stringThe id of one of the account's saved stacks to add the card to as well.
context
body
stringWhat the sentence should be about, at most 200 characters. Stored on the card and returned exactly as sent. The account's About you, and the stack's own context when stack is given, are read as well.

Returns The card, with stack (the stack's id) when one was given. Status 201.

Request
curl https://xiaozhujian.study/api/v1/cards \
  -H "Authorization: Bearer $XZJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"terms": [{"chinese": "躺平", "english": "to lie flat; to opt out"}]}'
Response
{
  "object": "card",
  "id": "cs_01j9x7d2k8b3",
  "terms": [
    {
      "text": "躺平",
      "pinyin": "tǎng píng",
      "translation": "to lie flat; to opt out",
      "rendering": null
    }
  ],
  "is_sentence": false,
  "sentence": {
    "text": "工作太累了,他决定躺平一段时间。",
    "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
    "translation": "Work was exhausting, so he decided to lie flat for a while.",
    "segments": [
      {
        "text": "工作",
        "pinyin": "gōng zuò"
      },
      {
        "text": "太",
        "pinyin": "tài"
      },
      {
        "text": "累",
        "pinyin": "lèi"
      },
      {
        "text": "了",
        "pinyin": "le"
      },
      {
        "text": ",",
        "pinyin": ""
      },
      {
        "text": "他",
        "pinyin": "tā"
      },
      {
        "text": "决定",
        "pinyin": "jué dìng"
      },
      {
        "text": "躺平",
        "pinyin": "tǎng píng"
      },
      {
        "text": "一段",
        "pinyin": "yí duàn"
      },
      {
        "text": "时间",
        "pinyin": "shí jiān"
      },
      {
        "text": "。",
        "pinyin": ""
      }
    ]
  },
  "audio": {
    "url": "https://audio.example.com/audio/static/output_7f3a….mp3",
    "model": "qwen-audio-3.0-tts-flash",
    "voice": "Cherry"
  },
  "language": "zh-CN",
  "level": "HSK4",
  "style": "general",
  "model": "qwen3.7-plus",
  "context": null,
  "idiomatic_notes": [],
  "review": {
    "state": "new",
    "due": null,
    "review_count": 0,
    "lapses": 0,
    "last_reviewed_at": null
  },
  "url": "https://xiaozhujian.study/cards/cs_01j9x7d2k8b3",
  "created_at": "2026-10-06T09:14:02.511000+00:00",
  "updated_at": "2026-10-06T09:14:02.511000+00:00"
}

MCP create_card writes

Make one Mandarin flashcard: an example sentence at the learner's level, pinyin, an English translation and native-speaker audio, saved to the learner's History. One term, or 2-4 related terms sharing the one sentence. Give `stack_id` (from list_my_stacks) to add it to one of the learner's saved stacks as well. For several unrelated words, call create_card_stack instead. Takes 10-30 seconds and counts one card against the learner's monthly allowance.

ArgumentTypeDescription
terms requiredarray of objectUsually one term. Several related or contrasting terms share the card and its one example sentence.
terms[].chinese requiredstringOne word or phrase in Simplified Chinese characters (no pinyin).
terms[].englishstringA short English gloss. Steers the example sentence toward this meaning.
stack_idstringOptional. The id of one of the learner's saved stacks to add the card to.

Retrieve a card

RESTGET/api/v1/cards/{card_id}

One card of the account's, by id.

ParameterTypeDescription
card_id required
path
stringThe card's id.

Returns A card object.

Request
curl https://xiaozhujian.study/api/v1/cards/cs_01j9x7d2k8b3 \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "card",
  "id": "cs_01j9x7d2k8b3",
  "terms": [
    {
      "text": "躺平",
      "pinyin": "tǎng píng",
      "translation": "to lie flat; to opt out",
      "rendering": null
    }
  ],
  "is_sentence": false,
  "sentence": {
    "text": "工作太累了,他决定躺平一段时间。",
    "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
    "translation": "Work was exhausting, so he decided to lie flat for a while.",
    "segments": [
      {
        "text": "工作",
        "pinyin": "gōng zuò"
      },
      {
        "text": "太",
        "pinyin": "tài"
      },
      {
        "text": "累",
        "pinyin": "lèi"
      },
      {
        "text": "了",
        "pinyin": "le"
      },
      {
        "text": ",",
        "pinyin": ""
      },
      {
        "text": "他",
        "pinyin": "tā"
      },
      {
        "text": "决定",
        "pinyin": "jué dìng"
      },
      {
        "text": "躺平",
        "pinyin": "tǎng píng"
      },
      {
        "text": "一段",
        "pinyin": "yí duàn"
      },
      {
        "text": "时间",
        "pinyin": "shí jiān"
      },
      {
        "text": "。",
        "pinyin": ""
      }
    ]
  },
  "audio": {
    "url": "https://audio.example.com/audio/static/output_7f3a….mp3",
    "model": "qwen-audio-3.0-tts-flash",
    "voice": "Cherry"
  },
  "language": "zh-CN",
  "level": "HSK4",
  "style": "general",
  "model": "qwen3.7-plus",
  "context": null,
  "idiomatic_notes": [],
  "review": {
    "state": "new",
    "due": null,
    "review_count": 0,
    "lapses": 0,
    "last_reviewed_at": null
  },
  "url": "https://xiaozhujian.study/cards/cs_01j9x7d2k8b3",
  "created_at": "2026-10-06T09:14:02.511000+00:00",
  "updated_at": "2026-10-06T09:14:02.511000+00:00"
}

Delete a card

RESTDELETE/api/v1/cards/{card_id}

Delete a card for good, with its review history. The row is gone when the call returns; its audio follows. A stack that holds a copy of the card keeps the copy.

ParameterTypeDescription
card_id required
path
stringThe card's id.

Returns An object with the card's id and deleted: true.

Request
curl -X DELETE https://xiaozhujian.study/api/v1/cards/cs_01j9x7d2k8b3 \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "card",
  "id": "cs_01j9x7d2k8b3",
  "deleted": true
}

MCP delete_card destructive

Delete one of the learner's flashcards for good, by the `id` search_my_cards gives. Only when the learner has clearly asked for this card to be deleted; confirm which card first. Its review history goes with it. A stack that holds a copy keeps the copy.

ArgumentTypeDescription
card_id requiredstringThe card's id, e.g. "cs_…".

Stacks

The stack object

A saved stack: a named, versioned list of cards. cards is present when one stack is retrieved or written and absent in a list; each entry is a stack_card, a snapshot of a card as it was when added, with card naming the card it came from (or null once that card is deleted — the stack keeps its copy). is_public cannot be set through the API; publishing is a decision made in the app.

{
  "object": "stack",
  "id": "ps_4b2f9c1e7a",
  "name": "Internet slang",
  "description": "Words people use online.",
  "is_public": false,
  "version": 1,
  "card_count": 1,
  "context": null,
  "review_status": "under_review",
  "folder": null,
  "url": "https://xiaozhujian.study/stacks/ps_4b2f9c1e7a",
  "created_at": "2026-10-06T09:15:40.102000+00:00",
  "updated_at": "2026-10-06T09:15:40.102000+00:00",
  "cards": [
    {
      "object": "stack_card",
      "key": "k_8c1d2e",
      "card": "cs_01j9x7d2k8b3",
      "terms": [
        {
          "text": "躺平",
          "pinyin": "tǎng píng",
          "translation": "to lie flat; to opt out",
          "rendering": null
        }
      ],
      "is_sentence": false,
      "sentence": {
        "text": "工作太累了,他决定躺平一段时间。",
        "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
        "translation": "Work was exhausting, so he decided to lie flat for a while.",
        "segments": [
          "…"
        ]
      },
      "audio": {
        "url": "https://audio.example.com/audio/stacks/ps_4b2f…/k_8c1d2e.mp3"
      }
    }
  ]
}

List stacks

RESTGET/api/v1/stacks

The account's saved stacks, most recently edited first, without their cards — retrieve one for those.

ParameterTypeDescription
limit
query
integerHow many to return, 1–100. Default 20.
starting_after
query
stringA stack id; the page after it.

Returns A list of stack objects, without cards.

Request
curl https://xiaozhujian.study/api/v1/stacks \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "list",
  "url": "/api/v1/stacks",
  "has_more": false,
  "data": [
    {
      "object": "stack",
      "id": "ps_4b2f9c1e7a",
      "name": "Internet slang",
      "description": "Words people use online.",
      "is_public": false,
      "version": 1,
      "card_count": 1,
      "context": null,
      "review_status": "under_review",
      "folder": null,
      "url": "https://xiaozhujian.study/stacks/ps_4b2f9c1e7a",
      "created_at": "2026-10-06T09:15:40.102000+00:00",
      "updated_at": "2026-10-06T09:15:40.102000+00:00"
    }
  ]
}

MCP list_my_stacks read-only

The learner's saved card stacks, with their ids and links.

No arguments.

Create a stack

RESTPOST/api/v1/stacks

Save a private stack: a name, an optional description, and its cards in order — each either a card already made ({id}) or one to make now ({terms}, exactly as Create a card takes them). At most 10 cards can be made in one request, each costing a generation; the stack's name and description are the topic of every sentence. A card that could not be made is listed in failed_cards and the stack is saved without it.

ParameterTypeDescription
name required
body
stringAt most 120 characters.
description
body
stringAt most 2000 characters.
cards
body
arrayUp to 100 of {id} or {terms}. May be left out for an empty stack.

Returns The stack, with its cards. Status 201.

Request
curl https://xiaozhujian.study/api/v1/stacks \
  -H "Authorization: Bearer $XZJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Internet slang", "description": "Words people use online.",
       "cards": [{"terms": [{"chinese": "躺平"}]}, {"id": "cs_01j9x7d2k8b3"}]}'
Response
{
  "object": "stack",
  "id": "ps_4b2f9c1e7a",
  "name": "Internet slang",
  "description": "Words people use online.",
  "is_public": false,
  "version": 1,
  "card_count": 1,
  "context": null,
  "review_status": "under_review",
  "folder": null,
  "url": "https://xiaozhujian.study/stacks/ps_4b2f9c1e7a",
  "created_at": "2026-10-06T09:15:40.102000+00:00",
  "updated_at": "2026-10-06T09:15:40.102000+00:00",
  "cards": [
    {
      "object": "stack_card",
      "key": "k_8c1d2e",
      "card": "cs_01j9x7d2k8b3",
      "terms": [
        {
          "text": "躺平",
          "pinyin": "tǎng píng",
          "translation": "to lie flat; to opt out",
          "rendering": null
        }
      ],
      "is_sentence": false,
      "sentence": {
        "text": "工作太累了,他决定躺平一段时间。",
        "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
        "translation": "Work was exhausting, so he decided to lie flat for a while.",
        "segments": [
          "…"
        ]
      },
      "audio": {
        "url": "https://audio.example.com/audio/stacks/ps_4b2f…/k_8c1d2e.mp3"
      }
    }
  ]
}

MCP create_card_stack writes

Make Mandarin flashcards and group them as a stack. You choose the terms: up to 10 cards of Simplified Chinese words or short phrases that fit what the learner asked for, each with a short English gloss. For every card we write one example sentence at the learner's level, on the stack's topic, add pinyin, an English translation and native-speaker audio, and save the card to the learner's History. Most cards have one term. When terms are related or contrasting (near-synonyms, opposites like 涨 and 跌, a verb and its usual object) and are best learned side by side, put 2-4 of them on one card: its one sentence uses every term, each term keeps its own pinyin and gloss, and it counts as one card. Never join terms into one `chinese` string ("带飞 / 背锅"). The cards are also saved as a private stack with a link (if the stack cannot be saved, the result includes a link to download them as an Anki deck instead). Takes 10-30 seconds. Each card counts against the learner's monthly allowance.

ArgumentTypeDescription
name requiredstringA short title for the stack, e.g. "Internet slang".
descriptionstringOptional. One sentence saying what the stack covers. Also keeps every card's example sentence on that topic.
cards requiredarray of objectThe cards to make.
cards[].terms requiredarray of objectUsually one term. Several related or contrasting terms share the card and its one example sentence.
cards[].terms[].chinese requiredstringOne word or phrase in Simplified Chinese characters (no pinyin).
cards[].terms[].englishstringA short English gloss. Steers the example sentence toward this meaning.

Retrieve a stack

RESTGET/api/v1/stacks/{stack_id}

One of the account's saved stacks, with its cards as the stack holds them: snapshots taken when each card was added, each naming the card it came from.

ParameterTypeDescription
stack_id required
path
stringThe stack's id.

Returns A stack object, with cards.

Request
curl https://xiaozhujian.study/api/v1/stacks/ps_4b2f9c1e7a \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "stack",
  "id": "ps_4b2f9c1e7a",
  "name": "Internet slang",
  "description": "Words people use online.",
  "is_public": false,
  "version": 1,
  "card_count": 1,
  "context": null,
  "review_status": "under_review",
  "folder": null,
  "url": "https://xiaozhujian.study/stacks/ps_4b2f9c1e7a",
  "created_at": "2026-10-06T09:15:40.102000+00:00",
  "updated_at": "2026-10-06T09:15:40.102000+00:00",
  "cards": [
    {
      "object": "stack_card",
      "key": "k_8c1d2e",
      "card": "cs_01j9x7d2k8b3",
      "terms": [
        {
          "text": "躺平",
          "pinyin": "tǎng píng",
          "translation": "to lie flat; to opt out",
          "rendering": null
        }
      ],
      "is_sentence": false,
      "sentence": {
        "text": "工作太累了,他决定躺平一段时间。",
        "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
        "translation": "Work was exhausting, so he decided to lie flat for a while.",
        "segments": [
          "…"
        ]
      },
      "audio": {
        "url": "https://audio.example.com/audio/stacks/ps_4b2f…/k_8c1d2e.mp3"
      }
    }
  ]
}

Delete a stack

RESTDELETE/api/v1/stacks/{stack_id}

Delete a saved stack with every version of it. The cards stay in the account's cards.

ParameterTypeDescription
stack_id required
path
stringThe stack's id.

Returns An object with the stack's id and deleted: true.

Request
curl -X DELETE https://xiaozhujian.study/api/v1/stacks/ps_4b2f9c1e7a \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "stack",
  "id": "ps_4b2f9c1e7a",
  "deleted": true
}

Add cards to a stack

RESTPOST/api/v1/stacks/{stack_id}/cards

Add cards already made to the end of a stack, as a new version. A card already in the stack is refreshed where it stands rather than added twice.

ParameterTypeDescription
stack_id required
path
stringThe stack's id.
cards required
body
arrayCard ids, in order.

Returns The stack, with its cards.

Request
curl https://xiaozhujian.study/api/v1/stacks/ps_4b2f9c1e7a/cards \
  -H "Authorization: Bearer $XZJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cards": ["cs_01j9x7d2k8b3"]}'
Response
{
  "object": "stack",
  "id": "ps_4b2f9c1e7a",
  "name": "Internet slang",
  "description": "Words people use online.",
  "is_public": false,
  "version": 1,
  "card_count": 1,
  "context": null,
  "review_status": "under_review",
  "folder": null,
  "url": "https://xiaozhujian.study/stacks/ps_4b2f9c1e7a",
  "created_at": "2026-10-06T09:15:40.102000+00:00",
  "updated_at": "2026-10-06T09:15:40.102000+00:00",
  "cards": [
    {
      "object": "stack_card",
      "key": "k_8c1d2e",
      "card": "cs_01j9x7d2k8b3",
      "terms": [
        {
          "text": "躺平",
          "pinyin": "tǎng píng",
          "translation": "to lie flat; to opt out",
          "rendering": null
        }
      ],
      "is_sentence": false,
      "sentence": {
        "text": "工作太累了,他决定躺平一段时间。",
        "pinyin": "gōng zuò tài lèi le tā jué dìng tǎng píng yí duàn shí jiān",
        "translation": "Work was exhausting, so he decided to lie flat for a while.",
        "segments": [
          "…"
        ]
      },
      "audio": {
        "url": "https://audio.example.com/audio/stacks/ps_4b2f…/k_8c1d2e.mp3"
      }
    }
  ]
}

Account

The account object

The account behind the key, as its usage: cards made today and this month, the generations spent and left of the monthly allowance, and when it resets.

{
  "object": "account",
  "usage": {
    "cards_this_month": 42,
    "cards_today": 3,
    "generations_used": 57,
    "generations_limit": 1000,
    "generations_remaining": 943,
    "resets_at": "2026-11-01T00:00:00+00:00"
  }
}

Retrieve the account

RESTGET/api/v1/account

What the account the key belongs to has spent of this month's allowance, and when it resets.

Returns An account object.

Request
curl https://xiaozhujian.study/api/v1/account \
  -H "Authorization: Bearer $XZJ_API_KEY"
Response
{
  "object": "account",
  "usage": {
    "cards_this_month": 42,
    "cards_today": 3,
    "generations_used": 57,
    "generations_limit": 1000,
    "generations_remaining": 943,
    "resets_at": "2026-11-01T00:00:00+00:00"
  }
}

MCP get_account read-only

How many cards the learner has made this month and how many they can still make. Check it if you are unsure whether cards can be made.

No arguments.

Link copied