# Get Media Item Progress Get the stored reading progress for a media item, plus the server-derived restore handles for reflowable books. **Endpoint**: `GET /api/media-items/:id/progress` **Auth**: Required (Bearer token) See [Position Contract](position-contract.md) for the semantics of every field — what is verified, what the currencies are, and how clients should restore. ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------------- | -------- | --------------- | | id | string (UUID) | Yes | Media item UUID | ### Example Request ```http GET /api/media-items/774641f9-317b-4087-8e04-53bb4392ae56/progress Authorization: Bearer eyJhbGciOiJIUzI1NiIs... ``` ## Response (200 OK) — no progress stored When the item has no progress row, an empty fixed-layout-style stub is returned (not 404): ```json { "current_page": 0, "total_pages": null } ``` ## Response (200 OK) — progress stored ```json { "id": "e8239659-0ed6-42ae-a946-8440d7655b42", "media_item_id": "774641f9-317b-4087-8e04-53bb4392ae56", "user_id": "1b64992e-3408-4d84-9e03-e2dc4950e1dd", "current_page": null, "total_pages": null, "last_read_at": "2026-09-26T14:52:34.806446Z", "percentage": 0.045, "character_offset": 16375, "epubcfi": "epubcfi(/6/4!/4/8[_idContainer003]/58/1:598)", "chapter": null, "chapter_progress": null, "format_group": "reflowable", "total_characters": 592216, "chapter_count": 1, "last_sync_device": "web", "last_sync_source": "koreader", "last_sync_timestamp": "2026-09-26T14:52:34.806446Z", "css_selector": "body>div:nth-child(4)>p:nth-child(29)", "anchor_href": "1984.xhtml", "char_offset": 598, "context_text": "from day to day, but there was none in which Goldstein was not the principal figure. He was the prim" } ``` ### Field Reference Base fields (always present when a row exists): | Field | Type | Description | | ----- | ---- | ----------- | | `percentage` | float | Position as a fraction of the whole book (0..1). | | `epubcfi` | string | The stored canonical standard CFI. **Spine steps index the OPF spine as written, including `linear="no"` items — do not resolve them against a readium reading order.** See the [Position Contract](position-contract.md#spine-numbering-hazard--read-this-before-parsing-a-stored-cfi). | | `context_text` | string | The stored verification context (≤100 whitespace-normalized chars from the anchor). | | `character_offset` | int | Book-wide rune offset. Internal currency — consistent with `total_characters`. | | `current_page`, `total_pages` | int | Fixed-layout page position (null for reflowable). | | `chapter`, `chapter_progress` | int, float | Chapter index and within-chapter fraction, when known. | | `format_group` | string | `reflowable`, `fixed_layout`, `comic_archive`, … Gates the restore handles. | | `total_characters`, `chapter_count` | int | Book metrics, for client-side fraction math. | | `last_sync_device`, `last_sync_source`, `last_sync_timestamp` | — | Which client last wrote the row. | Restore handles (conditional — served only for convertible reflowable books whose stored anchor re-resolves at GET time): | Field | Type | Description | | ----- | ---- | ----------- | | `anchor_href` | string | The spine document containing the anchor (e.g. `1984.xhtml`). **Resolve your resource by this**, never by the CFI's spine step. | | `css_selector` | string | Body-relative chain to the anchor's block element. | | `char_offset` | int | Anchor offset within the block's concatenated text, in UTF-16 code units. | | `epubcfi` | string | Re-served (possibly healed) canonical CFI — present whenever re-verification produced one. | ## Error Responses | Code | Description | | ---- | ----------- | | 400 | Invalid media item id | | 401 | Invalid or expired token | | 500 | Database error |