Skip to content

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'

probe

probe() -> None

One cheap authenticated read: the current user.

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:revisions; None shows the current one.

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:revisions; None shows the current one.

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 returns every ref.

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 returns every ref.

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 returns every grid.

None
order_by str | None

The sort field: title or created_at.

None
order_direction str | None

The sort direction for order_by: asc or desc.

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: content, title and slug.

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 recovery_token.

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 copy_inherited_access.

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 every revision.

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(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 every ref.

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: user or group, role and optional inheritance.

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: role and/or inheritance.

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 returns every comment.

None
order_by str | None

The sort field; the API accepts created_at.

None
order_direction str | None

The sort direction for order_by: asc or desc.

None
status_filter str | None

Keep only resolved or only unresolved comments.

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 collects every reply.

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 everything.

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: body and optional inline_text, parent_id, thread_id.

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 comments_count is the number of comments left.

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 returns every attachment.

None
order_by str | None

The sort field: name, size or created_at.

None
order_direction str | None

The sort direction for order_by: asc or desc.

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 <page-slug>/.files/<filename> URL.

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 returns every resource.

None
q str | None

The title search.

None
types str | None

The comma-separated kinds to list: attachment, grid.

None
order_by str | None

The sort field: name_title or created_at.

None
order_direction str | None

The sort direction for order_by: asc or desc.

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 PagesClient.delete returned.

required

Returns:

Type Description
RecoveredPage

The restored page's id and slug, and how many pages were restored.

Examples:

>>> wiki.recovery.restore("recovery-token-1").slug
'eng/restored'

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: query and optional filters, cursor, limit, order_by, highlight.

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: attributes, user_permissions.

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 revision.

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 revision.

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 revision.

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 revision.

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 revision.

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 revision.

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 revision.

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 revision.

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 title and/or slug.

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 pinned and color, and optionally revision.

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'