Skip to content
AnkinizeOpenAPILog in
OverviewSync with AnkiAuthenticationAPI referenceTypeScript SDKAnkiConnect alternativeAI agentsMCP serverSelf-host

Tutorials

Browser extension

AI agents and Ankinize

Give an AI agent an Ankinize API key and it can create decks, write flashcards, search your notes and read what is due, through the REST API or the TypeScript SDK. The cards sync to Anki Desktop, AnkiDroid and AnkiMobile like any others. This is how you ankinize anything an agent can read: an article, a lecture, a chat, a codebase.

Give the agent access#

  1. Create a key under API Keys & Devices. Use the read scope for an agent that only looks things up, and write for one that adds cards. See Authentication.
  2. Put it in the agent's environment as ANKINIZE_API_KEY. Never paste it into a prompt.
  3. Point the agent at the skills below, or hand it the tools in the next section.

For AI agents#

Agent skills are plain Markdown files an agent loads to learn the API. They are generated from the OpenAPI document, so they match the server.

ResourceWhat it is
https://ankinize.com/.well-known/agent-skills/ankinize-sdk/SKILL.mdThe agent skill for the TypeScript SDK: set-up, every function, batches, errors, retries and type-safe names. Load this when the task is TypeScript or JavaScript.
https://ankinize.com/.well-known/agent-skills/ankinize-api/SKILL.mdThe agent skill for the REST API: authentication, conventions and every endpoint. Use it from any other language or with plain HTTP.
https://ankinize.com/.well-known/agent-skills/index.jsonThe discovery index of both skills, with the SHA-256 of each file.
https://ankinize.com/llms.txtThe llms.txt index: what Ankinize is, and a link to every page and skill.
https://ankinize.com/llms-full.txtEvery documentation page in one Markdown file.
https://ankinize.com/api/v1/openapi.jsonThe OpenAPI document: exact parameters and responses. SDK functions are named after its operation ids.

Any page of these docs is also available as Markdown: add .md to its address (https://ankinize.com/docs/sdk.md), or ask for it with Accept: text/markdown.

A working agent in 85 lines#

This script gives a Claude model three tools, backed by the SDK: list decks, search notes and add cards. Run it with a request in plain language. The model decides which tool to call and the loop feeds the results back until it answers.

agent.ts
import Anthropic from "@anthropic-ai/sdk";
import { client, createNotes, listDecks, listNotes } from "@ankinize/sdk";

client.setConfig({ auth: process.env.ANKINIZE_API_KEY });

const tools: Anthropic.Tool[] = [
  {
    name: "list_decks",
    description: "List the decks in the user's Anki collection.",
    input_schema: { type: "object", properties: {} },
  },
  {
    name: "search_notes",
    description: "Search notes with Anki search syntax, such as 'deck:German tag:a1'.",
    input_schema: {
      type: "object",
      properties: { query: { type: "string" } },
      required: ["query"],
    },
  },
  {
    name: "add_cards",
    description: "Add Basic flashcards to a deck.",
    input_schema: {
      type: "object",
      properties: {
        deck: { type: "string" },
        cards: {
          type: "array",
          items: {
            type: "object",
            properties: { front: { type: "string" }, back: { type: "string" } },
            required: ["front", "back"],
          },
        },
      },
      required: ["deck", "cards"],
    },
  },
];

async function run(name: string, input: any): Promise<unknown> {
  if (name === "list_decks") return (await listDecks()).decks;
  if (name === "search_notes") return (await listNotes({ q: input.query, limit: 20 })).notes;
  if (name === "add_cards") {
    const result = await createNotes(
      input.cards.map((card: { front: string; back: string }) => ({
        notetypeName: "Basic",
        deckName: input.deck,
        fields: { front: card.front, back: card.back },
      })),
    );
    return { created: result.created, failed: result.failed };
  }
  throw new Error(`unknown tool ${name}`);
}

const anthropic = new Anthropic();
const messages: Anthropic.MessageParam[] = [
  { role: "user", content: process.argv[2] ?? "Add 5 cards about the French Revolution to a deck called History." },
];

for (;;) {
  const reply = await anthropic.messages.create({
    model: "claude-sonnet-5-5",
    max_tokens: 2048,
    tools,
    messages,
  });
  messages.push({ role: "assistant", content: reply.content });
  if (reply.stop_reason !== "tool_use") {
    console.log(reply.content.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("\n"));
    break;
  }
  const results: Anthropic.ToolResultBlockParam[] = [];
  for (const part of reply.content) {
    if (part.type !== "tool_use") continue;
    try {
      results.push({ type: "tool_result", tool_use_id: part.id, content: JSON.stringify(await run(part.name, part.input)) });
    } catch (error) {
      results.push({ type: "tool_result", tool_use_id: part.id, is_error: true, content: String(error) });
    }
  }
  messages.push({ role: "user", content: results });
}
Run
bun add @ankinize/sdk @anthropic-ai/sdk
ANKINIZE_API_KEY=... ANTHROPIC_API_KEY=... bun agent.ts "Add 5 cards on the French Revolution to History"

The same three functions fit any agent framework or tool-calling API: only the tool schemas change.

Use MCP instead of writing a loop

If your client speaks MCP (Claude, Cursor, VS Code and others), skip the script: connect it to https://ankinize.com/mcp and it gets every SDK function as a tool, with sign-in handled for you. See the MCP server.

Letting an agent write safely#

ConcernWhat to do
The agent retries a callSend an Idempotency-Key on writes. The first answer is replayed instead of adding the card twice.
Anki is syncingA write answers 423 with a Retry-After. Wait that many seconds and try again.
Too many requestsPast the plan limit the API answers 429 and changes nothing. Use the batch calls: one request can create many notes.
A bad batchEach entry reports its own ok and error, so one bad card does not hide the others. Pass atomic to write all or nothing.
Changing a note typeAdding a field forces a Full Sync on every device, so the API asks for acceptFullSync=true first. Do not set it blindly from an agent.
Reading onlyUse a read key. Every write is then refused.

More#

Put a card in one click with a browser extension. Coming from AnkiConnect? See the comparison.

On this page

  • Give the agent access
  • For AI agents
  • A working agent in 85 lines
  • Letting an agent write safely
  • More