Creating and editing *.apkg and *.anki2 safely

anki, srs, memorization, spaced-repetition, anki-flashcards
pip install ankisync2==


AnkiSync 2

PyPI version PyPI license

*.apkg and *.anki2 file structure is very simple, but with some quirks of incompleteness.

*.apkg file structure is a zip of at least two files.

├── example
│   ├── example.anki2
│   ├── media
│   ├── 1  # Media files with the names masked as integers
│   ├── 2
│   ├── 3
|   └── ...
└── example.apkg

*.anki2 is a SQLite file with foreign key disabled, and the usage of some JSON schemas instead of some tables

Also, *.anki2 is used internally at os.path.join(appdirs.user_data_dir('Anki2'), 'User 1', 'collection.anki2'), so editing the SQLite there will also edit the database.

The media file is a text file of at least a string of {}, which is actually a dictionary of keys -- stringified int; and values -- filenames.


Some extra tables are created if not exists.

from ankisync2.anki import Anki2, Apkg
apkg = Apkg("example.apkg")  # Or Apkg("example/") also works, otherwise the folder named 'example' will be created.
apkg.db.execute_sql(SQL, PARAMS)"example1.apkg")

I also support adding media.


To find the wanted cards and media, iterate though the Apkg and Apkg.iter_media object.

iter_apkg = iter(apkg)
for i in range(5):

Creating a new *.apkg

You can create a new *.apkg via Apkg with any custom filename (and *.anki2 via Anki2()). A folder required to create *.apkg needs to be created first.

from ankisync2.anki import Apkg
apkg = Apkg("example")  # Create example folder

After that, the Apkg will require at least 1 card, which is connected to at least 1 note, 1 model, 1 template, and 1 deck; which should be created in this order.

  1. Model, Deck
  2. Template, Note
  3. Card
from ankisync2 import db
m = db.Models.create(name="foo", flds=["field1", "field2"])
d = db.Decks.create(name="bar::baz")
t = [
    db.Templates.create(name="fwd",, qfmt="{{field1}}", afmt="{{field2}}"),
    db.Templates.create(name="bwd",, qfmt="{{field2}}", afmt="{{field1}}")   
n = db.Notes.create(, flds=["data1", "<img src='media.jpg'>"], tags=["tag1", "tag2"])
c = [
    db.Cards.create(,, ord=i)
    for i, _ in enumerate(t)

You can also add media, which is not related to the SQLite database.


Finally, finalize with"example1.apkg")

Updating an *.apkg

This is also possible, by modifying as sqlite_ext.JSONField, with peewee.signals.

It is now as simple as,

from ankisync2.anki import Apkg
from ankisync2 import db

apkg = Apkg("example1.apkg")

for n in db.Notes.filter(["field1"] == "data1"):["field3"] = "data2"


JSON schema of Col.models, Col.decks, Col.conf and Col.dconf

I have created dataclasses for this at /ankisync2/ To serialize it, use dataclasses.asdict or

from ankisync2.util import DataclassJSONEncoder
import json
json.dumps(dataclassObject, cls=DataclassJSONEncoder)

For an example of how this works, please see /ankisync2/

Using peewee framework

You can also use peewee ORM framework; and ArrayField, X1fField and JSONField will be automated. You can use Dataclasses and Lists directly, without converting them to string first.


Please see /example.ipynb.


pip install ankisync2

Related projects