AnkiWeb's sync protocol isn't documented anywhere public. The only spec is Anki's source code — a mix of Rust and Python that's evolved over years. When I set out to build ankiweb-cli, a headless Rust CLI for adding cards and backing up collections without the desktop app, I had to piece the protocol together from the codebase. The core of it is surprisingly straightforward: authenticate with hostKey (username + password → session key), call meta to check sync state, then either do a full upload/download or an incremental normal sync. Every request goes to https://sync.ankiweb.net/ as an HTTP POST with a JSON + zstd-compressed body and a small header encoding the sync version, session key, and client identifier. The server responds in the same format.
The tricky part is the normal (incremental) sync — the flow that lets you push a single new card without clobbering the whole collection. It's a seven-step dance: meta → start → applyGraves → applyChanges → chunk/applyChunk → sanityCheck2 → finish. You send your local deletions, then your changed models/decks/tags, then chunked batches of new notes/cards/revlog, and the server sends back its side of each. Getting the USN (update sequence number) bookkeeping right is the fiddly bit — every new object needs usn=-1 so the server assigns the real one. The minimal viable implementation ended up around 700 lines of Rust. Not bad for a protocol with zero documentation. The full source is on GitHub if you want to see how it works.