Skip to content

ANKIDROID

github-actions[bot] edited this page Oct 11, 2026 · 2 revisions

AnkiDroid integration APIs — ContentProvider, intents, JS bridge

Source: ankidroid/Anki-Android (api/ module), the AnkiDroid API wiki page and the JavaScript API wiki page, plus the ankidroid/apisample demo app. AnkiDroid is the official Anki client for Android; these APIs are official for the Android app, but they are Android-IPC surfaces — nothing here talks to ankiweb.net. For the network APIs see SYNC.md and ENDPOINTS.md.

Jump to: Surfaces · Setup · Methods · Provider URIs · Columns · Intents · Hidden surfaces · JS API · Testing

API surfaces

AnkiDroid exposes three independent APIs:

Surface Audience Transport Use for
Instant-Add / ContentProvider (api/) other Android apps content://com.ichi2.anki.flashcards Reading/writing notes, cards, decks, models, media
Intents any Android app Android intents Fallback sharing + triggering a sync
JavaScript API (AnkiDroidJS) deck/template authors WebView bridge inside the reviewer Custom card UIs calling native functions

A fourth path — bulk/low-level access — is just the raw ContentProvider with your own queries; the Instant-Add library is the supported wrapper over it.

Client setup (Instant-Add)

Gradle (JitPack), from the wiki:

// root build.gradle
repositories { maven { url "https://jitpack.io" } }

// module build.gradle
dependencies {
    implementation 'com.github.ankidroid:Anki-Android:api-v1.1.0'
}

Required manifest entry on Android 11+ (SDK 30), otherwise you get Failed to find provider info for com.ichi2.anki.flashcards:

<manifest>
    ...
    <queries>
        <package android:name="com.ichi2.anki" />
    </queries>
</manifest>

Permissions and switches:

Item Value
Permission (merged into your manifest by the library) com.ichi2.anki.permission.READ_WRITE_DATABASE
Runtime permission Enforced dynamically on Android M+; deny ⇒ fall back to the ACTION_SEND intent
User toggle AnkiDroid → Settings → Advanced → Enable AnkiDroid API (off ⇒ provider unavailable)
Availability check AddContentApi.getAnkiDroidPackageName(context) → package name or null
Provider spec version api.apiHostSpecVersion → 1 (AnkiDroid 2.5: slow bulk ops, model-persist quirks) or 2 (2.6+); -1 if not installed
Constants AddContentApi.DEFAULT_DECK_ID = 1, AddContentApi.READ_WRITE_PERMISSION

Minimal add (from the wiki; addNotes() is orders of magnitude faster than looping addNote()):

if (AddContentApi.getAnkiDroidPackageName(context) != null) {
    AddContentApi api = new AddContentApi(context);
    long deckId = api.addNewDeck("My app name");
    long modelId = api.addNewBasicModel("com.something.myapp");
    api.addNote(modelId, deckId, new String[] {"日の出", "sunrise"}, null);
} else {
    // fallback: ACTION_SEND share intent (see Intent API)
}

AddContentApi methods

From api/src/main/java/com/ichi2/anki/api/AddContentApi.kt:

Method Returns Purpose
addNote(modelId, deckId, fields, tags) Long? Insert one note; cards land in deckId; no duplicate check
addNotes(modelId, deckId, fieldsList, tagsList) Int Bulk insert (bulkInsert + ?deckId= query param); returns count added
addMediaFromUri(fileUri, preferredName, mimeType) String? Copy a file into collection.media; returns stored file name
findDuplicateNotes(mid, key) List<NoteInfo?> Duplicate lookup for one first-field key
findDuplicateNotes(mid, keys) SparseArray<…>? Duplicate lookup for many keys (fast path on spec ≥ 2)
getNoteCount(mid) Int Note count for a model
updateNoteTags(noteId, tags) Boolean Replace a note's tags; throws SecurityException if permission missing
updateNoteFields(noteId, fields) Boolean Replace a note's fields
getNote(noteId) NoteInfo? Read id/fields/tags of one note
previewNewNote(mid, flds) Map<String, Map<String, String>>? Render a would-be note (requires full read-write permission)
addNewBasicModel(name) Long? Create the standard two-field note type
addNewBasic2Model(name) Long? Create the "Basic (and reversed card)" note type
addNewCustomModel(name, fields, cards, qfmt, afmt, css, did, sortf, …) Long? Create a fully custom note type
getFieldList(modelId) Array<String>? Field names of a model
getModelList(minNumFields) Map<Long, String>? Model id → name (filtered by field count)
getModelName(mid) String? Model name lookup
addNewDeck(deckName) Long? Create a deck; returns its id
setFlagRaw(noteId, rawFlag) Boolean Set flag by raw int on all of the note's cards
setFlag(noteId, flag) Boolean Same, using the Flag enum
selectedDeckName (property) String? Name of the currently selected deck
deckList (property) Map<Long, String>? All deck id → name pairs
getDeckName(did) String? Deck name by id
apiHostSpecVersion (property) Int Installed provider's spec version (1/2, -1 absent)
getAnkiDroidPackageName(context) (static) String? Package providing authority com.ichi2.anki.flashcards

Supporting types (same package):

  • NoteInfo — getId(), getFields(), getTags(), getKey() (first field).
  • Flag — NONE(0) RED(1) ORANGE(2) GREEN(3) BLUE(4) PINK(5) TURQUOISE(6) PURPLE(7).
  • Ease — EASE_1(1) … EASE_4(4) (Again/Hard/Good/Easy).

ContentProvider URI map

Authority: com.ichi2.anki.flashcards (constant FlashCardsContract.AUTHORITY), base content://com.ichi2.anki.flashcards. The contract lives in FlashCardsContract.kt.

URI (path under the authority) Query Insert Update Delete Notes
/notes ✅ ✅ — — Selection is libanki browser search syntax; insert needs mid, flds, tags; bulk insert honours ?deckId=
/notes_v2 ✅ — — — Same rows, selection is a direct SQL WHERE
/notes/<id> ✅ — ✅ ✅ Update: flds, tags. Delete removes the note
/notes/<id>/cards ✅ — ✗ — All cards of the note (deck, question, answer)
/notes/<id>/cards/<ord> ✅ — ✅ — Update: deck_id (move card), flags
/models ✅ ✅ ✗ — Note types; bulk update rejected
/models/<id> (or current) ✅ — ✅ — Update: deck_id (default deck for the type)
/models/<id>/empty_cards — — — ✅ Registered + getType, but query throws; delete purges empty cards
/models/<id>/templates ✅ ✅ ✗ — Read/add card templates (see CardTemplate below)
/models/<id>/templates/<ord> ✅ — ✅ — Edit one template's qfmt/afmt
/models/<id>/fields — ✅ — — Insert-only — query is not implemented and throws
/cards ✅ — — — All cards with scheduling columns (Card.DEFAULT_COLUMNS)
/cards/<id> ✅ — — — Single card, read-only
/schedule ✅ — ✅ — ReviewInfo: query next due cards (limit=?, deckID=?), update = answer a card
/decks ✅ ✅ ✗ — All decks; insert = create (deck_name)
/decks/<id> ✅ — ✗* — *Update is stubbed “Not yet implemented”
/selected_deck ✅ — ✅ — Update: deck_id selects the active deck
/media — ✅ — — Copy a file into collection.media (file_uri, preferred_name)

— = no branch in the provider (throws IllegalArgumentException / UnsupportedOperationException); ✗ = explicitly rejected with a message. The full matcher list is the addUri(...) block in CardContentProvider.kt (lines 186–207) — 18 paths, several of which the wiki never mentions. There is no ContentProvider.call() RPC (no call() override exists), so this URI surface is the entire provider API.

MIME types follow vnd.android.cursor.(dir|item)/vnd.com.ichi2.anki.(note|card|model).

Column reference

Note (/notes)

Column Type Notes
_id long Note id (same as Anki's note id)
guid string Stable unique id
mid long Model (note type) id
flds string Fields joined with \x1f (Utils.joinFields)
tags string Space-separated tag list
sfld string Sort field
csum long Checksum of the first field (duplicate detection)
flags, data, mod, usn, allow_empty — Card flags, aux data, modification metadata

Insert with ContentValues(mid, flds, tags); bulk insert honours the deckId query parameter (Note.DECK_ID_QUERY_PARAM).

Card (/cards)

Column Notes
_id, note_id, ord, card_name, deck_id Identity + template ordinal
question, answer, question_simple, answer_simple, answer_pure Rendered content (read-only views)
reps, lapses, type, original_deck_id Counters and origin
queue, due, original_due, interval, sm2_factor, left, original_position, custom_data Raw scheduling
fsrs_stability, fsrs_difficulty, fsrs_desired_retention, fsrs_decay, last_review_time_secs FSRS scheduler state
flags Flag colour (see Flag enum)

Default projection: _id, note_id, ord, card_name, deck_id, question, answer, flags.

Model (/models)

_id, name, field_names, num_cards, note_count, css, sort_field_index, type, latex_pre, latex_post, deck_id. CURRENT_MODEL_ID = "current" addresses the current model.

CardTemplate (/models/<id>/templates)

_id, model_id, ord, card_template_name (NAME), question_format (QFMT), answer_format (AFMT), browser_question_format, browser_answer_format, card_count. Queryable per template via /models/<id>/templates/<ord>; insertable in bulk via /models/<id>/templates.

Deck (/decks, /selected_deck)

deck_name, deck_id, deck_count, options, deck_dyn, deck_desc.

ReviewInfo (/schedule)

Read: note_id, ord, button_count, next_review_times, media_files. Write (answer a card): answer_ease (1–4, see Ease), time_taken, bury, suspended. Query selection: limit=?, deckID=?.

Media (/media)

file_uri (source Uri of the file), preferred_name (name in collection.media); insert returns the stored file's Uri.

Intent API

Fallback / automation paths that need no permission and work on every AnkiDroid version (from the API wiki):

Intent Details
ACTION_SEND (recommended fallback) Type text/plain; subject = front, text = back. Build with ShareCompat.IntentBuilder so your app name is carried along. One card per send; the user completes the add in AnkiDroid.
org.openintents.action.CREATE_FLASHCARD Deprecated since AnkiDroid 2.5. Extras SOURCE_TEXT (front) and TARGET_TEXT (back). Kept for backwards compatibility only.
com.ichi2.anki.DO_SYNC Triggers a background sync (target type Activity). Throttled: a "server is busy" error appears if fired more often than once per 5 minutes. Works from Tasker/Automate; from a shell: am start -a 'com.ichi2.anki.DO_SYNC'.

Share example:

Intent shareIntent = ShareCompat.IntentBuilder.from(context)
        .setType("text/plain")
        .setSubject("日の出")     // front
        .setText("sunrise")       // back
        .getIntent();
if (shareIntent.resolveActivity(context.getPackageManager()) != null) {
    context.startActivity(shareIntent);
}

Hidden surfaces (found in source)

Not mentioned on the API wiki — recovered from AndroidManifest.xml, IntentHandler.kt and CardContentProvider.kt on main:

  1. Card Browser deep link — an exported activity-alias handles the x-callback-url scheme, including an optional search query:

    am start -a android.intent.action.VIEW \
      -d 'anki://x-callback-url/browser?search=deck%3Adefault'

    (anki://x-callback-url/browser with no search just opens the browser.)

  2. Direct-to-reviewer intent — any intent addressed to the exported com.ichi2.anki.IntentHandler that carries a long extra EXTRA_DECK_ID selects that deck and jumps straight into the reviewer:

    am start -n com.ichi2.anki/com.ichi2.anki.IntentHandler --el EXTRA_DECK_ID 1

    Upstream's own helper IntentHandler.getReviewDeckIntent(context, deckId) builds exactly this intent (used by widgets) — but because the activity is exported, any app can start studying a deck by id.

  3. com.ichi2.anki.COPY_DEBUG_INFO — action handled by IntentHandler with a string extra clip_data: AnkiDroid copies the supplied text to the clipboard and finishes. It has no intent-filter in the manifest, so it only works as an explicit-component intent.

  4. The DO_SYNC throttle is 2 minutes, not 5. The wiki says "once every 5 minutes", but current source defines INTENT_SYNC_MIN_INTERVAL = 2 * 60000 (2 min) in IntentHandler.kt and shows a "try again in N s" style notice inside that window.

  5. No hidden provider RPC. CardContentProvider does not override ContentProvider.call(), so the 18 URIs above (query/insert/update/delete) are the entire provider surface; the provider spec version is advertised via the com.ichi2.anki.provider.spec manifest meta-data.

  6. Internal-only broadcasts. com.ichi2.widget.UPDATE_WIDGET and com.ichi2.widget.ACTION_ROLLOVER exist in the manifest but their receivers are exported="false" — not callable by third-party apps.

  7. Richer implicit-intent matrix than the wiki's ACTION_SEND example. The launcher activity also accepts ACTION_VIEW/ACTION_SEND for application/apkg, application/colpkg, application/vnd.anki, application/x-apkg, x-colpkg, CSV/TSV (and extension-less variants Gmail hides), plain text (shared-text → add-note flow), any image/* (image-occlusion import), and ACTION_PROCESS_TEXT from Android's text-selection menu.

JavaScript API

For deck/template authors: card templates can call native AnkiDroid functions inside the reviewer's WebView via the injected AnkiDroidJS bridge.

Setup and responses

<script>
  // api version 0.0.3 with AnkiDroid 2.18 (0.0.2 with 2.17)
  var jsApiContract = { version: "0.0.3", developer: /* your email */ };
  var api = new AnkiDroidJS(jsApiContract);
  api.ankiGetCardDid().then(r => console.log(r)); // {success: true, value: '1707102058557'}
</script>
  • Every call returns Promise<{ success: boolean, value: any }>.
  • On failure: success: false plus a type-safe default in value — false, -1, "unknown", [], or {}.
  • Failures are normal in the Previewer (e.g. ankiGetETA() is unavailable there).
  • A developer contact must be provided; it is shown to users on breaking changes.

Method list (AnkiDroid 2.18)

Enumerate at runtime with console.log(Object.keys(jsApiList).sort().join("\r\n")).

Group Methods
Answering ankiAnswerEase1..4, ankiShowAnswer, ankiIsDisplayingAnswer
Bury / suspend ankiBuryCard, ankiBuryNote, ankiSuspendCard, ankiSuspendNote
Mark / flag / tags ankiMarkCard, ankiToggleFlag (none/red/orange/green/blue), ankiAddTagToCard, ankiSetNoteTags(tags[])
Scheduling ankiSetCardDue(days), ankiResetProgress, ankiSearchCard, ankiSearchCardWithCallback
Current card info ankiGetCardId, ankiGetCardNid, ankiGetCardDid, ankiGetCardDue, ankiGetCardMod, ankiGetCardFactor, ankiGetCardFlag, ankiGetCardInterval, ankiGetCardLapses, ankiGetCardLeft, ankiGetCardMark, ankiGetCardODid, ankiGetCardODue, ankiGetCardQueue, ankiGetCardReps, ankiGetCardType
Deck / counts ankiGetDeckName, ankiGetNewCardCount, ankiGetLrnCardCount, ankiGetRevCardCount, ankiGetETA, ankiGetNextTime1..4
UI state ankiIsInFullscreen, ankiIsTopbarShown, ankiIsInNightMode, ankiIsActiveNetworkMetered, ankiEnableHorizontalScrollbar, ankiEnableVerticalScrollbar
UI actions ankiShowToast(msg), ankiShowOptionsMenu, ankiShowNavigationDrawer
TTS / STT ankiTtsSpeak, ankiTtsStop, ankiTtsSetLanguage, ankiTtsSetPitch, ankiTtsSetSpeechRate, ankiTtsIsSpeaking, ankiTtsFieldModifierIsAvailable, ankiSttStart, ankiSttStop, ankiSttSetLanguage

Legacy global functions (no api. prefix) also work in templates: showAnswer(), buttonAnswerEase1()…buttonAnswerEase4() (Again/Hard/Good/Easy), ankiShowToast(msg), ankiShowOptionsMenu(), ankiShowNavDrawer().

A desktop-Anki port of most of this surface exists as the third-party AnkiJS-API add-on (not part of either org).

Testing and samples

  • Sample app: ankidroid/apisample — a Japanese→English dictionary prototype covering permission checks, duplicate checks, deck/model id persistence, tags and custom models. Its AnkiDroid_Sync.prf.xml is a ready-made Tasker profile for DO_SYNC.
  • Manual test checklist (wiki): verify adds land in a deck named after your app; toggle Enable AnkiDroid API off and confirm the intent fallback runs; on Android 6+ deny the runtime permission and confirm the fallback, then grant and confirm direct adds; uninstall AnkiDroid and confirm your app hides the option.
  • JS API test deck: ankiweb.net/shared/info/1045118423.
  • Low-level provider implementation: AnkiDroid/src/main/java/com/ichi2/anki/provider.

Related pages: SYNC.md — how AnkiDroid syncs over HTTP · ENDPOINTS.md — the AnkiWeb /svc/* API · ECOSYSTEM.md — map of every Anki API surface.