Install latest from pypi
$ pip install fastankifrom fastanki import *
import osuser = os.environ['ANKI_USER']
passw = os.environ['ANKI_PASS']add_card
lets you create a new card with a single function call. Just pass your
field values as keyword arguments. By default it uses the Basic note
type and Default deck, but you can specify any model, deck, or tags you
like.
notezh = add_card(Front='你好', Back='hello')find_cards
searches your collection and returns a list of Card objects. Pass any
Anki search query as the first argument. Common query patterns: -
deck:Spanish — cards in a specific deck - tag:vocab — cards with a
tag - front:hello — match field content - is:due — cards due for
review - added:7 — added in the last 7 days
Combine with spaces (AND) or OR: deck:Spanish tag:verb finds Spanish
cards tagged “verb”.
cards = find_cards("deck:Default")
cards[Card(1764738198390, nid=1764738198390, due=49, ivl=0, queue=0)]
cards[0]Card 1764738198390 (nid: 1764738198390, due: 49, ivl: 0d, queue: 0)
find_card_ids("deck:Default")[1764738198390]
find_notes
searches your collection and returns a list of Note objects (rather
than Card objects). The query language is the same as
find_cards
— all the same search patterns work. The difference is that
find_notes
returns one result per note, while
find_cards
may return multiple cards if a note generates more than one card (e.g.,
with Cloze or Basic-and-Reversed note types).
notes = find_notes("hello")
notes[Note(1764738198390, Front='你好', Back='hello', tags=[])]
note = notes[0]
noteFront: 你好 | Back: hello
find_note_ids("hello")[1764738198390]
update_note
modifies an existing note’s fields and/or tags. Pass either a Note
object or a note ID, along with any fields you want to change as keyword
arguments. For tags: - tags=['a','b'] — replaces all tags -
add_tags='newtag' — adds without removing existing tags
update_note(note, Back="updated answer", tags='testtag')Front: 你好 | Back: updated answer | 🏷️ testtag
update_note(note, add_tags='moretagz')Front: 你好 | Back: updated answer | 🏷️ testtag, moretagz
get_note(note.id)Front: 你好 | Back: updated answer | 🏷️ moretagz, testtag
del_card([notezh, note])✓ 1 change(s)
sync handles
the entire sync lifecycle for you — opening the collection,
authenticating with AnkiWeb, syncing, and closing up afterwards. The
first time you sync, pass your AnkiWeb credentials; they’ll be saved for
future use.
sync(user=user, passw=passw) # First time
# sync() # after thathost_number: 5
anki_tools()&`[add_fb_card, find_notes, find_note_ids, find_cards, find_card_ids, get_note, del_card, update_fb_note, sync]`
Here are the available tools:
&[add_fb_card, find_notes, find_note_ids, find_cards, find_card_ids, get_note, del_card, update_fb_note, sync].
Try to find all my notes. List the IDs and contents you see.
🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_016SJDA7Ls3sNefL5JdPpe7e",
"call": {
"function": "find_notes",
"arguments": {
"query": "*"
}
},
"result": "[Note(1764724348705, Front='What is the capital of France?', Back='Paris', tags=[]),\n Note(176472434<TRUNCATED>"
}Here’s what I found in your collection:
| ID | Front | Back | Tags |
|---|---|---|---|
| 1764724348705 | What is the capital of France? | Paris | (none) |
| 1764724348737 | What is the capital of France? | Paris | (none) |
You have 2 notes, and interestingly they appear to be duplicates — both have the same question and answer about the capital of France. Would you like to remove one of them, or is there something else you’d like to do with your collection?
Delete them.
🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_011dKzVuBUvYyWEydkYtG1sF",
"call": {
"function": "del_card",
"arguments": {
"notes": "1764724348705"
}
},
"result": "OpChangesWithCount(1)"
}{
"id": "toolu_016yZBTCXdNVpTydhTg8uTQZ",
"call": {
"function": "del_card",
"arguments": {
"notes": "1764724348737"
}
},
"result": "OpChangesWithCount(1)"
}Done! Both notes have been deleted (2 changes total). Your collection should now be empty. Would you like to add some new cards or do something else?
Try finding all notes again.
🧠🧠🧠🧠
{
"id": "toolu_01JVQAUn4vRZZYxwJikGDAbn",
"call": {
"function": "find_notes",
"arguments": {
"query": "*"
}
},
"result": "[]"
}The collection is now empty — no notes found. Ready for a fresh start! What would you like to add?
Try adding a note of your choice using
add_fb_card
and tell me the id.
🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_01EbRahwqio96vUq7AtMr2uU",
"call": {
"function": "add_fb_card",
"arguments": {
"front": "What is the speed of light in a vacuum?",
"back": "Approximately 299,792,458 meters per second (about 3 \u00d7 10\u2078 m/s)"
}
},
"result": "1764727897520"
}Done! I added a physics flashcard about the speed of light. The note ID is 1764727897520.
Would you like to add more cards, or do something else with this one?
Try finding all notes again.
🧠🧠🧠🧠🧠
{
"id": "toolu_01RpBYttNcGRr15YRP7HBhuy",
"call": {
"function": "find_notes",
"arguments": {
"query": "*"
}
},
"result": "[Note(1764727897520, Front='What is the speed of light in a vacuum?', Back='Approximately 299,792,45<TRUNCATED>"
}There’s now 1 note in your collection:
| ID | Front | Back | Tags |
|---|---|---|---|
| 1764727897520 | What is the speed of light in a vacuum? | Approximately 299,792,458 meters per second (about 3 × 10⁸ m/s) | (none) |
What would you like to do next?
OK try
get_note
with it.
🧠🧠🧠🧠🧠🧠
{
"id": "toolu_01Qy2LvuJSdxr421DrcdLq8y",
"call": {
"function": "get_note",
"arguments": {
"note_id": "1764727897520"
}
},
"result": "Note(1764727897520, Front='What is the speed of light in a vacuum?', Back='Approximately 299,792,458<TRUNCATED>"
}It works! The note comes back as a Note object with:
- ID: 1764727897520
- Front: What is the speed of light in a vacuum?
- Back: Approximately 299,792,458 meters per second (about 3 × 10⁸ m/s)
- Tags: [] (empty)
The serialization looks clean — the __repr__ method you defined
earlier is being used. Anything else you’d like to test?
Delete it now.
🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_01HE56VzDm5pAydPYQS3pPRg",
"call": {
"function": "del_card",
"arguments": {
"notes": "1764727897520"
}
},
"result": "OpChangesWithCount(1)"
}Done! The note has been deleted. Your collection is empty again.
OK create, update, and verify a note now.
🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_018fiP5JTuM8nCVfW46os4Qu",
"call": {
"function": "add_fb_card",
"arguments": {
"front": "What is H\u2082O?",
"back": "Water"
}
},
"result": "1764731543071"
}{
"id": "toolu_0153ANMUNw3HQ5AdZzkxu6Mj",
"call": {
"function": "update_fb_note",
"arguments": {
"note_id": "1764731543071",
"back": "Water (the chemical formula for water)",
"add_tags": "chemistry"
}
},
"result": "Note(1764731543071, Front='What is H\u2082O?', Back='Water (the chemical formula for water)', tags=['chem<TRUNCATED>"
}{
"id": "toolu_017TbPKpAPK6AJmSswhDDRhA",
"call": {
"function": "get_note",
"arguments": {
"note_id": "1764731543071"
}
},
"result": "Note(1764731543071, Front='What is H\u2082O?', Back='Water (the chemical formula for water)', tags=['chem<TRUNCATED>"
}All working! Here’s what happened:
- Added a card with Front=“What is H₂O?” and Back=“Water”
- Updated the back to “Water (the chemical formula for water)” and added the tag “chemistry”
- Verified with
get_note— the changes persisted
The note ID is 1764731543071. What would you like to test next?
Try the various find ones that we haven’t done yet.
🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠
{
"id": "toolu_016JBXpr9shwyawqdBqf7diZ",
"call": {
"function": "find_note_ids",
"arguments": {
"query": "*"
}
},
"result": "[1764731543071]"
}{
"id": "toolu_019mDgnKk6ifAinw6ej84LmG",
"call": {
"function": "find_cards",
"arguments": {
"query": "*"
}
},
"result": "[Card(1764731543071, nid=1764731543071, due=47, ivl=0, queue=0)]"
}{
"id": "toolu_01BBTUkQNTZfCgU2ehbFxDAU",
"call": {
"function": "find_card_ids",
"arguments": {
"query": "*"
}
},
"result": "[1764731543071]"
}All three work! Here’s the comparison:
| Function | Returns |
|---|---|
find_note_ids("*") |
[1764731543071] — just the note ID |
find_note_ids gives just IDs (lightweight) |
|
find_cards("*") |
Card(1764731543071, nid=1764731543071, due=47, ivl=0, queue=0) — full Card object with scheduling info |
find_card_ids("*") |
[1764731543071] — just the card ID |
Interesting that in this case the note ID and card ID happen to be the same (1764731543071). That’s because with a Basic note type, there’s exactly one card per note.
That just leaves
sync —
want me to try that too?
Sure. Delete that note, then sync.
🧠🧠🧠🧠🧠🧠
{
"id": "toolu_013cYoVSkSeF2iUxFoRsFqWb",
"call": {
"function": "del_card",
"arguments": {
"notes": "1764731543071"
}
},
"result": "OpChangesWithCount(1)"
}{
"id": "toolu_01Gx7MqumvhrTBiq92jdHytr",
"call": {
"function": "sync",
"arguments": {}
},
"result": "host_number: 5"
}Done! Note deleted and synced to AnkiWeb. All tools are working. 🎉
data_path()Path('/app/data/.local/share/Anki2')
profiles()['User 1']
col = Collection.open()
col.profile_pathPath('/app/data/.local/share/Anki2/User 1')
# First sync requires credentials
col.sync(user=user, passw=passw)
# Subsequent syncs use saved auth
col.sync()col.models- Basic
- Basic (and reversed card)
- Basic (optional reversed card)
- Basic (type in the answer)
- Cloze
- Image Occlusion
col.sched.deck_due_tree().children[0]Default: 2 new, 0 learn, 0 review
col.decks- Default
mdl = col.models['Basic']
mdl['id']1764648511131
note = col.new_note(mdl)
note['Front'], note['Back'] = 'hola', 'hello'
noteFront: hola | Back: hello
note2 = col.add(Front='hola', Back='hello', tags=['spanish'])col.add_deck('Spanish::Vocab')changes {
deck: true
browser_table: true
browser_sidebar: true
study_queues: true
mtime: true
}
id: 1764727764420
deck = col.decks['Spanish::Vocab']
deck.add(Front='adiós', Back='goodbye')
deck.dueVocab: 1 new, 0 learn, 0 review
col.remove_notes([note2.id])✓ 1 change(s)
col.decks.remove([col.decks.id_for_name('Spanish')])✓ 1 change(s)
col.sync()host_number: 5
col.close()