Python API
The SDK calls the default Rust public API through a native extension. It includes py.typed and checked public signatures. Start with the quickstart.
Project and note values
Section titled “Project and note values”| Entry | Behavior |
|---|---|
Project(namespace, *, default_deck=None) |
Validates the explicit stable namespace and configures the default deck |
project.add(key, note) |
Atomically collects the note, model and owned media; duplicate note keys fail |
project.add_asset(media) |
Includes an explicit raw HTML/CSS/script dependency |
len(project) |
Number of successfully added notes |
Note.basic(front, back) |
Built-in Basic model; strings mean Text |
Note.cloze(text) |
Built-in Cloze model; strings mean Text and retain cloze syntax |
note.field(key, content) |
Returns a new note with the field assignment; key validation occurs on add |
note.deck(name) / .tag(tag) / .tags(tags) |
Returns a configured note |
note.note_type |
The immutable owned model |
Ordinary strings become Text. Content.text(value) and Content.html(value) are explicit constructors; Content.sequence(iterable) composes strings and content without rendering or losing media dependencies. A field does not accept a raw filename as a media reference.
Models and templates
Section titled “Models and templates”NoteType.builder(key) returns an immutable builder. Its .name, .field, .template, .css, .cloze_field and .asset methods return updated builders. .build() validates the complete definition and returns an immutable NoteType. model.note() creates a note owning that model. NoteType.from_bundle(path, limits=MediaLimits()) loads the same model and its assets from the current bundle format; the optional budget applies separately to each asset before reading.
Field(key, name=None, required=False, sort=False) separates the stable key from the display name. Template(key, front, back, name=None, browser_front=None, browser_back=None, target_deck=None, generation=GenerationRule()) uses field keys in every source string. GenerationRule.all(keys) and .any(keys) configure card generation. The default infers Anki’s field-presence condition. A builder’s .cloze_field(key) declares custom cloze generation.
Media and occlusion
Section titled “Media and occlusion”| Entry | Behavior |
|---|---|
Media.file(path, *, limits=MediaLimits()) |
Reads an owned snapshot immediately |
Media.bytes(data, media_type, *, limits=MediaLimits()) |
Owns bytes with an explicit MIME type |
media.with_export_name(name) |
Returns a new filename value sharing the snapshot; previous references retain their names |
media.filename, media.media_type, len(media) |
Read-only snapshot metadata |
media.image() / .sound() |
Typed content that retains the snapshot |
MediaLimits(max_bytes=256 << 20) controls each import before snapshot creation. Large media can spill to owned temporary storage. Naming conflicts are validated across all explicit and automatically collected assets.
Note.image_occlusion(media) returns an ImageOcclusionBuilder. Chain .mask(Mask.rect(key, x, y, width, height)), optionally .mode(OcclusionMode.HIDE_ONE_GUESS_ONE), then .build(). The default mode is HIDE_ALL_GUESS_ONE. Completion decodes the image and validates stable mask keys and finite in-bounds pixel coordinates. On the returned Note, set header, back_extra or comments normally. The generated image and occlusion fields cannot be assigned manually.
Operations and results
Section titled “Operations and results”| Entry | Result |
|---|---|
project.build(BuildOptions.to(path)) |
BuildOutput with a guaranteed persistent artifact |
project.build(BuildOptions.temporary()) |
BuildOutput with an owned temporary artifact |
options.update_from(path) |
Returns options for updating from complete baseline evidence |
project.compare(CompareOptions.against(path)) |
Completed ComparisonReport, including a separate publication policy decision |
options.update_policy(policy) |
Applies explicit risk policy to Update/Compare; invalid for Create |
options.inspect_limits(limits) |
Applies finite limits independently to baseline and candidate |
BuildOutput has .artifact, .report and .snapshot(). BuildReport exposes .counts (BuildCounts with notes/cards/media), .diagnostics, .comparison and .snapshot(). A report has no outcome or file owner. ComparisonReport exposes .findings, .policy, .allows_publication and .snapshot(). Snapshots are ordinary JSON-serializable dictionaries and contain no artifact ownership.
ApkgArtifact exposes .path, .persist_to(path) and .close(). Copying it with copy.copy retains another owner; a context manager releases that owner on exit. Temporary output disappears when its last owner is released. Persistent paths survive handle cleanup. A failed persist keeps the source handle usable.
UpdatePolicy() blocks High and Critical findings. .fail_on(RiskLevel.MEDIUM) changes the threshold. .allow('RISK.NOTE_REMOVED') explicitly accepts the whole registered category, preserving evidence. Unknown codes fail immediately. InspectLimits exposes all finite byte/count budgets, including max_identity_bytes; it cannot disable required inspections.
Errors and concurrency
Section titled “Errors and concurrency”Failures raise SchemaError, AddError, MediaError, ImageOcclusionError, TemplateBundleError, CompareError, PolicyError, BuildError or PersistError. Each exposes kind, code, details, structured observations in source_details, and source-chain text in causes, and retains the native exception as __cause__. I/O causes retain a chained OSError where available. BuildError provides .report and .snapshot(); CompareError provides partial .report; PersistError details include actual publication facts.
Native Project operations release the GIL and reject simultaneous use of the same object with BINDING.PROJECT_BUSY; independent projects can run concurrently. After a process fork, recreate all native values and handles. Inherited handles reject operations with BINDING.FORKED_OBJECT, and their cleanup cannot remove parent-owned files. Child-created values operate and clean up normally. See diagnostics.
Addition context
Section titled “Addition context”Failed additions leave the project unchanged. Structured context records the
original note/model keys and the target, with optional conflict or media usage
details. Rust exposes note::{AddContext, AddTarget, AddDetail} through
AddError::context() / detail(); SDKs expose AddError.details with typed
context/detail. Node fields use camelCase and are deeply frozen; Python uses
snake_case TypedDicts. Tags retain their insertion index; field locations retain
original sequence indices (null/None for the field, [] for its root) and optional
UTF-8 byte ranges. Default-deck errors from build have no note/model keys and
remain available in the source chain.
Typed images require image/* MIME at addition; sound references accept audio/*
or video/*. Other categories raise NOTE.MEDIA_USAGE_INVALID. Constructors stay
infallible; explicit assets and raw HTML are unaffected. The check uses retained
import MIME, and does not certify playback. Build’s independent MIME/extension
validation still applies. See the design for the complete target/detail table.
Batch media and prepared publication
Section titled “Batch media and prepared publication”Batch media and prepared publication are included in the public 0.3.1 package.
Prepare, review and publish
Section titled “Prepare, review and publish”from ankiforge import Project, Note, BuildOptions, BuildErrorproject = Project('review-once').add('one', Note.basic('Question', 'Answer'))previous = project.build(BuildOptions.to('previous.apkg'))previous.artifact.close()prepared = project.prepare_publication( BuildOptions.to('next.apkg').update_from('previous.apkg'))try: report = prepared.report # observations; does not own the candidate if report.comparison is None or report.comparison.allows_publication: output = prepared.publish() output.artifact.close() # persistent next.apkg remainsexcept BuildError as error: print(error.snapshot()['result']) # actual publication/durability facts raisefinally: prepared.close() # idempotent; deletes an unused candidateOne publish attempt consumes the owner, including policy or I/O failure. Later
calls raise PreparedPublicationStateError with code BUILD.PREPARED_UNAVAILABLE
and reason closed or consumed; close does not cancel an already running
publication. Relative paths bind at preparation invocation, and Project changes
cannot modify the reviewed APKG. Reports survive close without retaining files.
Change options or retry by preparing again. Duration excludes review waiting.
Late errors can mean replacement succeeded with unconfirmed durability; inspect
publication facts before deciding what to do. Native binding protocol 1 and
contract bundle 2.1.0 are required; older same-version binaries are rejected.