Wiki SDK¶
Examples use a client built as wiki = WikiClient(oauth_token="…", organization_id="…").
WikiClient ¶
WikiClient(*, oauth_token: str, organization_id: str, http: HTTPConfig | None = None, transport: BaseTransport | None = None, before_send: BeforeSend | None = None)
Bases: DomainClient
Holds the per-resource wiki clients, all sharing one httpx2 core session.
Examples:
>>> wiki.me.get().username
'vera.petrova'
me¶
MeClient ¶
MeClient(*, session: SyncSession)
Bases: Resource
The authenticated Wiki user.
get ¶
get() -> Me
GET /users/me → the authenticated Me (a safe auth probe).
Returns:
| Type | Description |
|---|---|
Me
|
The authenticated user. |
Examples:
>>> wiki.me.get().username
'vera.petrova'
pages¶
PagesClient ¶
PagesClient(*, session: SyncSession)
Bases: Resource
/pages: get, descendants, grids, create, update, delete, append, clone, move.
move, revisions and backlinks call operations Yandex does not document (they are
in the live OpenAPI only), so their contract may change without notice.
get_by_id ¶
get_by_id(page_id: int, fields: str | None = None, *, revision_id: int | None = None, raise_on_redirect: bool = False) -> PageDetails
GET /pages/{id}?fields= → a single page by numeric id (raises on non-2xx).
The slug-addressed sibling is :meth:get; use this when you hold the numeric id
(e.g. from a descendants listing). fields is the same comma-separated selector
(content, attributes, breadcrumbs, …); omit it for id/slug/title only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's numeric id. |
required |
fields
|
str | None
|
The comma-separated blocks to include. |
None
|
revision_id
|
int | None
|
The past revision to show, from :meth: |
None
|
raise_on_redirect
|
bool
|
Answer with an error when the page is a redirect, instead of the page it leads to. |
False
|
Returns:
| Type | Description |
|---|---|
PageDetails
|
The page. |
Examples:
>>> wiki.pages.get_by_id(4101, fields="content,breadcrumbs").content
'# Arch'
get ¶
get(slug: str, fields: str | None = None, *, revision_id: int | None = None, raise_on_redirect: bool = False) -> PageDetails
GET /pages?slug=&fields= → a single page (raises on non-2xx).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
slug
|
str
|
The page's slug. |
required |
fields
|
str | None
|
The comma-separated blocks to include. |
None
|
revision_id
|
int | None
|
The past revision to show, from :meth: |
None
|
raise_on_redirect
|
bool
|
Answer with an error when the page is a redirect, instead of the page it leads to. |
False
|
Returns:
| Type | Description |
|---|---|
PageDetails
|
The page. |
Examples:
>>> wiki.pages.get("team/handbook", fields="content,attributes").content
'# Handbook'
descendants ¶
descendants(slug: str, *, limit: int | None = None, actuality: str | None = None, include_self: bool = False, show_all: bool = False) -> ItemList[PageRef]
All descendant refs under slug, draining next_cursor internally.
Capped at limit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
slug
|
str
|
The ancestor page's slug. |
required |
limit
|
int | None
|
The most refs to return; |
None
|
actuality
|
str | None
|
The page state to list. |
None
|
include_self
|
bool
|
Also return the ancestor page itself. |
False
|
show_all
|
bool
|
The API's flag of that name. |
False
|
Returns:
| Type | Description |
|---|---|
ItemList[PageRef]
|
The descendants' refs. |
Examples:
>>> [ref.slug for ref in wiki.pages.descendants("eng", limit=40).root]
['eng/a', 'eng/b']
descendants_by_id ¶
descendants_by_id(page_id: int, *, limit: int | None = None, actuality: str | None = None, include_self: bool = False, show_all: bool = False) -> ItemList[PageRef]
All descendant refs under numeric page_id, draining next_cursor internally.
The numeric-id twin of :meth:descendants; capped at limit (None = every ref).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The ancestor page's numeric id. |
required |
limit
|
int | None
|
The most refs to return; |
None
|
actuality
|
str | None
|
The page state to list. |
None
|
include_self
|
bool
|
Also return the ancestor page itself. |
False
|
show_all
|
bool
|
The API's flag of that name. |
False
|
Returns:
| Type | Description |
|---|---|
ItemList[PageRef]
|
The descendants' refs. |
Examples:
>>> [ref.slug for ref in wiki.pages.descendants_by_id(4210, limit=35).root]
['sales/a', 'sales/b']
grids ¶
grids(page_id: int, *, limit: int | None = None, order_by: str | None = None, order_direction: str | None = None) -> ItemList[GridRef]
GET /pages/{id}/grids → flat ItemList[GridRef], draining next_cursor.
Dynamic tables (grids) attached to the page. Capped at limit (None = every
grid); order_by sorts the server-side listing (title or created_at).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
limit
|
int | None
|
The most grids to return; |
None
|
order_by
|
str | None
|
The sort field: |
None
|
order_direction
|
str | None
|
The sort direction for |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[GridRef]
|
The page's grids. |
Examples:
>>> [grid.title for grid in wiki.pages.grids(4301, limit=30).root]
['Roadmap', 'Budget']
create ¶
create(body: dict[str, Any], *, fields: str | None = None, is_silent: bool = False) -> PageDetails
POST /pages — create. body carries content/title/slug.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
dict[str, Any]
|
The new page: |
required |
fields
|
str | None
|
The comma-separated blocks to include in the reply. |
None
|
is_silent
|
bool
|
Do not notify the page's subscribers. |
False
|
Returns:
| Type | Description |
|---|---|
PageDetails
|
The created page. |
Examples:
>>> body = {"slug": "eng/new", "title": "New page", "content": "# New"}
>>> wiki.pages.create(body).id
4401
update ¶
update(page_id: int, body: dict[str, Any], *, fields: str | None = None, is_silent: bool = False, allow_merge: bool = False) -> PageDetails
POST /pages/{id} — update (POST not PATCH; PATCH returns 405).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
body
|
dict[str, Any]
|
The fields to change. |
required |
fields
|
str | None
|
The comma-separated blocks to include in the reply. |
None
|
is_silent
|
bool
|
Do not notify the page's subscribers. |
False
|
allow_merge
|
bool
|
Merge with a concurrent edit (3-way merge) instead of failing on the conflict. |
False
|
Returns:
| Type | Description |
|---|---|
PageDetails
|
The updated page. |
Examples:
>>> wiki.pages.update(4403, {"content": "# Body only"}).id
4403
delete ¶
delete(page_id: int, *, recursive: bool = False) -> PageDeleteResult
DELETE /pages/{id} → {recovery_token}; keep the token to restore (undo).
The returned :class:PageDeleteResult carries the recovery_token — the only handle
to undo this delete, via RecoveryClient.restore (POST /recovery_tokens/{token}/recover).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
recursive
|
bool
|
Also delete every page under it. |
False
|
Returns:
| Type | Description |
|---|---|
PageDeleteResult
|
The result, carrying the deleted page's |
Examples:
>>> wiki.pages.delete(4501).recovery_token
'recovery-token-2'
append_content ¶
append_content(page_id: int, body: dict[str, Any], *, fields: str | None = None, is_silent: bool = False) -> PageDetails
POST /pages/{id}/append-content — append YFM without rewriting the whole body.
body is a dumped :class:PageAppendContent ({content, body?, section?, anchor?}).
Unlike :meth:update (which replaces the body), this adds to it; body.location /
section / anchor pinpoint where. Returns the updated :class:PageDetails.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
body
|
dict[str, Any]
|
The YFM to append and where to put it. |
required |
fields
|
str | None
|
The comma-separated blocks to include in the reply. |
None
|
is_silent
|
bool
|
Do not notify the page's subscribers. |
False
|
Returns:
| Type | Description |
|---|---|
PageDetails
|
The updated page. |
Examples:
>>> body = {"content": "## Footer", "body": {"location": "bottom"}}
>>> wiki.pages.append_content(4602, body).slug
'eng/footer'
clone ¶
clone(page_id: int, body: dict[str, Any]) -> AsyncOperation
POST /pages/{id}/clone — copy the page to a new address (async trigger).
Returns a :class:AsyncOperation; poll its operation.id via
OperationsClient.clone_get until terminal. body is a dumped :class:PageClone
({target, title?, subscribe_me}).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
body
|
dict[str, Any]
|
The target address, an optional title and whether to subscribe the caller. |
required |
Returns:
| Type | Description |
|---|---|
AsyncOperation
|
The clone operation to poll. |
Examples:
>>> body = {"target": "eng/copy", "title": "Copy", "subscribe_me": True}
>>> wiki.pages.clone(4701, body).operation.id
'task-4701'
move ¶
move(body: dict[str, Any], *, dry_run: bool = False) -> AsyncOperation
POST /pages/move — give pages new addresses (async; undocumented, may change).
The only way to rename or relocate a page: a page update has no slug. Returns a
:class:AsyncOperation; poll its operation.id via OperationsClient.move_get
until terminal. body is a dumped :class:PageMove
({operations: [{source, target, next_to_slug?, position?}], copy_inherited_access};
the API answers 400 unless copy_inherited_access is a boolean). A page moves with its
subtree. dry_run=True validates the request without applying it, and the task id it
returns answers 404 when polled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
dict[str, Any]
|
The moves and |
required |
dry_run
|
bool
|
Validate the request without applying it. |
False
|
Returns:
| Type | Description |
|---|---|
AsyncOperation
|
The move operation to poll. |
Examples:
>>> body = {
... "operations": [{"source": "eng/b", "target": "eng/c"}],
... "copy_inherited_access": False,
... }
>>> wiki.pages.move(body, dry_run=True).operation.id
'mv-6101'
revisions ¶
revisions(page_id: int, *, ids: str | None = None, limit: int | None = None) -> ItemList[PageRevision]
GET /pages/{id}/revisions → ItemList[PageRevision], draining next_cursor.
Undocumented by Yandex (live OpenAPI only), may change. A revision id is what
GET /pages takes as revision_id. ids keeps only these revisions (comma
separated); capped at limit (None = every revision).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
ids
|
str | None
|
The comma-separated revision ids to keep. |
None
|
limit
|
int | None
|
The most revisions to return; |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[PageRevision]
|
The page's revisions. |
Examples:
>>> revisions = wiki.pages.revisions(6201, ids="7002,7003", limit=40)
>>> [revision.id for revision in revisions.root]
[7003, 7002]
backlinks ¶
backlinks(page_id: int, *, for_cluster: bool = False, show_all: bool = False, limit: int | None = None) -> ItemList[PageRef]
GET /pages/{id}/backlinks → refs of the pages that link here, draining the cursor.
Undocumented by Yandex (live OpenAPI only), may change. for_cluster also reports links
to the page's descendants. show_all is the API's flag of that name (no effect showed in
a live check). The index lags a few seconds behind an edit. Capped at limit
(None = every ref).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
for_cluster
|
bool
|
Also report links to the page's descendants. |
False
|
show_all
|
bool
|
The API's flag of that name. |
False
|
limit
|
int | None
|
The most refs to return; |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[PageRef]
|
The refs of the pages that link here. |
Examples:
>>> refs = wiki.pages.backlinks(6301, for_cluster=True, show_all=True, limit=30)
>>> [ref.slug for ref in refs.root]
['eng/linker-a', 'eng/linker-b']
access¶
AccessClient ¶
AccessClient(*, session: SyncSession)
Bases: Resource
/pages/{id}/access: grant, change, revoke and clear a page's personal accesses.
Read them back with pages.get_by_id(page_id, fields="access_policy,access_lists").
prevent_selflock=True makes the API refuse a change that would leave the caller without
read access or the right to change accesses; the page owner's own grant cannot be changed
or revoked either way.
create ¶
create(page_id: int, body: dict[str, Any]) -> PageAccess
POST /pages/{id}/access — grant a user or a group a role; returns the grant.
body is a dumped :class:PageAccessCreate (user or group, role, optional
inheritance). Granting a user who already holds a personal access is refused; use
:meth:update instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
body
|
dict[str, Any]
|
The grant: |
required |
Returns:
| Type | Description |
|---|---|
PageAccess
|
The created grant. |
Examples:
>>> body = {"user": {"uid": "9001"}, "role": "editor"}
>>> wiki.access.create(6001, body).id
'5001'
update ¶
update(page_id: int, access_id: str, body: dict[str, Any], *, prevent_selflock: bool = False) -> PageAccess
POST /pages/{id}/access/{access_id} — change a grant's role or reach.
body is a dumped :class:PageAccessUpdate (role and/or inheritance).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
access_id
|
str
|
The grant's id. |
required |
body
|
dict[str, Any]
|
The fields to change: |
required |
prevent_selflock
|
bool
|
Refuse a change that would lock the caller out of the page. |
False
|
Returns:
| Type | Description |
|---|---|
PageAccess
|
The updated grant. |
Examples:
>>> wiki.access.update(
... 6003, "5003", {"role": "extra_editor"}, prevent_selflock=True
... ).role
'extra_editor'
delete ¶
delete(page_id: int, access_id: str, *, prevent_selflock: bool = False) -> None
DELETE /pages/{id}/access/{access_id} — revoke one personal access (204).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
access_id
|
str
|
The grant's id. |
required |
prevent_selflock
|
bool
|
Refuse a change that would lock the caller out of the page. |
False
|
Examples:
>>> wiki.access.delete(6005, "5005", prevent_selflock=True)
clear ¶
clear(page_id: int, *, prevent_selflock: bool = False) -> None
DELETE /pages/{id}/access — revoke every personal access but the owner's (204).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
prevent_selflock
|
bool
|
Refuse a change that would lock the caller out of the page. |
False
|
Examples:
>>> wiki.access.clear(6007, prevent_selflock=True)
comments¶
CommentsClient ¶
CommentsClient(*, session: SyncSession)
Bases: Resource
/pages/{id}/comments: list, create, delete; thread rebuilds a thread client-side.
list ¶
list(page_id: int, *, limit: int | None = None, order_by: str | None = None, order_direction: str | None = None, status_filter: str | None = None) -> ItemList[Comment]
GET /pages/{id}/comments → flat ItemList[Comment], draining next_cursor.
Capped at limit (None = every comment).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
limit
|
int | None
|
The most comments to return; |
None
|
order_by
|
str | None
|
The sort field; the API accepts |
None
|
order_direction
|
str | None
|
The sort direction for |
None
|
status_filter
|
str | None
|
Keep only |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[Comment]
|
The page's comments. |
Examples:
>>> [comment.author for comment in wiki.comments.list(5501, limit=45).root]
['Vera', 'Ivan']
thread ¶
thread(page_id: int, comment_id: int, *, limit: int | None = None) -> ItemList[Comment]
The comment comment_id followed by its replies, reconstructed from comments list.
The Wiki /comments/{id}/thread endpoint (:meth:thread_get) returns
{"results": []} for real parent/child pairs, and the flat listing returns a reply as a
sibling of its parent (tagged only by parent_id; thread_id / thread_info are
null). So this fetches every comment on the page and rebuilds the thread client-side
by chaining parent_id from the target to any depth. Returns a flat
ItemList[Comment] — the target comment first, then its descendants in depth-first order
(each carrying the parent_id that wires it to its parent) — or an empty list if
comment_id is not found. limit caps the replies collected (None = every reply).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
comment_id
|
int
|
The id of the thread's first comment. |
required |
limit
|
int | None
|
The most replies to collect; |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[Comment]
|
The comment and its replies. |
Examples:
>>> [comment.content for comment in wiki.comments.thread(5503, 5511, limit=15).root]
['Ship it?', 'Agreed']
thread_get ¶
thread_get(page_id: int, comment_id: int, *, limit: int | None = None) -> ItemList[Comment]
GET /pages/{id}/comments/{comment_id}/thread → what the server calls the thread.
Checked live on 2026-10-02: the endpoint answers {"results": []} for a root comment
and for its replies, plain or inline, so this returns an empty list for every real
thread. Use :meth:thread, which rebuilds the thread from :meth:list; this raw call
stays for the day the server fills it in. Capped at limit (None = everything).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
comment_id
|
int
|
The comment's id. |
required |
limit
|
int | None
|
The most comments to return; |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[Comment]
|
The thread's comments. |
Examples:
>>> wiki.comments.thread_get(5508, 5512).root
[]
create ¶
create(page_id: int, body: dict[str, Any]) -> CommentCreated
POST /pages/{id}/comments — add a comment; returns a :class:CommentCreated.
body is a dumped :class:CommentCreate (body + optional
inline_text / parent_id / thread_id).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
body
|
dict[str, Any]
|
The comment: |
required |
Returns:
| Type | Description |
|---|---|
CommentCreated
|
The created comment. |
Examples:
>>> wiki.comments.create(5505, {"body": "Plain note"}).id
5515
delete ¶
delete(page_id: int, comment_id: int) -> CommentDeleteResult
DELETE /pages/{id}/comments/{comment_id} → {comments_count} left on the page.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
comment_id
|
int
|
The comment's id. |
required |
Returns:
| Type | Description |
|---|---|
CommentDeleteResult
|
The result, whose |
Examples:
>>> wiki.comments.delete(5506, 5516).comments_count
4
attachments¶
AttachmentsClient ¶
AttachmentsClient(*, session: SyncSession)
Bases: Resource
/pages/{id}/attachments: list, get, attach, upload, delete, download and preview.
get and preview call operations Yandex does not document (they are in the live OpenAPI
only), so their contract may change without notice.
list ¶
list(page_id: int, *, limit: int | None = None, order_by: str | None = None, order_direction: str | None = None) -> ItemList[Attachment]
GET /pages/{id}/attachments → ItemList[Attachment], draining next_cursor.
Capped at limit (None = every attachment).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
limit
|
int | None
|
The most attachments to return; |
None
|
order_by
|
str | None
|
The sort field: |
None
|
order_direction
|
str | None
|
The sort direction for |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[Attachment]
|
The page's attachments. |
Examples:
>>> [file.name for file in wiki.attachments.list(5601, limit=20).root]
['spec.pdf', 'logo.png']
get ¶
get(page_id: int, file_id: int) -> AttachedFile
GET /pages/{id}/attachments/{file_id} → one attachment's metadata.
Undocumented by Yandex (live OpenAPI only), may change. The same descriptor attach
returns: name, size, MIME type, download URL, preview flag and virus-check status.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
file_id
|
int
|
The attachment's id. |
required |
Returns:
| Type | Description |
|---|---|
AttachedFile
|
The attachment's metadata. |
Examples:
>>> wiki.attachments.get(5607, 5621).mimetype
'image/png'
preview ¶
preview(page_id: int, file_id: int) -> bytes
GET /pages/{id}/attachments/{file_id}/preview → the preview image's raw bytes.
Undocumented by Yandex (live OpenAPI only), may change. The bytes are returned as sent.
For a file with no preview (has_preview is false: not an image, say) the API sends
200 image/png with the base64 text of a 1-pixel PNG instead of the PNG itself.
Binary payload — SDK/CLI only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
file_id
|
int
|
The attachment's id. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
The preview image's bytes. |
Examples:
>>> wiki.attachments.preview(5608, 5622)
b'\x89PNG preview bytes'
download ¶
download(page_id: int, file_id: int) -> bytes
GET /pages/{id}/attachments/{file_id}/download → the file's raw bytes.
Binary payload — SDK/CLI only (never MCP: base64 blobs are not an agent payload).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
file_id
|
int
|
The attachment's id. |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
The file's bytes. |
Examples:
>>> wiki.attachments.download(5603, 5613)
b'%PDF-1.7 spec'
download_by_url ¶
download_by_url(url: str) -> bytes
GET /pages/attachments/download_by_url?url= → the file's raw bytes.
Addresses a file by the <page-slug>/.files/<filename> URL instead of its numeric
id; follows page redirects server-side. Binary payload — SDK/CLI only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The file's |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
The file's bytes. |
Examples:
>>> wiki.attachments.download_by_url("eng/specs/.files/spec.pdf")
b'%PDF-1.7 by url'
delete ¶
delete(page_id: int, file_id: int) -> None
DELETE /pages/{id}/attachments/{file_id} — remove an attachment (204, no body).
Returns None on success; raises a typed YandexError on any non-2xx.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
file_id
|
int
|
The attachment's id. |
required |
Examples:
>>> wiki.attachments.delete(5604, 5614)
attach ¶
attach(page_id: int, session_ids: Sequence[str]) -> ItemList[AttachedFile]
POST /pages/{id}/attachments — attach file(s) from finished upload sessions.
session_ids are the session_id of each finished upload session (see
:class:UploadSessionsClient). Returns the flat list of newly-attached files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
session_ids
|
Sequence[str]
|
The ids of the finished upload sessions to attach. |
required |
Returns:
| Type | Description |
|---|---|
ItemList[AttachedFile]
|
The newly-attached files. |
Examples:
>>> wiki.attachments.attach(5605, ["s-5605-a", "s-5605-b"]).root[1].name
'b.png'
upload ¶
upload(sessions: UploadSessionsClient, page_id: int, *, file_name: str, data: bytes) -> ItemList[AttachedFile]
Run the whole upload pipeline for one file, then attach it to page_id.
Drives the four steps end to end against the injected sessions client: open a
session sized to data, PUT the bytes as a single octet-stream part, finish the
session, then attach the finished session to the page. Small-file path — the bytes
go up as one part_number=1 part (chunk large files with upload_part directly).
Returns the flat list of newly-attached files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sessions
|
UploadSessionsClient
|
The upload-sessions client that opens, fills and finishes the session. |
required |
page_id
|
int
|
The page's id. |
required |
file_name
|
str
|
The name the attachment gets. |
required |
data
|
bytes
|
The file's bytes. |
required |
Returns:
| Type | Description |
|---|---|
ItemList[AttachedFile]
|
The newly-attached files. |
Examples:
>>> wiki.attachments.upload(
... wiki.uploadsessions, 5606, file_name="diagram.txt", data=b"hello"
... ).root[0].name
'diagram.txt'
resources¶
ResourcesClient ¶
ResourcesClient(*, session: SyncSession)
Bases: Resource
The unified listing of a page's attachments and grids.
list ¶
list(page_id: int, *, limit: int | None = None, q: str | None = None, types: str | None = None, order_by: str | None = None, order_direction: str | None = None) -> ItemList[ResourceItem]
GET /pages/{id}/resources → ItemList[ResourceItem], draining next_cursor.
The unified listing of everything attached to a page — attachments AND grids — as
{type, item} envelopes. Capped at limit (None = every resource); narrow
with q (title search), types (comma-separated attachment,grid), and
order_by (name_title or created_at).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_id
|
int
|
The page's id. |
required |
limit
|
int | None
|
The most resources to return; |
None
|
q
|
str | None
|
The title search. |
None
|
types
|
str | None
|
The comma-separated kinds to list: |
None
|
order_by
|
str | None
|
The sort field: |
None
|
order_direction
|
str | None
|
The sort direction for |
None
|
Returns:
| Type | Description |
|---|---|
ItemList[ResourceItem]
|
The page's attachments and grids. |
Examples:
>>> found = wiki.resources.list(5401, limit=25, q="plan", types="attachment,grid")
>>> [resource.type for resource in found.root]
['attachment', 'grid']
recovery¶
RecoveryClient ¶
RecoveryClient(*, session: SyncSession)
Bases: Resource
Restore a deleted page by its recovery token.
restore ¶
restore(token: str) -> RecoveredPage
POST /recovery_tokens/{token}/recover → the restored page and how many came back.
Redeems a recovery_token returned by PagesClient.delete to undo the delete.
No request body — the token in the path is the whole request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
The recovery token |
required |
Returns:
| Type | Description |
|---|---|
RecoveredPage
|
The restored page's |
Examples:
>>> wiki.recovery.restore("recovery-token-1").slug
'eng/restored'
search¶
SearchClient ¶
SearchClient(*, session: SyncSession)
Bases: Resource
/search: one page of full-text results per call.
query ¶
query(body: dict[str, Any]) -> SearchPage
POST /search → one :class:SearchPage of hits for the query.
body is a dumped :class:SearchRequest (query, optional filters, cursor,
limit, order_by, highlight). Pages are walked by hand: next_cursor is the
next page's number as text, but the API also sets it after an empty page and repeats
hits for a page past the last one, so there is no reliable end to drain to. Stop at the
first page with no results or when next_cursor is None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
dict[str, Any]
|
The search request: |
required |
Returns:
| Type | Description |
|---|---|
SearchPage
|
The page of hits. |
Examples:
>>> body = {"query": "quarterly roadmap", "cursor": 3, "limit": 25}
>>> page = wiki.search.query(body)
>>> page.results[0].slug, page.next_cursor
('team/roadmap', '4')
grids¶
GridsClient ¶
GridsClient(*, session: SyncSession)
Bases: Resource
/grids — dynamic tables (CRUD + rows/columns/cells + clone).
Reads: :meth:get, :meth:suggest_column. Writes: :meth:create, :meth:update,
:meth:delete, the row/column add/remove/move calls, :meth:update_cells, the async
:meth:clone, :meth:update_column and :meth:update_row.
Every mutating body carries a revision for optimistic locking except create (no prior
revision) and clone (a deferred trigger); update_column and update_row take one but
the API does not enforce it there.
suggest_column, update_column and update_row call operations Yandex does not
document (they are in the live OpenAPI only), so their contract may change without notice.
get ¶
get(grid_id: str, fields: str | None = None, row_filter: str | None = None, only_cols: str | None = None, only_rows: str | None = None, revision: str | None = None, sort: str | None = None) -> Grid
GET /grids/{id} → the full :class:~ycli.yandex.wiki.grids.models.Grid.
fields adds optional blocks (attributes, user_permissions); row_filter /
only_cols / only_rows / sort narrow the returned rows and columns server-side;
revision loads a historical version. Read the revision off the result to drive any
subsequent write's optimistic lock.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
fields
|
str | None
|
The optional blocks to add: |
None
|
row_filter
|
str | None
|
The server-side row filter. |
None
|
only_cols
|
str | None
|
The columns to return. |
None
|
only_rows
|
str | None
|
The rows to return. |
None
|
revision
|
str | None
|
The historical revision to load. |
None
|
sort
|
str | None
|
The sort order of the rows. |
None
|
Returns:
| Type | Description |
|---|---|
Grid
|
The grid. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> wiki.grids.get(grid_id).revision
'12'
create ¶
create(body: dict[str, Any]) -> Grid
POST /grids — create a grid as a page resource. body is a dumped GridCreate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
dict[str, Any]
|
The new grid: its title and the page it goes on. |
required |
Returns:
| Type | Description |
|---|---|
Grid
|
The created grid. |
Examples:
>>> body = {"title": "Hiring plan", "page": {"slug": "hr/hiring"}}
>>> wiki.grids.create(body).title
'Hiring plan'
update ¶
update(grid_id: str, body: dict[str, Any]) -> RevisionResult
POST /grids/{id} — rename / re-sort (POST not PATCH). body carries revision.
body is a dumped GridUpdate; its default_sort must use the write shape
[{"<column_slug>": "asc"|"desc"}] — the {slug, title, direction} read shape
returned by :meth:get is rejected with a 400.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The changes, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> body = {
... "revision": "12",
... "title": "Roadmap 2027",
... "default_sort": [{"due": "desc"}],
... }
>>> wiki.grids.update(grid_id, body).revision
'13'
delete ¶
delete(grid_id: str) -> Ack
DELETE /grids/{id} → an :class:Ack (204 No Content).
The API returns no body, so the result is synthesized; a non-2xx status raises a typed
YandexError before this returns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
Returns:
| Type | Description |
|---|---|
Ack
|
The acknowledgement of the delete. |
Examples:
>>> wiki.grids.delete("0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a03").ok
True
add_rows ¶
add_rows(grid_id: str, body: dict[str, Any]) -> RowsAddResult
POST /grids/{id}/rows — insert rows. body is a dumped RowsAdd (+ revision).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The rows to add, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RowsAddResult
|
The grid's new revision and the added rows. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> rows = [{"name": "Launch", "owner": "vera"}]
>>> wiki.grids.add_rows(grid_id, {"revision": "13", "rows": rows}).revision
'14'
remove_rows ¶
remove_rows(grid_id: str, body: dict[str, Any]) -> RevisionResult
DELETE /grids/{id}/rows — delete rows by id. body is a dumped RowsRemove.
A rare DELETE-with-body: row_ids + revision travel in the JSON body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The ids of the rows to delete, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> wiki.grids.remove_rows(
... grid_id, {"revision": "14", "row_ids": ["r1", "r2"]}
... ).revision
'15'
move_rows ¶
move_rows(grid_id: str, body: dict[str, Any]) -> RevisionResult
POST /grids/{id}/rows/move — reorder rows. body is a dumped RowsMove.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The row to move and where, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> wiki.grids.move_rows(
... grid_id, {"revision": "15", "row_id": "r3", "position": 4}
... ).revision
'16'
add_columns ¶
add_columns(grid_id: str, body: dict[str, Any]) -> RevisionResult
POST /grids/{id}/columns — add columns. body is a dumped ColumnsAdd.
The API requires a slug on every column (400 value_error.missing without one);
ColumnsAdd derives it from the title when omitted, but a raw dict body passed here
directly must carry it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The columns to add, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> columns = [{"title": "Due Date", "type": "date", "slug": "due_date"}]
>>> wiki.grids.add_columns(grid_id, {"revision": "16", "columns": columns}).revision
'17'
remove_columns ¶
remove_columns(grid_id: str, body: dict[str, Any]) -> RevisionResult
DELETE /grids/{id}/columns — delete columns by slug. body is a ColumnsRemove.
A rare DELETE-with-body: column_slugs + revision travel in the JSON body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The slugs of the columns to delete, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> body = {"revision": "17", "column_slugs": ["stage", "due_date"]}
>>> wiki.grids.remove_columns(grid_id, body).revision
'18'
move_columns ¶
move_columns(grid_id: str, body: dict[str, Any]) -> RevisionResult
POST /grids/{id}/columns/move — reorder columns. body is a ColumnsMove dump.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The column to move and where, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
RevisionResult
|
The grid's new revision. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> body = {"revision": "18", "column_slug": "owner", "position": 0}
>>> wiki.grids.move_columns(grid_id, body).revision
'19'
update_cells ¶
update_cells(grid_id: str, body: dict[str, Any]) -> CellsUpdateResult
POST /grids/{id}/cells — set individual cell values. body is a CellsUpdate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The cells to set, with the grid's |
required |
Returns:
| Type | Description |
|---|---|
CellsUpdateResult
|
The grid's new revision and the updated cells. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> cells = [{"row_id": 101, "column_slug": "name", "value": "Launch v2"}]
>>> wiki.grids.update_cells(grid_id, {"revision": "19", "cells": cells}).revision
'20'
clone ¶
clone(grid_id: str, body: dict[str, Any]) -> AsyncOperation
POST /grids/{id}/clone — copy the grid onto another page (async trigger).
Returns a :class:~ycli.yandex.wiki.models.AsyncOperation; poll its
operation.id via OperationsClient.gridclone_get until terminal. body is a
dumped GridClone ({target, title?, with_data}).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The target page, an optional title and whether to copy the data. |
required |
Returns:
| Type | Description |
|---|---|
AsyncOperation
|
The clone operation to poll. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> body = {"target": "eng/roadmap-copy", "title": "Roadmap copy", "with_data": True}
>>> wiki.grids.clone(grid_id, body).operation.id
'task-6201'
suggest_column ¶
suggest_column(grid_id: str, body: dict[str, Any]) -> ColumnSuggestion
POST /grids/{id}/columns/suggest — is a column slug free? (undocumented, may change).
A read despite the POST: it changes nothing. body is a dumped :class:ColumnSuggest
({title?, slug?}); a title is turned into a slug first. The reply says whether the
slug is occupied and lists free alternatives.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
body
|
dict[str, Any]
|
The column's |
required |
Returns:
| Type | Description |
|---|---|
ColumnSuggestion
|
Whether the slug is occupied, with free alternatives. |
Examples:
>>> wiki.grids.suggest_column(
... "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a02", {"title": "Due date"}
... ).occupied
False
update_column ¶
update_column(grid_id: str, column_slug: str, body: dict[str, Any]) -> ColumnUpdateResult
POST /grids/{id}/column/{slug} — edit a column in place (undocumented, may change).
The only way to change a column after creating it; its type and slug stay. body
is a dumped :class:ColumnUpdate: only the fields sent change. revision is accepted but
not enforced (a stale or missing one works) and every call moves the grid's revision on.
Returns the new revision and the column as saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
column_slug
|
str
|
The column's slug. |
required |
body
|
dict[str, Any]
|
The fields to change. |
required |
Returns:
| Type | Description |
|---|---|
ColumnUpdateResult
|
The grid's new revision and the column as saved. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> wiki.grids.update_column(grid_id, "stage", {"title": "Stage 2"}).column.title
'Stage 2'
update_row ¶
update_row(grid_id: str, row_id: str, body: dict[str, Any]) -> RowUpdateResult
POST /grids/{id}/rows/{row_id} — pin or colour one row (undocumented, may change).
body is a dumped :class:RowUpdate ({revision?, pinned?, color?}). The reply is a
bare acknowledgement without the new revision (read it with :meth:get); revision is
accepted but not enforced, and every call moves the grid's revision on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid_id
|
str
|
The grid's id. |
required |
row_id
|
str
|
The row's id. |
required |
body
|
dict[str, Any]
|
The row's |
required |
Returns:
| Type | Description |
|---|---|
RowUpdateResult
|
The acknowledgement of the change. |
Examples:
>>> grid_id = "0b5e6f7a-1c2d-4e3f-8a9b-0c1d2e3f4a01"
>>> wiki.grids.update_row(grid_id, "103", {"pinned": True, "color": "orange"})
RowUpdateResult(...)
operations¶
OperationsClient ¶
OperationsClient(*, session: SyncSession)
Bases: Resource
Status reads for async page/grid clones and page moves.
Every endpoint is a normal read (they surface on MCP): re-read one until its status is
terminal to wait for the operation triggered by pages clone / grids clone /
pages move.
clone_get ¶
clone_get(task_id: str) -> CloneOperationStatus
GET /operations/clone/{task_id} → a page-clone's status (poll this to wait).
The task_id is the operation.id returned by PagesClient.clone. Poll until
is_terminal; on success the result.page names the clone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
The operation's id. |
required |
Returns:
| Type | Description |
|---|---|
CloneOperationStatus
|
The clone's status. |
Examples:
>>> wiki.operations.clone_get("task-5201").is_terminal
True
gridclone_get ¶
gridclone_get(task_id: str) -> GridCloneOperationStatus
GET /operations/clone_inline_grid/{task_id} → a grid-clone's status (poll to wait).
The task_id is the operation.id returned by GridsClient.clone. Poll until
is_terminal; on success the result.grid_id names the copy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
The operation's id. |
required |
Returns:
| Type | Description |
|---|---|
GridCloneOperationStatus
|
The grid clone's status. |
Examples:
>>> wiki.operations.gridclone_get("task-5301").is_terminal
False
move_get ¶
move_get(task_id: str) -> MoveOperationStatus
GET /operations/move/{task_id} → a page-move's status (poll this to wait).
Undocumented by Yandex (present in the live OpenAPI only) and may change. The task_id
is the operation.id returned by PagesClient.move. Poll until is_terminal; on
success the result.page_count says how many pages moved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task_id
|
str
|
The operation's id. |
required |
Returns:
| Type | Description |
|---|---|
MoveOperationStatus
|
The move's status. |
Examples:
>>> wiki.operations.move_get("task-5401").result.page_count
4
uploadsessions¶
UploadSessionsClient ¶
UploadSessionsClient(*, session: SyncSession)
Bases: Resource
/upload_sessions: create · get · upload-part · finish · abort.
create ¶
create(body: UploadSessionCreate) -> UploadSession
Open an upload session from a typed UploadSessionCreate body. Returns the session.
The returned session_id addresses the session for upload_part / finish.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body
|
UploadSessionCreate
|
The file's name and size. |
required |
Returns:
| Type | Description |
|---|---|
UploadSession
|
The opened session. |
Examples:
>>> from ycli.yandex.wiki.uploadsessions.models import UploadSessionCreate
>>> body = UploadSessionCreate(file_name="report.xlsx", file_size=7340032)
>>> wiki.uploadsessions.create(body).status
'not_started'
get ¶
get(session_id: str) -> UploadSession
GET /upload_sessions/{session_id} → the session's current state (poll status).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
The session's id. |
required |
Returns:
| Type | Description |
|---|---|
UploadSession
|
The session. |
Examples:
>>> session_id = "9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
>>> wiki.uploadsessions.get(session_id).status
'in_progress'
upload_part ¶
upload_part(session_id: str, *, part_number: int, data: bytes) -> UploadSession
Upload one file part as raw application/octet-stream bytes. Returns the session.
part_number is 1-based (1 for the first part, +1 for each next). Parts may be
5-16 MB except the last; a small file fits in a single part_number=1 call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
The session's id. |
required |
part_number
|
int
|
The part's 1-based number. |
required |
data
|
bytes
|
The part's bytes. |
required |
Returns:
| Type | Description |
|---|---|
UploadSession
|
The session. |
Examples:
>>> session_id = "9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
>>> wiki.uploadsessions.upload_part(session_id, part_number=1, data=b"part").status
'in_progress'
finish ¶
finish(session_id: str) -> UploadSession
POST /upload_sessions/{session_id}/finish — close the session so the file can attach.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
The session's id. |
required |
Returns:
| Type | Description |
|---|---|
UploadSession
|
The finished session. |
Examples:
>>> session_id = "9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
>>> wiki.uploadsessions.finish(session_id).status
'finished'
abort ¶
abort(session_id: str) -> UploadSession
POST /upload_sessions/{session_id}/abort — cancel one in-progress session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str
|
The session's id. |
required |
Returns:
| Type | Description |
|---|---|
UploadSession
|
The aborted session. |
Examples:
>>> session_id = "9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
>>> wiki.uploadsessions.abort(session_id).status
'aborted'
abort_all ¶
abort_all() -> AbortActiveUploadsResult
POST /upload_sessions/abort_active_uploads — cancel ALL active sessions (free quota).
Returns:
| Type | Description |
|---|---|
AbortActiveUploadsResult
|
The result of the abort. |
Examples:
>>> wiki.uploadsessions.abort_all().status
'ok'