{"openapi":"3.1.0","info":{"title":"Ankinize REST API","version":"1.0.0","description":"Read and change your Anki collection over HTTP. Everything here edits the same collection that Anki syncs, on Anki schema 18, so a note created through the API appears in Anki on its next sync and the other way round.\n\nEvery request is made as one user with an API key. While Anki is syncing that user's collection, writes wait: you get `423 Locked` with a `Retry-After` header, and nothing is lost if you retry."},"paths":{"/api/v1/decks":{"get":{"summary":"List decks","operationId":"listDecks","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"Only the decks whose name this search accepts. Plain text matches anywhere in the full name (`Languages::German`), ignoring case, with `*` and `_` as wildcards. `deck:Languages` matches that deck and everything below it, `deck:Lang*` and `deck:*::German` use wildcards. `re:regex`, `nc:text` (ignore accents) and `fz:text` (typos allowed, see [Searching](/docs/api#search)) work, and `-`, `or`, `and`, quotes and parentheses combine terms. A colon in plain text has to be escaped (`Languages\\:\\:German`). Any other term is `400 invalid_input`. Leave it out for every deck."}],"responses":{"200":{"description":"List of decks","content":{"application/json":{"schema":{"type":"object","properties":{"decks":{"type":"array","items":{"$ref":"#/components/schemas/Deck"},"description":"Every deck, in no particular order."}},"required":["decks"]},"example":{"decks":[{"id":1,"name":"Default"},{"id":1736899200123,"name":"Languages::German"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Decks"],"description":"Returns every deck in the collection, including the built-in `Default` deck and nested decks, or only the decks whose name `q` accepts. A nested deck's name is its full path, such as `Languages::German`."},"post":{"summary":"Create a deck","operationId":"createDeck","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeckInput"},"example":{"name":"Languages::German"}}}},"responses":{"201":{"description":"Deck created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deck"},"example":{"id":1736899200123,"name":"Languages::German"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Decks"],"description":"Creates a deck and returns it. Use `::` in the name to nest it under a parent, for example `Languages::German`."}},"/api/v1/decks/{id}":{"get":{"summary":"Get a deck by ID","operationId":"getDeck","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."}],"responses":{"200":{"description":"Deck details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deck"},"example":{"id":1736899200123,"name":"Languages::German"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Decks"],"description":"Returns one deck by its numeric id."},"patch":{"summary":"Rename a deck","operationId":"renameDeck","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameDeckInput"},"example":{"name":"Languages::Deutsch"}}}},"responses":{"200":{"description":"Deck renamed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deck"},"example":{"id":1736899200123,"name":"Languages::Deutsch"}}}},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Decks"],"description":"Changes a deck's name. The deck keeps its id and its cards, and its subdecks are renamed with it."},"delete":{"summary":"Delete a deck and its subdecks","operationId":"deleteDeck","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"responses":{"200":{"description":"Deck deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true` when the deck was deleted."},"deletedDeckIds":{"type":"array","items":{"type":"integer"},"description":"The deck and each of its subdecks."},"deletedCardIds":{"type":"array","items":{"type":"integer"},"description":"Every card that was in those decks."},"deletedNoteIds":{"type":"array","items":{"type":"integer"},"description":"Notes that were left with no cards and were removed with them."},"defaultDeckReset":{"type":"boolean","description":"`true` when the built-in `Default` deck was among the decks. It cannot be removed, so it was emptied and kept."},"returnedCardIds":{"type":"array","items":{"type":"integer"},"description":"Cards that were in a deleted filtered deck. They go back to the deck they came from instead of being deleted."}},"required":["ok","deletedDeckIds","defaultDeckReset","deletedCardIds","deletedNoteIds","returnedCardIds"]},"example":{"ok":true,"deletedDeckIds":[1736899200123],"defaultDeckReset":false,"deletedCardIds":[1736899300457,1736899300458],"deletedNoteIds":[1736899300456],"returnedCardIds":[]}}}},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Decks"],"description":"Deletes the deck, all of its subdecks and every card in them. Notes left with no cards are removed too.\n\nThis cannot be undone. The response lists exactly which decks and cards were removed. Deleting is still allowed while the account is over its storage quota."}},"/api/v1/notes":{"get":{"summary":"Search notes","operationId":"listNotes","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"A search, written in Anki's search syntax: the text you would type in the browser, such as `deck:Languages is:due -tag:easy`. Leave it out for everything. The terms, the differences from desktop Anki, `fz:` for typos and the 1000-character limit are in [Searching](/docs/api#search); a search Anki would refuse is `400 invalid_input`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The id of the last item on the previous page, which is its `nextCursor`. Leave it out for the first page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Page size, from 1 to 100. Defaults to 50."}],"responses":{"200":{"description":"Paginated list of notes","content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"array","items":{"$ref":"#/components/schemas/Note"},"description":"This page of notes, in ascending id order."},"nextCursor":{"type":["string","null"],"description":"Send this as `cursor` to get the next page. `null` on the last page."}},"required":["notes","nextCursor"]},"example":{"notes":[{"id":1736899300456,"guid":"k3Jx9!aQ2c","notetypeId":1736899100001,"mtime":1736899300,"usn":-1,"tags":["vocab","a1"],"fields":{"front":"der Hund","back":"the dog"}}],"nextCursor":"1736899300456"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Notes"],"description":"Returns the notes that match a search (`q`; every note when it is empty), in ascending id order, one page at a time. `q` is the search of [Searching](/docs/api#search), including `fz:` for typos: the Anki browser's notes mode. A note is found when one of its cards satisfies the whole search, so `deck:Languages is:due` needs a card that is in the deck *and* due. A search that asks nothing of the cards (`tag:`, a field, plain text) looks at the notes alone, so it also finds a note that has no cards.\n\nTo read the next page, send the previous response's `nextCursor` as `cursor`. `nextCursor` is `null` on the last page. As for cards, a search that has to look at every note, such as a regular expression or a fuzzy search, stops after about 10,000 notes and returns what it found so far with a `nextCursor`, so keep following it until it is `null`."},"post":{"summary":"Create a note and generate its cards","operationId":"createNote","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNoteInput"},"example":{"notetypeName":"Basic (and reversed card)","deckName":"Languages::German","fields":{"front":"der Hund","back":"the dog"},"tags":["vocab","a1"]}}}},"responses":{"201":{"description":"Note created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id of the new note."},"guid":{"type":"string","description":"The note's globally unique id: the one you sent, or a generated one."},"cards":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedCard"},"description":"The cards the note type's templates produced."},"newTags":{"type":"array","items":{"type":"string"},"description":"Tags that did not exist in the collection before and were created by this request."}},"required":["id","guid","cards","newTags"]},"example":{"id":1736899300456,"guid":"k3Jx9!aQ2c","cards":[{"id":1736899300457,"ord":0,"deckId":1736899200123,"due":1},{"id":1736899300458,"ord":1,"deckId":1736899200123,"due":2}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Notes"],"description":"Creates a note and generates its cards from the note type's templates.\n\nName the note type with `notetypeName` and the deck with `deckName` (compared ignoring case; a nested deck is `Languages::German`), or send `notetypeId` and `deckId` instead. Exactly one of `notetypeName` and `notetypeId` is needed; without a deck the note goes to the `Default` deck. A name that matches nothing is `404`.\n\n`fields` is an object with one entry per field of the note type, keyed by the field's name in camelCase: `Back Extra` is `backExtra`. Each field of a note type shows its `key` in [Get a note type](#getNoteType). The field's exact name works as a key too. A field you leave out is empty; a key that names no field is `400 invalid_input` and lists the valid keys.\n\nSend a `guid` only when you import notes from somewhere else and need to keep a stable identity; otherwise one is generated. All fields together may not exceed 5 MiB."}},"/api/v1/notes/batch-create":{"post":{"summary":"Create many notes in one call","operationId":"createNotesBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotesBatchInput"},"example":{"notes":[{"notetypeName":"Basic (and reversed card)","deckName":"Languages::German","fields":{"front":"der Hund","back":"the dog"},"tags":["vocab","a1"]},{"notetypeName":"Basic (and reversed card)","deckName":"Languages::German","fields":{"front":"die Katze","back":"the cat"}}]}}}},"responses":{"200":{"description":"Every entry was handled; see `results`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotesBatchResult"},"example":{"atomic":false,"committed":true,"created":2,"failed":0,"results":[{"index":0,"ok":true,"id":1736899300001,"guid":"k3f9&aZ1xq","cards":[{"id":1736899300002,"ord":0,"deckId":1736899200123,"due":1}],"newTags":["vocab","a1"]},{"index":1,"ok":true,"id":1736899300003,"guid":"p0L!m4Qe2c","cards":[{"id":1736899300004,"ord":0,"deckId":1736899200123,"due":2}],"newTags":[]}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/BatchRefused"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Notes"],"description":"Creates up to 200 notes in one call. It does what [Create a note](#createNote) does for each entry (also naming note types and decks by name), but in one transaction and with the note types, decks and tags looked up once, so importing is far faster than one request per note.\n\nThe answer is `200` with one entry in `results` per note you sent, in the same order (`index`). An entry the collection refuses, such as an unknown deck or a key that names no field, is reported as `ok: false` with an `error`, and the others are still written. Send `atomic: true` to write all or nothing: if any entry is refused, nothing is written and the answer is `422` with the same body.\n\nA batch counts as one request per 10 notes (at least one) against your plan's rate limit. At most 2 batches run at once on the server; when it is busy the answer is `503` with `Retry-After`, and nothing was changed."}},"/api/v1/notes/batch-update":{"post":{"summary":"Edit many notes in one call","operationId":"updateNotesBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotesBatchInput"},"example":{"updates":[{"id":1736899300001,"fields":{"back":"the hound"}},{"id":1736899300003,"addTags":["animals"],"removeTags":["a1"]}]}}}},"responses":{"200":{"description":"Every entry was handled; see `results`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotesBatchResult"},"example":{"atomic":false,"committed":true,"changed":2,"failed":0,"results":[{"index":0,"ok":true,"id":1736899300001,"changed":true,"newCards":[],"newTags":[]},{"index":1,"ok":true,"id":1736899300003,"changed":true,"newCards":[],"newTags":["animals"]}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/BatchRefused"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Notes"],"description":"Edits up to 200 notes in one call. It does what [Edit a note](#updateNote) does for each entry, but in one transaction with note types, decks and tags looked up once. Each entry names a note by `id` and may send `fields`, `tags`, or the tag edits `addTags` and `removeTags`; what it leaves out stays as it was. `tags` replaces all tags and can not be sent with `addTags` or `removeTags`. `removeTags` works as in Anki: a tag is matched ignoring case, and removing `lang` also removes `lang::de` and the other tags below it (`*` is not a wildcard). A note may appear once per call.\n\nThe answer is `200` with one entry in `results` per entry you sent, in the same order (`index`). An entry the collection refuses, such as an unknown note or the wrong number of fields, is reported as `ok: false` with an `error`, and the others are still written. Send `atomic: true` to write all or nothing: if any entry is refused, nothing is written and the answer is `422` with the same body. `changed` counts notes that differed from what was stored.\n\nA batch counts as one request per 10 entries (at least one) against your plan's rate limit."}},"/api/v1/notes/batch-delete":{"post":{"summary":"Delete many notes in one call","operationId":"deleteNotesBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteNotesBatchInput"},"example":{"ids":[1736899300001,1736899300003]}}}},"responses":{"200":{"description":"The notes that existed were deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"How many notes were deleted."},"deletedNoteIds":{"type":"array","items":{"type":"integer"},"description":"The notes that were deleted."},"deletedCardIds":{"type":"array","items":{"type":"integer"},"description":"Every card that was made from them."},"missingNoteIds":{"type":"array","items":{"type":"integer"},"description":"Ids that did not exist and were ignored."}},"required":["deleted","deletedNoteIds","deletedCardIds","missingNoteIds"]},"example":{"deleted":2,"deletedNoteIds":[1736899300001,1736899300003],"deletedCardIds":[1736899300002,1736899300004],"missingNoteIds":[]}}}},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Notes"],"description":"Deletes up to 1000 notes and every card made from them, in one transaction. Ids that do not exist are not an error; they are listed in `missingNoteIds`. This cannot be undone. Deleting is still allowed while the account is over its storage quota.\n\nA batch counts as one request per 10 ids (at least one) against your plan's rate limit."}},"/api/v1/notes/{id}":{"get":{"summary":"Get note by ID with cards","operationId":"getNote","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."}],"responses":{"200":{"description":"Note details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteDetails"},"example":{"id":1736899300456,"guid":"k3Jx9!aQ2c","notetypeId":1736899100001,"mtime":1736899300,"usn":-1,"tags":["vocab","a1"],"fields":{"front":"der Hund","back":"the dog"},"cards":[{"id":1736899300457,"ord":0,"deckId":1736899200123,"due":1}]}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Notes"],"description":"Returns one note, including the cards it produced."},"patch":{"summary":"Update note fields or tags","operationId":"updateNote","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNoteInput"},"example":{"fields":{"back":"the dog (animal)"},"tags":["vocab","a1","animals"]}}}},"responses":{"200":{"description":"Note updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id of the note."},"changed":{"type":"boolean","description":"`false` when nothing differed from what was stored, so nothing was written."},"newCards":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedCard"},"description":"Cards that the new field content caused to be generated."},"newTags":{"type":"array","items":{"type":"string"},"description":"Tags that did not exist in the collection before and were created by this request."}},"required":["id","changed","newCards","newTags"]},"example":{"id":1736899300456,"changed":true,"newCards":[],"newTags":["animals"]}}}},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Notes"],"description":"Changes a note's fields, its tags, or both. Anything you leave out stays as it was, including each field missing from `fields`: send only the fields that change. If the new field content makes a template produce a card it did not before, the new cards are returned in `newCards`.\n\n`changed` is `false` when the request matched what was already stored, and nothing is written."},"delete":{"summary":"Delete a note and its cards","operationId":"deleteNote","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"responses":{"200":{"description":"Note deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true` when the note was deleted."},"deletedNoteIds":{"type":"array","items":{"type":"integer"},"description":"The note that was deleted."},"deletedCardIds":{"type":"array","items":{"type":"integer"},"description":"Every card that was made from it."}},"required":["ok","deletedNoteIds","deletedCardIds"]},"example":{"ok":true,"deletedNoteIds":[1736899300456],"deletedCardIds":[1736899300457,1736899300458]}}}},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Notes"],"description":"Deletes a note and every card made from it. This cannot be undone. Deleting is still allowed while the account is over its storage quota."}},"/api/v1/note-types":{"get":{"summary":"List note types","operationId":"listNoteTypes","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"Only the note types whose name this search accepts. Plain text matches anywhere in the name, ignoring case, with `*` and `_` as wildcards. `note:Basic*` matches whole names (the way it does in a card search), and `re:regex`, `nc:text` and `fz:text` work. `-`, `or`, `and`, quotes and parentheses combine terms. Any other term is `400 invalid_input`. Leave it out for every note type."}],"responses":{"200":{"description":"List of note types","content":{"application/json":{"schema":{"type":"object","properties":{"noteTypes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}},"description":"Every note type, with its id and name. Fetch one by id for its fields and templates."}},"required":["noteTypes"]},"example":{"noteTypes":[{"id":1736899100001,"name":"Basic"},{"id":1736899100002,"name":"Cloze"}]}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Note types"],"description":"Returns the id and name of every note type, or only the ones whose name `q` accepts. Use [Get a note type](#getNoteType) for its fields and templates."},"post":{"summary":"Create note type (stock or custom)","operationId":"createNoteType","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotetypeInput"},"example":{"stock":"basic-and-reversed","name":"Vocabulary"}}}},"responses":{"201":{"description":"Note type created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id of the new note type."},"name":{"type":"string","description":"Its name."}},"required":["id","name"]},"example":{"id":1736899400001,"name":"Vocabulary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Note types"],"description":"Creates a note type, either from one of Anki's stock designs or from your own fields and templates.\n\nSend `stock` (`basic`, `basic-and-reversed`, `basic-optional-reversed`, `basic-type-in-answer`, `cloze` or `image-occlusion`) to start from a stock design, and optionally `name` to rename it. Or send `fields` and `templates` to define it yourself."}},"/api/v1/note-types/{id}":{"get":{"summary":"Get note type details with fields and templates","operationId":"getNoteType","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."}],"responses":{"200":{"description":"Note type details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteTypeDetails"},"example":{"id":1736899100001,"name":"Basic","kind":"normal","css":".card { font-family: arial; font-size: 20px; text-align: center; }","sortFieldIdx":0,"fields":[{"ord":0,"name":"Front","key":"front"},{"ord":1,"name":"Back","key":"back"}],"templates":[{"ord":0,"name":"Card 1","qFormat":"{{Front}}","aFormat":"{{FrontSide}}<hr id=answer>{{Back}}"}]}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Note types"],"description":"Returns a note type with its fields and card templates, its CSS and its sort field."},"patch":{"summary":"Update note type fields, templates, or styling","operationId":"updateNoteType","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"acceptFullSync","in":"query","required":false,"schema":{"type":"boolean"},"description":"Set to `true` to accept that this change forces every device to download the whole collection again on its next sync. Without it the request is refused with `409 full_sync_required`."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotetypeInput"},"example":{"css":".card { font-size: 24px; }"}}}},"responses":{"200":{"description":"Note type updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id of the note type."},"name":{"type":"string","description":"Its name after the change."},"schemaChanged":{"type":"boolean","description":"`true` when the change altered the collection's schema, so every device downloads the whole collection on its next sync."},"change":{"$ref":"#/components/schemas/NotetypeChange","description":"What the change did to the note type's structure: how many fields and templates were added, removed or moved."},"updatedNotes":{"type":"integer","description":"Notes that were rewritten because a field was added, removed or moved, or the sort field changed."},"deletedCards":{"type":"integer","description":"Cards that were deleted because their template was removed."},"movedCards":{"type":"integer","description":"Cards whose template moved to a different position, so their number changed."},"newCards":{"type":"integer","description":"Cards that were generated because a template was added or now produces a card."}},"required":["id","name","schemaChanged","change","updatedNotes","deletedCards","movedCards","newCards"]},"example":{"id":1736899100001,"name":"Basic","schemaChanged":false,"change":{"fieldsAdded":0,"fieldsRemoved":0,"fieldsMoved":0,"sortFieldChanged":false,"templatesAdded":0,"templatesRemoved":0,"templatesMoved":0,"destructive":false},"updatedNotes":0,"deletedCards":0,"movedCards":0,"newCards":0}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Note types"],"description":"Changes a note type's name, CSS, sort field, fields or templates.\n\nAdding, reordering or renaming fields and templates is safe. Removing a field or template, or changing which fields a template needs, changes the collection's schema, so every device has to download the whole collection again on its next sync (a *full sync*). The request is refused with `409 full_sync_required` until you add `?acceptFullSync=true`.\n\nRemoving a field or template also needs *Allow destructive schema changes via API* turned on under Settings → Developer, or the answer is `403 destructive_change_disabled`."},"delete":{"summary":"Delete a note type","operationId":"deleteNoteType","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"acceptFullSync","in":"query","required":false,"schema":{"type":"boolean"},"description":"Set to `true` to accept that this change forces every device to download the whole collection again on its next sync. Without it the request is refused with `409 full_sync_required`."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"responses":{"200":{"description":"Note type deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true` when the note type was deleted."},"deletedNotes":{"type":"integer","description":"Notes that used the note type and were removed."},"deletedCards":{"type":"integer","description":"Cards of those notes, removed with them."},"currentNotetypeId":{"type":"integer","description":"The note type Anki now treats as current: the first by name, or a new Basic one if none were left."},"addedBasic":{"type":"boolean","description":"`true` when that was the last note type, so a stock Basic one was added."}},"required":["ok","deletedNotes","deletedCards","currentNotetypeId","addedBasic"]},"example":{"ok":true,"deletedNotes":42,"deletedCards":42,"currentNotetypeId":1736899100002,"addedBasic":false}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Note types"],"description":"Deletes a note type and every note that uses it.\n\nThis changes the collection's schema, so it needs `?acceptFullSync=true` (otherwise `409 full_sync_required`) and *Allow destructive schema changes via API* turned on under Settings → Developer (otherwise `403 destructive_change_disabled`)."}},"/api/v1/queue":{"get":{"summary":"Get the study queue of a deck","operationId":"getStudyQueue","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"deckId","in":"query","required":false,"schema":{"type":"integer"},"description":"Only items that belong to this deck."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Page size, from 1 to 100. Defaults to 50."}],"responses":{"200":{"description":"The next cards to study, in order","content":{"application/json":{"schema":{"type":"object","properties":{"deckId":{"type":"integer"},"counts":{"type":"object","properties":{"new":{"type":"integer"},"learning":{"type":"integer"},"review":{"type":"integer"}},"required":["new","learning","review"]},"total":{"type":"integer"},"learnAheadSecs":{"type":"integer"},"cards":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Card"},{"type":"object","properties":{"kind":{"type":"string","enum":["new","learning","review"]}},"required":["kind"]}]}}},"required":["deckId","counts","total","learnAheadSecs","cards"]}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Study"],"description":"Returns the cards Anki would show next in a deck, in the order it would show them: the deck (and its subdecks) is searched for learning cards due now, then reviews and day-learning cards, then new cards, under the daily limits of every deck preset and deck (including *New cards ignore review limit* and *Apply all parent limits*), with new-card gather and sort order, review order, FSRS orders, burying of siblings and the mixes of new cards and day-learning cards with reviews. The result matches the desktop client's queue for the same collection and moment.\n\n`counts` is what Anki shows above the buttons (new, learning, review). `cards` holds the first `limit` cards, each with a `kind` of `new`, `learning` or `review`; `total` is the length of the whole queue. Learning cards that are due within the preset's learn-ahead limit are listed at the end. Nothing is written, so asking again before you answer gives the same queue; after `POST /api/v1/cards/{id}/answer` ask again.\n\nWithout `deckId` the collection's current deck is used. Only the v3 scheduler is supported (`409 card_state_error` otherwise). A filtered deck is queued with unlimited counts, in the order it was built in Anki (its cards keep that position)."}},"/api/v1/tags":{"get":{"summary":"List tags","operationId":"listTags","description":"Returns every tag in the collection, sorted by name, or only the tags `q` accepts. A nested tag keeps Anki's `::` separator, such as `Languages::German`; searching `tag:Languages` also finds the notes tagged with the nested ones.","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"Only the tags this search accepts. Plain text matches anywhere in the tag, ignoring case, with `*` and `_` as wildcards. `tag:Languages` matches that tag and the tags below it (`Languages::German`); `tag:re:regex`, `tag:nc:text` and `tag:fz:text` work as in a card search, and `re:`, `nc:` and `fz:` without `tag:` look at the whole name. `-`, `or`, `and`, quotes and parentheses combine terms. Any other term is `400 invalid_input`. Leave it out for every tag."}],"responses":{"200":{"description":"List of tags","content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"},"description":"Every tag, sorted by name."}},"required":["tags"]},"example":{"tags":["german","Languages::German","vocab"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Tags"]},"post":{"summary":"Create a tag","operationId":"createTag","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagInput"},"example":{"name":"Languages::German"}}}},"responses":{"200":{"description":"The tag already existed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTag"},"example":{"name":"Languages::German","created":true}}}},"201":{"description":"Tag created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedTag"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Tags"],"description":"Creates a tag without putting it on a note, as Anki registers a tag when a note brings it. A name that is already used (ignoring case) answers `200` with `created: false` and the spelling that is stored, so sending it twice is safe. A nested name keeps the spelling of a parent that exists, as in Anki.\n\nA tag no note carries is removed by Anki's *Check Database* and *Clear Unused Tags*."}},"/api/v1/tags/{name}":{"get":{"summary":"Get a tag by name","operationId":"getTag","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`."}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tag"},"example":{"name":"Languages::German","noteCount":42}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Tags"],"description":"Returns a tag and `noteCount`, the notes that carry it or one of its nested tags (the notes `tag:name` finds). A tag exists when it is in the list, when a nested tag is, or when a note carries it. The `name` in the path is the tag, percent-encoded, and is compared ignoring case."},"patch":{"summary":"Rename a tag","operationId":"renameTag","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameTagInput"},"example":{"newName":"Lang::German"}}}},"responses":{"200":{"description":"Tag renamed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenamedTag"},"example":{"name":"Lang::German","notes":42}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Tags"],"description":"Renames a tag and every tag nested under it on all notes: renaming `Languages` to `Lang` makes `Languages::German` into `Lang::German`. The notes that change get a new modification time, so they sync. If the new name is already a tag, the two are merged, and a note that ends up with the tag twice keeps it once. Renaming a tag that no note carries renames it in the list.\n\n`notes` is how many notes were looked at and changed."},"delete":{"summary":"Delete a tag","operationId":"deleteTag","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"responses":{"200":{"description":"Tag deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deletedTags":{"type":"array","items":{"type":"string"},"description":"The tags that were removed from the list: the one named and those nested under it."},"notes":{"type":"integer","description":"How many notes lost a tag."}},"required":["ok","deletedTags","notes"]},"example":{"ok":true,"deletedTags":["Lang::German","Lang::German::Verbs"],"notes":57}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Tags"],"description":"Removes a tag and the tags nested under it from every note and from the list. The notes keep everything else. This cannot be undone. Deleting is still allowed while the account is over its storage quota."}},"/api/v1/tags/batch-create":{"post":{"summary":"Create many tags in one call","operationId":"createTagsBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagsBatchInput"},"example":{"names":["vocab","grammar","two words"]}}}},"responses":{"200":{"description":"Every entry was handled; see `results`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTagsBatchResult"},"example":{"atomic":false,"committed":true,"created":2,"failed":1,"results":[{"index":0,"ok":true,"name":"vocab","created":true},{"index":1,"ok":true,"name":"grammar","created":true},{"index":2,"name":"two words","ok":false,"error":{"code":"invalid_input","message":"a tag name can not contain a space"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/BatchRefused"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Tags"],"description":"Creates up to 200 tags in one call, in one transaction. It does what [Create a tag](#createTag) does for each name. The answer is `200` with one entry in `results` per name, in order; a name that is refused (such as one with a space) is `ok: false` with an `error` and the others are still created. Send `atomic: true` to write all or nothing: then the answer is `422` with the same body.\n\nA batch counts as one request per 10 names against your plan's rate limit."}},"/api/v1/tags/batch-update":{"post":{"summary":"Rename many tags in one call","operationId":"updateTagsBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTagsBatchInput"},"example":{"updates":[{"name":"vocab","newName":"vocabulary"},{"name":"grammar","newName":"Grammar"}]}}}},"responses":{"200":{"description":"Every entry was handled; see `results`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTagsBatchResult"},"example":{"atomic":false,"committed":true,"renamed":2,"failed":0,"results":[{"index":0,"ok":true,"name":"vocab","newName":"vocabulary","notes":12},{"index":1,"ok":true,"name":"grammar","newName":"Grammar","notes":4}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/BatchRefused"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Tags"],"description":"Renames up to 200 tags in one call, in one pass over the notes. Each entry names a tag by `name` and gives its `newName`, as [Rename a tag](#renameTag) does. Two entries may use the same `newName` (that merges the tags), but an entry that touches another one (a `name` that is, contains or lies inside another entry's `name` or `newName`) is refused with `invalid_input`, because the result would depend on the order. Entries are reported in `results` as in [Create many notes](#createNotesBatch); `atomic: true` writes all or nothing."}},"/api/v1/tags/batch-delete":{"post":{"summary":"Delete many tags in one call","operationId":"deleteTagsBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteTagsBatchInput"},"example":{"names":["vocabulary","unused"]}}}},"responses":{"200":{"description":"The tags that existed were deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"How many tags were removed from the list."},"deletedTags":{"type":"array","items":{"type":"string"},"description":"The tags that were removed: those named and those nested under them."},"notes":{"type":"integer","description":"How many notes lost a tag."},"missingNames":{"type":"array","items":{"type":"string"},"description":"Names that matched nothing and were ignored."}},"required":["deleted","deletedTags","notes","missingNames"]},"example":{"deleted":1,"deletedTags":["vocabulary"],"notes":12,"missingNames":["unused"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Tags"],"description":"Deletes up to 1000 tags, with the tags nested under them, from every note and from the list, in one transaction. Names that do not exist are not an error; they are listed in `missingNames`. This cannot be undone. Deleting is still allowed while the account is over its storage quota."}},"/api/v1/stats":{"get":{"summary":"Get the statistics of a search","operationId":"getStats","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"A search, written in Anki's search syntax: the text you would type in the browser, such as `deck:Languages is:due -tag:easy`. Leave it out for everything. The terms, the differences from desktop Anki, `fz:` for typos and the 1000-character limit are in [Searching](/docs/api#search); a search Anki would refuse is `400 invalid_input`."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":36500,"default":0},"description":"Days of review history to count, from today back; `0` (the default) is all of it. At most 36500."}],"responses":{"200":{"description":"The graphs of Anki's statistics screen","content":{"application/json":{"schema":{"type":"object","properties":{"buttons":{"type":"object","properties":{"oneMonth":{"type":"object","properties":{"learning":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"young":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"mature":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4}},"required":["learning","young","mature"]},"threeMonths":{"type":"object","properties":{"learning":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"young":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"mature":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4}},"required":["learning","young","mature"]},"oneYear":{"type":"object","properties":{"learning":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"young":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"mature":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4}},"required":["learning","young","mature"]},"allTime":{"type":"object","properties":{"learning":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"young":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4},"mature":{"type":"array","items":{"type":"integer"},"minItems":4,"maxItems":4}},"required":["learning","young","mature"]}},"required":["oneMonth","threeMonths","oneYear","allTime"],"description":"How many times each answer button (Again, Hard, Good, Easy: the four numbers, in that order) was pressed, in learning, on young review cards (interval under 21 days) and on mature ones, over the last month, three months, year and all time."},"cardCounts":{"type":"object","properties":{"includingInactive":{"type":"object","properties":{"new":{"type":"integer"},"learn":{"type":"integer"},"relearn":{"type":"integer"},"young":{"type":"integer"},"mature":{"type":"integer"},"suspended":{"type":"integer"},"buried":{"type":"integer"}},"required":["new","learn","relearn","young","mature","suspended","buried"]},"excludingInactive":{"type":"object","properties":{"new":{"type":"integer"},"learn":{"type":"integer"},"relearn":{"type":"integer"},"young":{"type":"integer"},"mature":{"type":"integer"},"suspended":{"type":"integer"},"buried":{"type":"integer"}},"required":["new","learn","relearn","young","mature","suspended","buried"]}},"required":["includingInactive","excludingInactive"],"description":"Cards by kind. `includingInactive` counts suspended and buried cards by their kind; `excludingInactive` counts them only as `suspended` and `buried`. `young` and `mature` are review cards with an interval under and over 21 days."},"hours":{"type":"object","properties":{"oneMonth":{"type":"array","items":{"type":"object","properties":{"total":{"type":"integer"},"correct":{"type":"integer"}},"required":["total","correct"]},"minItems":24,"maxItems":24},"threeMonths":{"type":"array","items":{"type":"object","properties":{"total":{"type":"integer"},"correct":{"type":"integer"}},"required":["total","correct"]},"minItems":24,"maxItems":24},"oneYear":{"type":"array","items":{"type":"object","properties":{"total":{"type":"integer"},"correct":{"type":"integer"}},"required":["total","correct"]},"minItems":24,"maxItems":24},"allTime":{"type":"array","items":{"type":"object","properties":{"total":{"type":"integer"},"correct":{"type":"integer"}},"required":["total","correct"]},"minItems":24,"maxItems":24}},"required":["oneMonth","threeMonths","oneYear","allTime"],"description":"Answers by hour of the day (24 entries, hour 0 first), in the collection's `localOffset`: how many (`total`) and how many were not Again (`correct`), over the last month, three months, year and all time."},"today":{"type":"object","properties":{"answerCount":{"type":"integer"},"answerMillis":{"type":"integer"},"correctCount":{"type":"integer"},"matureCorrect":{"type":"integer"},"matureCount":{"type":"integer"},"learnCount":{"type":"integer"},"reviewCount":{"type":"integer"},"relearnCount":{"type":"integer"},"earlyReviewCount":{"type":"integer"}},"required":["answerCount","answerMillis","correctCount","matureCorrect","matureCount","learnCount","reviewCount","relearnCount","earlyReviewCount"],"description":"What was answered since the last day rollover: the number of answers and the milliseconds they took, how many were not Again, how many of those were on mature cards, and the answers by kind (`earlyReviewCount` is answers in a filtered deck)."},"eases":{"type":"object","properties":{"eases":{"type":"object","additionalProperties":{"type":"integer"}},"average":{"type":"number"}},"required":["eases","average"],"description":"SM-2 cards: how many have each ease, as a percent (250 is 250%), and the median ease. Cards with an FSRS memory state are in `difficulty`."},"difficulty":{"type":"object","properties":{"eases":{"type":"object","additionalProperties":{"type":"integer"}},"average":{"type":"number"}},"required":["eases","average"],"description":"FSRS cards: how many fall in each 1% bin of difficulty (0 to 99), and the median difficulty in percent."},"intervals":{"type":"object","properties":{"intervals":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["intervals"],"description":"Review and relearning cards by interval, in days."},"stability":{"type":"object","properties":{"intervals":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["intervals"],"description":"FSRS cards by stability, in days, rounded."},"futureDue":{"type":"object","properties":{"futureDue":{"type":"object","additionalProperties":{"type":"integer"}},"haveBacklog":{"type":"boolean"},"dailyLoad":{"type":"integer"}},"required":["futureDue","haveBacklog","dailyLoad"],"description":"Cards that are due, by day (0 is today, 1 tomorrow, negative is overdue): suspended and new cards are left out, and buried cards that are already due. `haveBacklog` is true when something is overdue. `dailyLoad` is the average number of reviews a day the collection needs, from the intervals."},"added":{"type":"object","properties":{"added":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["added"],"description":"Cards by the day they were created (0 is today, -1 yesterday)."},"reviews":{"type":"object","properties":{"count":{"type":"object","additionalProperties":{"type":"object","properties":{"learn":{"type":"integer"},"relearn":{"type":"integer"},"young":{"type":"integer"},"mature":{"type":"integer"},"filtered":{"type":"integer"}},"required":["learn","relearn","young","mature","filtered"]}},"time":{"type":"object","additionalProperties":{"type":"object","properties":{"learn":{"type":"integer"},"relearn":{"type":"integer"},"young":{"type":"integer"},"mature":{"type":"integer"},"filtered":{"type":"integer"}},"required":["learn","relearn","young","mature","filtered"]}}},"required":["count","time"],"description":"Answers (`count`) and the milliseconds they took (`time`) by day (0 is today, -1 yesterday), split into learning, relearning, young and mature review cards and filtered decks. Manual changes to the schedule are not answers."},"rolloverHour":{"type":"integer","description":"The hour of the day a new day starts (the `rollover` setting)."},"retrievability":{"type":"object","properties":{"retrievability":{"type":"object","additionalProperties":{"type":"integer"}},"average":{"type":"number"},"sumByCard":{"type":"number"},"sumByNote":{"type":"number"}},"required":["retrievability","average","sumByCard","sumByNote"],"description":"FSRS cards: how many fall in each 1% bin of the chance of recalling them now, the average in percent, and the sum of the chances over cards and over notes (the expected number of cards, and of notes, remembered)."},"fsrs":{"type":"boolean","description":"Whether the collection has FSRS turned on."},"trueRetention":{"type":"object","properties":{"today":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]},"yesterday":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]},"week":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]},"month":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]},"year":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]},"allTime":{"type":"object","properties":{"youngPassed":{"type":"integer"},"youngFailed":{"type":"integer"},"maturePassed":{"type":"integer"},"matureFailed":{"type":"integer"}},"required":["youngPassed","youngFailed","maturePassed","matureFailed"]}},"required":["today","yesterday","week","month","year","allTime"],"description":"Of the review answers on cards that had an interval of a day or more, how many were passed and how many failed (Again), for young and mature cards: today, yesterday, the last week, month and year, and all time."},"cards":{"type":"integer","description":"The number of cards the search matched."},"reviewEntries":{"type":"integer","description":"The number of review-log entries read, which is the history inside `days` of the matched cards (all entries for an empty search, even those of deleted cards)."},"timing":{"type":"object","properties":{"now":{"type":"integer"},"daysElapsed":{"type":"integer"},"nextDayAt":{"type":"integer"},"localOffsetSecs":{"type":"integer"}},"required":["now","daysElapsed","nextDayAt","localOffsetSecs"],"description":"The clock the graphs use: `now` (when they were computed, which for a repeated request can be a few minutes ago) and `nextDayAt` (when the current day ends) in seconds since the epoch, `daysElapsed` since the collection was created, and `localOffsetSecs` east of UTC."}},"required":["buttons","cardCounts","hours","today","eases","difficulty","intervals","stability","futureDue","added","reviews","rolloverHour","retrievability","fsrs","trueRetention","cards","reviewEntries","timing"]},"example":{"buttons":{"oneMonth":{"learning":[4,1,22,3],"young":[6,3,40,5],"mature":[2,1,31,4]},"threeMonths":{"learning":[9,2,51,6],"young":[12,5,88,9],"mature":[5,2,70,8]},"oneYear":{"learning":[9,2,51,6],"young":[12,5,88,9],"mature":[5,2,70,8]},"allTime":{"learning":[9,2,51,6],"young":[12,5,88,9],"mature":[5,2,70,8]}},"cardCounts":{"includingInactive":{"new":120,"learn":3,"relearn":2,"young":40,"mature":85,"suspended":0,"buried":0},"excludingInactive":{"new":118,"learn":3,"relearn":2,"young":40,"mature":84,"suspended":1,"buried":1}},"hours":{"oneMonth":[{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":40,"correct":36},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0},{"total":40,"correct":36},{"total":0,"correct":0},{"total":0,"correct":0},{"total":0,"correct":0}],"threeMonths":[],"oneYear":[],"allTime":[]},"today":{"answerCount":42,"answerMillis":301000,"correctCount":38,"matureCorrect":20,"matureCount":22,"learnCount":6,"reviewCount":34,"relearnCount":2,"earlyReviewCount":0},"eases":{"eases":{"230":4,"250":60,"270":9},"average":250},"difficulty":{"eases":{},"average":0},"intervals":{"intervals":{"1":5,"7":18,"30":40}},"stability":{"intervals":{}},"futureDue":{"futureDue":{"0":12,"1":9,"2":11},"haveBacklog":false,"dailyLoad":14},"added":{"added":{"-30":40,"-1":6}},"reviews":{"count":{"-1":{"learn":4,"relearn":1,"young":22,"mature":30,"filtered":0}},"time":{"-1":{"learn":20000,"relearn":5000,"young":110000,"mature":150000,"filtered":0}}},"rolloverHour":4,"retrievability":{"retrievability":{},"average":0,"sumByCard":0,"sumByNote":0},"fsrs":false,"trueRetention":{"today":{"youngPassed":20,"youngFailed":2,"maturePassed":18,"matureFailed":2},"yesterday":{"youngPassed":18,"youngFailed":3,"maturePassed":17,"matureFailed":1},"week":{"youngPassed":120,"youngFailed":14,"maturePassed":110,"matureFailed":9},"month":{"youngPassed":400,"youngFailed":40,"maturePassed":380,"matureFailed":30},"year":{"youngPassed":400,"youngFailed":40,"maturePassed":380,"matureFailed":30},"allTime":{"youngPassed":400,"youngFailed":40,"maturePassed":380,"matureFailed":30}},"cards":250,"reviewEntries":1190,"timing":{"now":1736899200,"daysElapsed":412,"nextDayAt":1736913600,"localOffsetSecs":0}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Study"],"description":"Returns the graphs of Anki's statistics screen for the cards a search matches (`q`, default every card) and the last `days` days of review history (`0`, the default, is all of it). The numbers are Anki's: the same counts, buckets and averages the desktop client draws, computed from the review log and the cards in place. Nothing is copied into another database. A request reads the review log once, so a big collection takes a moment; one that takes longer than 20 seconds is refused with `400 invalid_input` (narrow the search or the days). The server keeps the graphs of a search in memory for up to five minutes, and never past the end of the day, while the collection is unchanged, so a repeat request is answered without reading the collection again; any change to the collection ends that.\n\nDays are counted from the end of the current day, so `0` is today, `-1` yesterday and `1` tomorrow; a day starts at the collection's `rollover` hour and is read in its `localOffset` (UTC when unset), as `timing` says. `buttons`, `hours` and `trueRetention` count answers over a month, three months, a year and all time. `cardCounts` counts cards by kind, with suspended and buried cards counted separately or together. `eases` (SM-2) and `difficulty`, `stability` and `retrievability` (FSRS) cover the cards that have the data; `fsrs` says whether the collection has FSRS turned on. `retrievability` is the chance of recalling each card now, from its memory state and the time since its last review. Keys of the count objects are numbers: a day, a percent bin or an interval in days."}},"/api/v1/media":{"get":{"summary":"List media files","operationId":"listMedia","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"prefix","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"default":100},"description":"Page size, from 1 to 100. Defaults to 50."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The id of the last item on the previous page, which is its `nextCursor`. Leave it out for the first page."}],"responses":{"200":{"description":"A page of media files, in name order","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MediaFile"}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Media"],"description":"Lists the collection's media files by name, 100 at a time (`limit` up to 1000). `prefix` keeps only the names that start with it. When `nextCursor` is not null, pass it as `cursor` to get the next page. Each item has the file's `size`, its `sha1` and `sha256`, the `contentType` it is served as, and the `usn` of its last change, which is the number the Anki media sync uses."}},"/api/v1/media/{name}":{"get":{"summary":"Download a media file","operationId":"getMedia","security":[{"BearerAuth":["read"]},{}],"parameters":[{"name":"name","in":"path","required":true,"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`.","schema":{"type":"string","minLength":1,"maxLength":120}},{"name":"Range","in":"header","required":false,"schema":{"type":"string"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"}},{"name":"u","in":"query","required":false,"schema":{"type":"string"}},{"name":"exp","in":"query","required":false,"schema":{"type":"integer"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The file","headers":{"Content-Length":{"schema":{"type":"integer"}},"Content-Type":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}},"Accept-Ranges":{"schema":{"type":"string"}}},"content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"206":{"description":"The requested byte range","headers":{"Content-Length":{"schema":{"type":"integer"}},"Content-Type":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}},"Accept-Ranges":{"schema":{"type":"string"}}},"content":{"*/*":{"schema":{"type":"string","format":"binary"}}}},"304":{"description":"The ETag still matches"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"416":{"description":"The range is not inside the file"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Media"],"description":"Returns the file's bytes. The type comes from the file name's extension only, never from the bytes, and every response says `X-Content-Type-Options: nosniff` and a `Content-Security-Policy` with `sandbox`, so a file from your collection cannot run script on this origin. Images, audio, video and fonts are shown inline; SVG, PDF and any other type are offered as downloads (`Content-Disposition: attachment`).\n\nThe `ETag` is the file's SHA-256, so `If-None-Match` answers `304`. `Range: bytes=...` (one range) answers `206`, which lets a browser seek in audio and video; a range past the end is `416`.\n\nInstead of a key, the request may carry the `u`, `exp` and `sig` query parameters of a signed link. A signed link opens that one file for reading until it expires, which is how a card shown in a sandboxed frame loads its pictures: `GET /api/v1/cards/{id}/render` puts such links into the card unless you pass `mediaBaseUrl`. The links last an hour."},"head":{"summary":"Get a media file's headers","operationId":"headMedia","security":[{"BearerAuth":["read"]},{}],"parameters":[{"name":"name","in":"path","required":true,"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`.","schema":{"type":"string","minLength":1,"maxLength":120}},{"name":"u","in":"query","required":false,"schema":{"type":"string"}},{"name":"exp","in":"query","required":false,"schema":{"type":"integer"}},{"name":"sig","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The file's headers","headers":{"Content-Length":{"schema":{"type":"integer"}},"Content-Type":{"schema":{"type":"string"}},"ETag":{"schema":{"type":"string"}},"Accept-Ranges":{"schema":{"type":"string"}}}},"304":{"description":"The ETag still matches"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Media"],"description":"Like `GET`, without the body: the headers tell the size, type and `ETag`."},"put":{"summary":"Store a media file","operationId":"putMedia","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"name","in":"path","required":true,"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`.","schema":{"type":"string","minLength":1,"maxLength":120}}],"requestBody":{"required":true,"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"The file replaced one, or the bytes were the same","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPutResult"}}}},"201":{"description":"The file was created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPutResult"}}}},"400":{"description":"The file name cannot be decoded (`invalid_media_name`), or the body is not as long as `Content-Length` says (`length_mismatch`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key lacks the `write` scope, the account is suspended or unverified, the file would take the storage over its quota (`over_quota`), or the collection already holds the most file names it may (`media_entry_limit`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"411":{"description":"No Content-Length header (length_required)"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"The name is not one Anki clients keep as it is (`invalid_media_name`), or the file is empty (`empty_file`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Media"],"description":"Stores the request body as the file, creating it or replacing it. Send the bytes as they are (any `Content-Type`) with a `Content-Length` header; chunked uploads are refused with `411`. A file is at most 100 MiB and not empty.\n\nThe name must be one Anki clients would keep as it is: at most 120 bytes of UTF-8, in Unicode NFC, with none of `[ ] < > : \" / ? * ^ \\ |` or control characters, no Windows device name (`CON`, `NUL`, `LPT1`...), and not ending in a space or a dot. A client rewrites any other name when it syncs, so the file would never settle between devices; the `422 invalid_media_name` says the name to use. `Thumbs.db` and `.DS_Store` are never synced and are refused as well.\n\n`201` means the file is new; `200` means it replaced a file (`created` is false) or the bytes were the same (`unchanged` is true, and the `usn` stays). A new or changed file gets the next `usn`, so the media sync sends it to your devices. A file that would take the storage over the quota is `403 over_quota`, and a collection already holding the most file names it may (deleted names count) is `403 media_entry_limit`; replacing a name never adds one."},"delete":{"summary":"Delete a media file","operationId":"deleteMedia","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"name","in":"path","required":true,"description":"The tag's name, percent-encoded: `Languages%3A%3AGerman` is `Languages::German`.","schema":{"type":"string","minLength":1,"maxLength":120}}],"responses":{"200":{"description":"The file was deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"name":{"type":"string"},"usn":{"type":"integer"}},"required":["ok","name","usn"]}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Media"],"description":"Deletes the file. Your devices are told on their next media sync, as when a file is deleted in Anki. The storage it used is freed once no other name uses the same bytes. A name that is not there is `404`."}},"/api/v1/cards":{"get":{"summary":"Search cards","operationId":"listCards","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"A search, written in Anki's search syntax: the text you would type in the browser, such as `deck:Languages is:due -tag:easy`. Leave it out for everything. The terms, the differences from desktop Anki, `fz:` for typos and the 1000-character limit are in [Searching](/docs/api#search); a search Anki would refuse is `400 invalid_input`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The id of the last item on the previous page, which is its `nextCursor`. Leave it out for the first page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50},"description":"Page size, from 1 to 100. Defaults to 50."}],"responses":{"200":{"description":"Paginated list of cards","content":{"application/json":{"schema":{"type":"object","properties":{"cards":{"type":"array","items":{"$ref":"#/components/schemas/Card"},"description":"This page of cards, in ascending id order."},"nextCursor":{"type":["string","null"],"description":"Send this as `cursor` to get the next page. `null` when the search is complete. While it is not `null` the page may hold fewer than `limit` cards, or none."}},"required":["cards","nextCursor"]},"example":{"cards":[{"id":1736899300457,"noteId":1736899300456,"deckId":1736899200123,"ord":0,"mtime":1736899300,"usn":-1,"type":0,"queue":0,"due":1,"interval":0,"factor":0,"reps":0,"lapses":0,"left":0,"originalDue":0,"originalDeckId":0,"flags":0}],"nextCursor":null}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Returns the cards that match a search (`q`; every card when it is empty), in ascending id order, one page at a time. Narrow it by deck or note with `deck:`, `did:` and `nid:` in the search. Paginate with `nextCursor` as for notes, with one difference: a search that has to look at every card, such as a regular expression or `prop:s`, stops after about 10,000 cards and returns what it found so far together with a `nextCursor`. A page can therefore hold fewer than `limit` cards, or none, while `nextCursor` is not `null`; keep following it until it is."}},"/api/v1/cards/batch-update":{"post":{"summary":"Edit many cards in one call","operationId":"updateCardsBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardsBatchInput"},"example":{"ids":[1736899300002,1736899300004],"patch":{"flag":2}}}}},"responses":{"200":{"description":"Every card was handled; see `results`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardsBatchResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/BatchRefused"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Cards"],"description":"Applies one change to up to 1000 cards, in one transaction: set a flag, move them to a deck, suspend or bury them. It does what [Edit a card](#updateCard) does for each id, with the same rules for `patch`.\n\nThe answer is `200` with one entry in `results` per distinct id. A card that does not exist, or that sits in a filtered deck and so can not be moved, is reported as `ok: false`, and the others are still changed. Send `atomic: true` to write all or nothing: if any card is refused, nothing is written and the answer is `422` with the same body. `changed` counts the cards that differed from what was stored.\n\nA batch counts as one request per 10 ids (at least one) against your plan's rate limit."}},"/api/v1/cards/batch-delete":{"post":{"summary":"Delete many cards in one call","operationId":"deleteCardsBatch","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCardsBatchInput"},"example":{"ids":[1736899300002,1736899300004]}}}},"responses":{"200":{"description":"The cards that existed were deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer","description":"How many cards were deleted."},"deletedCardIds":{"type":"array","items":{"type":"integer"},"description":"The cards that were deleted."},"deletedNoteIds":{"type":"array","items":{"type":"integer"},"description":"Notes that were deleted because their last card went."},"missingCardIds":{"type":"array","items":{"type":"integer"},"description":"Ids that did not exist and were ignored."}},"required":["deleted","deletedCardIds","deletedNoteIds","missingCardIds"]},"example":{"deleted":2,"deletedCardIds":[1736899300002,1736899300004],"deletedNoteIds":[1736899300001,1736899300003],"missingCardIds":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Busy"}},"tags":["Cards"],"description":"Deletes up to 1000 cards in one transaction. A note whose last card is deleted is deleted with it. Ids that do not exist are not an error; they are listed in `missingCardIds`. This cannot be undone. Deleting is still allowed while the account is over its storage quota.\n\nA batch counts as one request per 10 ids (at least one) against your plan's rate limit."}},"/api/v1/cards/{id}":{"get":{"summary":"Get card details by ID","operationId":"getCard","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."}],"responses":{"200":{"description":"Card details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Returns one card with its scheduling state: queue, due, interval, ease factor, repetitions and lapses."},"patch":{"summary":"Edit a card","operationId":"updateCard","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCardInput"},"example":{"flag":2,"suspended":false}}}},"responses":{"200":{"description":"Card updated","content":{"application/json":{"schema":{"type":"object","properties":{"changed":{"type":"boolean","description":"`false` when nothing differed, so nothing was written."},"card":{"$ref":"#/components/schemas/Card","description":"The card as stored after the change."}},"required":["changed","card"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Changes a card's flag, moves it to another deck, or suspends or buries it. Anything you leave out stays as it was.\n\n`flag` is 0 (none) to 7. `suspended` and `buried` cannot both be sent. Suspending keeps a card out of review until you send `suspended: false`; burying hides it only until the next day or until you unbury it. A suspended card is never buried. Moving a card clears its FSRS memory state; it is replayed from the review history with the new deck's parameters the next time the card is shown or answered. Cards in a filtered deck can neither be moved nor moved into one (`409 card_state_error`).\n\n`changed` is `false` when the request matched what was already stored."},"delete":{"summary":"Delete a card","operationId":"deleteCard","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"responses":{"200":{"description":"Card deleted","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"`true` when the card was deleted."},"deletedCardIds":{"type":"array","items":{"type":"integer"},"description":"The card that was deleted."},"deletedNoteIds":{"type":"array","items":{"type":"integer"},"description":"The note, if the deleted card was its last one."}},"required":["ok","deletedCardIds","deletedNoteIds"]}}}},"404":{"$ref":"#/components/responses/NotFound"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Deletes one card. If it was the last card of its note, the note is removed too. This cannot be undone. Deleting is still allowed while the account is over its storage quota."}},"/api/v1/cards/{id}/render":{"get":{"summary":"Render a card's front and back","operationId":"renderCard","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","html"],"default":"json"},"description":"`json` (default) or `html` for one complete, sandboxed HTML document."},{"name":"typedAnswer","in":"query","required":false,"schema":{"type":"string","maxLength":1000},"description":"What the learner typed, for a `{{type:Field}}` comparison. At most 1000 characters."},{"name":"mediaBaseUrl","in":"query","required":false,"schema":{"type":"string","format":"uri"},"description":"An `http(s)` URL prefix. Media file names in the card are turned into this prefix plus the URL-encoded name."}],"responses":{"200":{"description":"The rendered card","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderedCard"}},"text/html":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Renders the card as Anki shows it: the note's fields are put into the card template, so the question and the answer are ready to display. The answer includes the question above a divider, as in Anki.\n\nEverything is sanitized on the server before it is returned: scripts, event handlers, iframes, forms, `javascript:` links and remote styles are removed, and CSS is limited to a safe subset. The result is still *content from your collection*, so display it in a sandboxed `<iframe sandbox>` or `srcdoc` and not straight into your page.\n\nWith `format=json` (the default) you get `question`, `answer` and `css`. With `format=html` you get one complete HTML document, with a `Content-Security-Policy` that allows no script and the `sandbox` directive, which is meant for `<iframe sandbox srcdoc>`: fetch it with your key and put the text in `srcdoc` (an iframe's `src` cannot send the key, and this server refuses to be framed). Media in a card is referred to by file name and listed in `media`. The file names in the question and answer are turned into signed links to `GET /api/v1/media/{name}` that last an hour, so the card shows its pictures and plays its audio from a sandboxed frame; or pass `mediaBaseUrl` (an `http(s)` URL ending in `/`) to have them point at files you host instead.\n\n**Typing the answer.** For `{{type:Field}}`, `{{type:nc:Field}}` (accents ignored) and `{{type:cloze:Field}}` the question holds `<div class=\"typeans\" data-field=\"Field\"></div>`, where your own input goes. Pass what the learner typed as `typedAnswer` (up to 1000 characters) and the answer shows the comparison Anki shows, computed the same way: a `<code id=\"typeans\">` with the typed text and the expected text as `typeGood`, `typeBad` and `typeMissed` spans around an arrow, in the font and size of the field. The classes carry no colors, so style them in your page. Without `typedAnswer` the answer shows the expected text. A text too long to compare is shown as one change.\n\n**Formulas.** `\\(...\\)` and `\\[...\\]` are returned as written. Anki's `[$]...[/$]` and `[$$]...[/$$]` tags are rewritten to them, and `[latex]...[/latex]` becomes display math; images are not generated, because that needs a TeX installation. `math` is true when the question or the answer holds a formula. Typeset it in your page with MathJax 3 after inserting the HTML (`format=html` allows no script, so a formula stays as text there). TeX that is not math, such as packages or TikZ, is not supported.\n\nA template that cannot be rendered is `422 render_error`."}},"/api/v1/cards/{id}/scheduling":{"get":{"summary":"Preview the four answer buttons","operationId":"getCardScheduling","security":[{"BearerAuth":["read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."}],"responses":{"200":{"description":"What each answer would do","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardScheduling"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Shows what each answer button would do, without answering: the time until the card would next be due after Again, Hard, Good and Easy. This is the preview Anki shows above its buttons.\n\nOnly the v3 scheduler is supported. The card must be answerable: not suspended or buried (`409 card_state_error`). A card in a filtered deck is previewed as Anki does: in a deck that reschedules, the buttons are the home deck preset's, and a review that comes before it was due is scheduled from the days it was early; in a deck that does not reschedule, the buttons are the preview delays (Again, Hard and Good come back after the deck's delays, a delay of 0 ends the preview, and Easy always ends it) and show `0` seconds when they end it.\n\nReview intervals are fuzzed and load balanced like Anki's: the day is drawn within the fuzz range, weighted towards days with fewer cards of the same preset due, the preset's Easy Days and (if it buries siblings) days away from the card's siblings. The draw is fixed by the card and its review count, so the preview, the answer and a repeat of either agree."}},"/api/v1/cards/{id}/answer":{"post":{"summary":"Answer (review) a card","operationId":"answerCard","security":[{"BearerAuth":["write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"The numeric id, as returned by the list endpoint or when it was created."},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"required":false,"description":"Any unique string, such as a UUID. Send the same key when you retry a write: the first answer is replayed (marked `X-Idempotent-Replay: true`) instead of doing the work twice. Keys are remembered for 24 hours. Reusing a key with a different request is `422 idempotency_conflict`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerCardInput"},"example":{"rating":"good","timeTakenMs":4200}}}},"responses":{"200":{"description":"The card was scheduled and the review logged","content":{"application/json":{"schema":{"type":"object","properties":{"revlogId":{"type":"integer","description":"Id of the review-log entry, a millisecond timestamp."},"leeched":{"type":"boolean","description":"`true` when this answer made the card a leech."},"buriedSiblingIds":{"type":"array","items":{"type":"integer"},"description":"Other cards of the same note that were buried until tomorrow."},"card":{"$ref":"#/components/schemas/Card","description":"The card as stored after the answer."}},"required":["revlogId","leeched","buriedSiblingIds","card"]}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/IdempotencyConflict"},"423":{"$ref":"#/components/responses/Locked"},"429":{"$ref":"#/components/responses/RateLimited"}},"tags":["Cards"],"description":"Answers a card, the same as pressing a button in Anki's reviewer. The card is rescheduled with the deck preset's v3 scheduler (or FSRS when the preset turns it on), a review-log entry is written, the deck's daily counts go up, and siblings are buried if the preset says so. A card that reaches the leech threshold gets the `leech` tag and, if the preset says so, is suspended.\n\n`rating` is `again`, `hard`, `good` or `easy` (or 1 to 4). `timeTakenMs` is how long the learner looked at the card, capped by the preset. Send an `Idempotency-Key` so a retry does not answer twice.\n\nThe card must not be suspended or buried, and the collection must use the v3 scheduler; otherwise the answer is `409 card_state_error`. A card in a filtered deck is answered as Anki does. The preset, the answer-time cap and burying come from the card's home deck, the daily counts go to the filtered deck and its parents, and the review log records an answer given before the card was due (or in a deck that does not reschedule) with type 3 (*filtered*). In a deck that reschedules, a review card that is passed returns to its home deck, and a learning card stays in the filtered deck until it graduates; in a deck that does not reschedule, nothing is rescheduled: Again, Hard and Good bring the card back after the deck's preview delay, and the card returns to its home deck in its old state when the preview ends. The legacy schedulers are not supported. A card that has no FSRS memory state yet (for example one that was just moved into an FSRS deck) gets one replayed from its review history, as Anki does, and from its SM-2 ease and interval when the history is too short.\n\nThe load balancer is applied to review intervals as in Anki (see `GET /api/v1/cards/{id}/scheduling`). It reads the cards due in the next 99 days when the card is answered (Anki reads them once when it builds a queue), and the weekday used for Easy Days is taken in the collection's `localOffset` (UTC when unset)."}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Create a key in the dashboard under **API Keys & Devices** and send it as `Authorization: Bearer <key>`. A key with the `read` scope may call every `GET` endpoint; a key with the `write` scope may call all of them. A key that only has the `sync` scope cannot use this API.","bearerFormat":"API key"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"correlationId":{"type":"string"}},"required":["code","message"]},"correlationId":{"type":"string"}},"required":["error"]},"Deck":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id. Stable for the life of the deck."},"name":{"type":"string","description":"Full path, with `::` between parent and child."}},"required":["id","name"]},"CreateDeckInput":{"type":"object","properties":{"name":{"type":"string","description":"Deck name. Use `::` to nest it."}},"required":["name"]},"RenameDeckInput":{"type":"object","properties":{"name":{"type":"string","description":"The new full name."}},"required":["name"]},"Note":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id. Stable for the life of the note."},"guid":{"type":"string","description":"Globally unique id. Anki uses it to tell whether an imported note is the same one."},"notetypeId":{"type":"integer","description":"The note type this note was made with."},"mtime":{"type":"integer","description":"When the note last changed, in seconds since 1970."},"usn":{"type":"integer","description":"Update sequence number used by sync. `-1` means changed here and not yet synced."},"tags":{"type":"array","items":{"type":"string"},"description":"Tags on the note."},"fields":{"type":"object","additionalProperties":{"type":"string"},"description":"Every field's content, keyed by the field's key (its name in camelCase, such as `backExtra`), in the order of the note type's fields. May contain HTML."}},"required":["id","guid","notetypeId","tags","fields"]},"NoteDetails":{"allOf":[{"$ref":"#/components/schemas/Note"},{"type":"object","properties":{"cards":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedCard"}}}}]},"CreateNoteInput":{"type":"object","properties":{"notetypeName":{"type":"string","description":"Which note type to use, by name (ignoring case). Send this or `notetypeId`."},"notetypeId":{"type":"integer","description":"Which note type to use, by id. See [List note types](#listNoteTypes). Send this or `notetypeName`."},"deckName":{"type":"string","description":"The deck that receives the new cards, by name (ignoring case), such as `Languages::German`. Send this or `deckId`."},"deckId":{"type":"integer","description":"The deck that receives the new cards, by id. Without a deck the note goes to the `Default` deck."},"fields":{"type":"object","additionalProperties":{"type":"string"},"description":"The field contents, keyed by each field's key: its name in camelCase (`Back Extra` is `backExtra`) or its exact name. Fields left out are empty. HTML is allowed."},"tags":{"type":"array","items":{"type":"string"},"description":"Tags to put on the note."},"guid":{"type":"string","description":"Optional. Only set it to keep an identity from another system."}},"required":["fields"]},"Tag":{"type":"object","properties":{"name":{"type":"string","description":"The tag, spelled as it is stored."},"noteCount":{"type":"integer","description":"How many notes carry this tag or a tag nested under it."}},"required":["name","noteCount"]},"CreateTagInput":{"type":"object","properties":{"name":{"type":"string","description":"The tag, with `::` for nesting. It can not contain a space or have an empty part."}},"required":["name"]},"CreatedTag":{"type":"object","properties":{"name":{"type":"string","description":"The tag, spelled as it is stored."},"created":{"type":"boolean","description":"`false` when the tag already existed."}},"required":["name","created"]},"RenameTagInput":{"type":"object","properties":{"newName":{"type":"string","description":"The new name. If it is already a tag, the two are merged."}},"required":["newName"]},"RenamedTag":{"type":"object","properties":{"name":{"type":"string","description":"The tag's new name, as stored."},"notes":{"type":"integer","description":"How many notes were changed."}},"required":["name","notes"]},"CreateTagsBatchInput":{"type":"object","properties":{"names":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string"},"description":"The tags to create, 1 to 200."},"atomic":{"type":"boolean","default":false,"description":"Write all or nothing. See [Create many notes](#createNotesBatch)."}},"required":["names"]},"CreateTagsBatchResult":{"type":"object","properties":{"atomic":{"type":"boolean","description":"Echoes the request."},"committed":{"type":"boolean","description":"Whether anything was written."},"created":{"type":"integer","description":"How many tags were new."},"failed":{"type":"integer","description":"How many entries were refused."},"results":{"type":"array","items":{"$ref":"#/components/schemas/CreateTagsBatchItem"},"description":"One entry per name sent, in order."}},"required":["atomic","committed","created","failed","results"]},"CreateTagsBatchItem":{"type":"object","properties":{"index":{"type":"integer"},"ok":{"type":"boolean"},"name":{"type":"string"},"created":{"type":"boolean","description":"`false` when the tag already existed."},"error":{"$ref":"#/components/schemas/ItemError"}},"required":["index","ok"]},"UpdateTagsBatchInput":{"type":"object","properties":{"updates":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/UpdateTagsBatchEntry"},"description":"The renames, 1 to 200."},"atomic":{"type":"boolean","default":false,"description":"Write all or nothing. See [Create many notes](#createNotesBatch)."}},"required":["updates"]},"UpdateTagsBatchEntry":{"type":"object","properties":{"name":{"type":"string","description":"The tag to rename."},"newName":{"type":"string","description":"Its new name."}},"required":["name","newName"]},"UpdateTagsBatchResult":{"type":"object","properties":{"atomic":{"type":"boolean"},"committed":{"type":"boolean","description":"Whether anything was written."},"renamed":{"type":"integer","description":"How many tags were renamed."},"failed":{"type":"integer","description":"How many entries were refused."},"results":{"type":"array","items":{"$ref":"#/components/schemas/UpdateTagsBatchItem"},"description":"One entry per update sent, in order."}},"required":["atomic","committed","renamed","failed","results"]},"UpdateTagsBatchItem":{"type":"object","properties":{"index":{"type":"integer"},"ok":{"type":"boolean"},"name":{"type":"string"},"newName":{"type":"string","description":"The name the tag now has, as stored."},"notes":{"type":"integer","description":"How many notes were changed."},"error":{"$ref":"#/components/schemas/ItemError"}},"required":["index","ok"]},"DeleteTagsBatchInput":{"type":"object","properties":{"names":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"string"},"description":"The tags to delete, 1 to 1000."}},"required":["names"]},"UpdateNoteInput":{"type":"object","properties":{"fields":{"type":"object","additionalProperties":{"type":"string"},"description":"The fields to change, keyed by each field's key (its name in camelCase, such as `backExtra`) or its exact name. Fields left out keep their content. HTML is allowed."},"tags":{"type":"array","items":{"type":"string"},"description":"The complete list of tags; it replaces the existing tags."}}},"MediaFile":{"type":"object","properties":{"name":{"type":"string"},"size":{"type":"integer"},"sha1":{"type":"string"},"sha256":{"type":["string","null"]},"contentType":{"type":"string"},"mtime":{"type":"integer"},"usn":{"type":"integer"}},"required":["name","size","sha1","contentType","mtime","usn"]},"MediaPutResult":{"allOf":[{"$ref":"#/components/schemas/MediaFile"},{"type":"object","properties":{"created":{"type":"boolean"},"unchanged":{"type":"boolean"}},"required":["created","unchanged"]}]},"GeneratedCard":{"type":"object","properties":{"id":{"type":"integer","description":"Numeric id of the card."},"ord":{"type":"integer","description":"Which template of the note type made this card (0 is the first)."},"deckId":{"type":"integer","description":"The deck the card was put in."},"due":{"type":"integer","description":"Position in the new-card queue."}},"required":["id","ord","deckId","due"]},"Card":{"type":"object","properties":{"id":{"type":"integer"},"noteId":{"type":"integer","description":"The note this card was made from."},"deckId":{"type":"integer"},"ord":{"type":"integer","description":"Which template of the note type made this card (0 is the first)."},"mtime":{"type":"integer"},"usn":{"type":"integer"},"type":{"type":"integer","description":"0 new, 1 learning, 2 review, 3 relearning."},"queue":{"type":"integer","description":"Where the card sits in the scheduler: -3 or -2 buried, -1 suspended, 0 new, 1 learning, 2 review, 3 day-learning, 4 preview."},"due":{"type":"integer","description":"When it is due. For a review card this is a day number; for a new card, its position in the queue."},"interval":{"type":"integer","description":"Days until the next review, once it is a review card."},"factor":{"type":"integer","description":"Ease factor in permille: 2500 means 250%."},"reps":{"type":"integer","description":"How many times it has been reviewed."},"lapses":{"type":"integer","description":"How many times it was forgotten after being learnt."},"left":{"type":"integer"},"originalDue":{"type":"integer"},"originalDeckId":{"type":"integer"},"flags":{"type":"integer","description":"Colour flag, 0 for none."}},"required":["id","noteId","deckId","ord"]},"ItemError":{"type":"object","properties":{"code":{"type":"string","description":"`not_found`, `invalid_input`, `deck_error`, `card_state_error`, `validation_error`, or `batch_aborted` for an entry that was fine but not written because an atomic call was refused."},"message":{"type":"string","description":"A sentence explaining the refusal."}},"required":["code","message"]},"CreateNotesBatchInput":{"type":"object","properties":{"notes":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/CreateNoteInput"},"description":"The notes to create, 1 to 200, each shaped like the body of [Create a note](#createNote). An entry that is not valid is reported in its slot."},"atomic":{"type":"boolean","default":false,"description":"Write all or nothing: one refused entry means nothing is written (`422`)."}},"required":["notes"]},"CreateNotesBatchResult":{"type":"object","properties":{"atomic":{"type":"boolean"},"committed":{"type":"boolean"},"created":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/CreateNotesBatchItem"}}},"required":["atomic","committed","created","failed","results"]},"CreateNotesBatchItem":{"type":"object","properties":{"index":{"type":"integer","description":"Position of the note in `notes`."},"ok":{"type":"boolean","description":"`true` when the note was created."},"id":{"type":"integer","description":"Numeric id of the new note."},"guid":{"type":"string","description":"The note's globally unique id."},"cards":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedCard"},"description":"The cards its note type's templates produced."},"newTags":{"type":"array","items":{"type":"string"},"description":"Tags this note introduced; a tag is listed only for the first note that uses it."},"error":{"$ref":"#/components/schemas/ItemError","description":"Why the note was refused. Absent when `ok`."}},"required":["index","ok"]},"UpdateNotesBatchInput":{"type":"object","properties":{"updates":{"type":"array","minItems":1,"maxItems":200,"items":{"$ref":"#/components/schemas/UpdateNotesBatchEntry"},"description":"The edits, 1 to 200. An entry that is not valid is reported in its slot."},"atomic":{"type":"boolean","default":false,"description":"Write all or nothing: one refused entry means nothing is written (`422`)."}},"required":["updates"]},"UpdateNotesBatchEntry":{"type":"object","properties":{"id":{"type":"integer","description":"The note to edit."},"fields":{"type":"object","additionalProperties":{"type":"string"},"description":"The fields to change, keyed like `UpdateNoteInput.fields`. Fields left out keep their content."},"tags":{"type":"array","items":{"type":"string"},"description":"Replaces all the note's tags. Not with `addTags` or `removeTags`."},"addTags":{"type":"array","items":{"type":"string"},"description":"Tags to add to the ones the note has."},"removeTags":{"type":"array","items":{"type":"string"},"description":"Tags to remove from the ones the note has, ignoring case. A removed tag takes the tags below it (`a::b` for `a`) with it."}},"required":["id"]},"UpdateNotesBatchResult":{"type":"object","properties":{"atomic":{"type":"boolean"},"committed":{"type":"boolean"},"changed":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/UpdateNotesBatchItem"}}},"required":["atomic","committed","changed","failed","results"]},"UpdateNotesBatchItem":{"type":"object","properties":{"index":{"type":"integer","description":"Position of the entry in `updates`."},"ok":{"type":"boolean","description":"`true` when the note exists and the edit was valid."},"id":{"type":"integer","description":"The note id."},"changed":{"type":"boolean","description":"`false` when the note already matched, so nothing was written for it."},"newCards":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedCard"},"description":"Cards that the new field content caused to be generated."},"newTags":{"type":"array","items":{"type":"string"},"description":"Tags this entry introduced; a tag is listed only for the first entry that uses it."},"error":{"$ref":"#/components/schemas/ItemError","description":"Why the entry was refused. Absent when `ok`."}},"required":["index","ok"]},"DeleteCardsBatchInput":{"type":"object","properties":{"ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"integer"},"description":"The cards to delete, 1 to 1000."}},"required":["ids"]},"UpdateCardsBatchInput":{"type":"object","properties":{"ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"integer"},"description":"The cards to change, 1 to 1000. Repeats are ignored."},"patch":{"$ref":"#/components/schemas/UpdateCardInput","description":"The change, shaped like the body of [Edit a card](#updateCard)."},"atomic":{"type":"boolean","default":false,"description":"Write all or nothing: one refused card means nothing is written (`422`)."}},"required":["ids","patch"]},"UpdateCardsBatchResult":{"type":"object","properties":{"atomic":{"type":"boolean"},"committed":{"type":"boolean"},"changed":{"type":"integer"},"failed":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/UpdateCardsBatchItem"}}},"required":["atomic","committed","changed","failed","results"]},"UpdateCardsBatchItem":{"type":"object","properties":{"id":{"type":"integer","description":"The card id."},"ok":{"type":"boolean","description":"`true` when the card exists and could take the change."},"changed":{"type":"boolean","description":"`false` when the card already matched, so nothing was written for it."},"card":{"anyOf":[{"$ref":"#/components/schemas/Card"},{"type":"null"}],"description":"The card as stored after the call."},"error":{"$ref":"#/components/schemas/ItemError","description":"Why the card was refused. Absent when `ok`."}},"required":["id","ok"]},"DeleteNotesBatchInput":{"type":"object","properties":{"ids":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"integer"},"description":"The notes to delete, 1 to 1000."}},"required":["ids"]},"UpdateCardInput":{"type":"object","properties":{"deckId":{"type":"integer"},"flag":{"type":"integer","minimum":0,"maximum":7},"suspended":{"type":"boolean"},"buried":{"type":"boolean"}}},"AnswerCardInput":{"type":"object","properties":{"rating":{"oneOf":[{"type":"string","enum":["again","hard","good","easy"]},{"type":"integer","minimum":1,"maximum":4}]},"timeTakenMs":{"type":"integer","minimum":0,"maximum":3600000}},"required":["rating"]},"RenderedCard":{"type":"object","properties":{"cardId":{"type":"integer"},"noteId":{"type":"integer"},"notetypeId":{"type":"integer"},"deckId":{"type":"integer"},"ord":{"type":"integer"},"isEmpty":{"type":"boolean"},"math":{"type":"boolean","description":"The question or the answer holds a formula (`\\(...\\)` or `\\[...\\]`) for MathJax to typeset."},"question":{"type":"string"},"answer":{"type":"string"},"css":{"type":"string"},"media":{"type":"array","items":{"type":"string"}}},"required":["cardId","question","answer","css","media","math"]},"CardScheduling":{"type":"object","properties":{"cardId":{"type":"integer"},"fsrs":{"type":"boolean"},"today":{"type":"integer"},"nextDayAt":{"type":"integer"},"buttons":{"type":"array","items":{"type":"object","properties":{"rating":{"type":"string","enum":["again","hard","good","easy"]},"intervalSecs":{"type":"integer"}},"required":["rating","intervalSecs"]}}},"required":["cardId","fsrs","buttons"]},"NotetypeChange":{"type":"object","properties":{"fieldsAdded":{"type":"integer","description":"Fields that are new."},"fieldsRemoved":{"type":"integer","description":"Fields that were removed, with their content."},"fieldsMoved":{"type":"integer","description":"Fields that kept their data but changed position."},"sortFieldChanged":{"type":"boolean","description":"`true` when a different field is now the one the browser sorts by."},"templatesAdded":{"type":"integer","description":"Card templates that are new."},"templatesRemoved":{"type":"integer","description":"Card templates that were removed."},"templatesMoved":{"type":"integer","description":"Card templates that changed position."},"destructive":{"type":"boolean","description":"`true` when any field or template was removed, so note or card data was lost."}},"required":["fieldsAdded","fieldsRemoved","fieldsMoved","sortFieldChanged","templatesAdded","templatesRemoved","templatesMoved","destructive"]},"NoteTypeDetails":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"kind":{"type":"string","enum":["normal","cloze"],"description":"`normal` makes one card per template; `cloze` makes one per cloze deletion."},"css":{"type":"string","description":"Stylesheet shared by every card of this note type."},"sortFieldIdx":{"type":"integer","description":"Index of the field the browser sorts by."},"latexPre":{"type":"string"},"latexPost":{"type":"string"},"latexSvg":{"type":"boolean"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/NoteTypeField"}},"templates":{"type":"array","items":{"type":"object"}}},"required":["id","name","kind","fields","templates"]},"NoteTypeField":{"type":"object","properties":{"ord":{"type":"integer"},"name":{"type":"string","description":"The field's name as Anki shows it."},"key":{"type":"string","description":"The key of this field in the `fields` of a note, whether you create, read or edit it: its name in camelCase (`The field` is `theField`). When two fields would get the same key, or a field's exact name is another field's key, they keep their exact names."},"sticky":{"type":"boolean"},"rtl":{"type":"boolean"},"plainText":{"type":"boolean"},"collapsed":{"type":"boolean"},"excludeFromSearch":{"type":"boolean"},"preventDeletion":{"type":"boolean"},"description":{"type":"string"},"fontName":{"type":"string"},"fontSize":{"type":"integer"}},"required":["ord","name","key"]},"CreateNotetypeInput":{"type":"object","properties":{"stock":{"type":"string","description":"Start from one of Anki's stock designs."},"name":{"type":"string","description":"Name of the note type."},"kind":{"type":"string","enum":["normal","cloze"]},"fields":{"type":"array","items":{"type":"object"},"description":"Field definitions, in order. Each needs a `name`."},"templates":{"type":"array","items":{"type":"object"},"description":"Card templates. Each needs a `name`, a question format `qFormat` and an answer format `aFormat`."},"css":{"type":"string"},"sortFieldIdx":{"type":"integer"}}},"UpdateNotetypeInput":{"type":"object","properties":{"name":{"type":"string"},"css":{"type":"string"},"sortFieldIdx":{"type":"integer"},"fields":{"type":"array","items":{"type":"object"},"description":"The full list of fields. A field with an `ord` is an existing one being kept or renamed; one without is new; an existing one that is missing is removed."},"templates":{"type":"array","items":{"type":"object"},"description":"The full list of templates, following the same rule as `fields`."}}}},"responses":{"BadRequest":{"description":"The request was understood but is not valid, for example an unknown deck or a `fields` key that names no field of the note type. The code is `invalid_input`, `deck_error` or `card_type_error`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BatchRefused":{"description":"The body failed validation (`validation_error`), or the call was `atomic` and an entry was refused: then the body is the usual batch answer with every entry's `error`, and nothing was written.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/CreateNotesBatchResult"},{"$ref":"#/components/schemas/UpdateNotesBatchResult"},{"$ref":"#/components/schemas/UpdateCardsBatchResult"}]}}}},"Busy":{"description":"Other batch operations are using the server. Wait the number of seconds in `Retry-After` and retry; nothing was changed.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The card's template has a syntax error or refers to a field that does not exist (`render_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The `Authorization` header is missing, or the key is invalid, disabled or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks the scope, the account is suspended or its email unverified, the account is over its storage quota (`over_quota`; deleting is still allowed), or a destructive schema change is switched off (`destructive_change_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No deck, note, note type or card has that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The change alters the collection's schema and needs `?acceptFullSync=true` (`full_sync_required`), or the card is in a state that does not allow this (`card_state_error`: suspended, buried, in a filtered deck, or an unsupported scheduler).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"IdempotencyConflict":{"description":"The body does not match the earlier request that used this `Idempotency-Key` (`idempotency_conflict`), or the body failed validation (`validation_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"A media file is larger than 100 MiB (`payload_too_large`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"The plan's rate limit is used up: a limit per minute on every plan, and a limit per day on Free. Nothing was changed. Wait Retry-After seconds and send the same request again.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying"},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed in the window that was used up"},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in that window (0 here)"},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until that window starts again"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Locked":{"description":"Another write or an Anki sync is using the collection. Wait the number of seconds in `Retry-After` and retry; nothing was changed.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"tags":[{"name":"Decks","description":"Decks group cards for study. Nest them with `::`, for example `Languages::German`."},{"name":"Notes","description":"A note holds the content (its fields and tags). Anki turns each note into one or more cards using the note type's templates."},{"name":"Note types","description":"A note type is the template for notes: its fields, and the card templates that decide which cards a note produces."},{"name":"Tags","description":"The labels notes carry. A tag is created when a note brings it, or here with no note yet. List them, rename a tag (its nested tags follow) or delete it from every note. Nested tags keep Anki's `::` separator; a tag name is one path segment, so percent-encode it (`Languages%3A%3AGerman`). The names `batch-create`, `batch-update` and `batch-delete` belong to the batch endpoints: a tag with such a name can only be reached through them."},{"name":"Cards","description":"A card is one thing to review, made from a note. Change what a card shows by editing its note; here you can search, flag, suspend, bury, move and delete cards, render them for display, and answer them with Anki's v3 scheduler (SM-2 or FSRS, as the deck's preset says)."},{"name":"Study","description":"What to study next. The study queue is built the way Anki builds it, from the deck presets, daily limits and the current time."},{"name":"Media","description":"The images, audio and video your cards refer to. They are the same files the Anki desktop and mobile clients sync: a file you put here reaches your devices on their next media sync, and a file synced from a device can be read here."}],"servers":[{"url":"/","description":"This server"}]}