Pages
Endpoints for managing Grav pages including listing, creating, updating, deleting, copying, moving, and reordering, plus page translations, batch operations, draft preview tokens, taxonomy, and the site's language list.
Endpoints for managing Grav pages including listing, creating, updating, deleting, copying, moving, and reordering, plus page translations, batch operations, draft preview tokens, taxonomy, and the site's language list.
List Pages
/pages
Parameters
| Name | Type | Description |
|---|---|---|
| page optional | integer | Page number for pagination (default 1). |
| per_page optional | integer | Number of results per page (default 20, capped at `plugins.api.pagination.max_per_page`, 1000 by default). |
| sort optional | string | Sort field: `date`, `title`, `slug`, `modified`, `order`, or `default`. When omitted, pages are sorted by `date` descending. `default` with `children_of` uses the parent's native page ordering; without `children_of` it sorts by `order` ascending. An unknown field returns 422. |
| order optional | string | Sort direction: `asc` (default) or `desc`. Ignored when `sort` is omitted. |
| search optional | string | Full-text search across indexed page fields (Flex backend only). |
| published optional | boolean | Filter by published state. |
| visible optional | boolean | Filter by visible state. |
| routable optional | boolean | Filter by routable state. |
| template optional | string | Filter by page template name. |
| parent optional | string | Filter to descendants whose route starts with this parent path (e.g. `/blog` returns `/blog/post-1`, `/blog/sub/post-2`). |
| children_of optional | string | Filter to direct children of a given route. |
| root optional | boolean | `true` returns only top-level pages (direct children of the pages root, so children of the home page are not counted). `false` returns every page that is not top-level. |
| locate optional | string | Route of a page to jump to. When it is in the filtered, sorted results, the server returns the chunk that contains it (overriding `page`) and reports its position in `meta.pagination.located_at_index`. |
| lang optional | string | List pages in a specific language. A code that is not a configured language returns 422. |
| translations optional | boolean | Include translation metadata on each item: `translated_languages`, `untranslated_languages`, `has_default_file`, `explicit_language_files`. |
{"data": [{"route": "/blog", "slug": "blog", "title": "Blog", "template": "blog", "published": true, "visible": true, "routable": true, "has_children": true, "permissions": {"create": true, "read": true, "update": true, "delete": true, "publish": true, "list": true}}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 42, "total_pages": 3}}, "links": {"self": "/api/v1/pages?page=1&per_page=20", "next": "/api/v1/pages?page=2&per_page=20", "last": "/api/v1/pages?page=3&per_page=20"}}
Response Codes
Get Page
/pages/{route}
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (e.g. `/blog/my-post`). |
| summary optional | boolean | Leave out the raw `content` and return a plain-text `summary` (rendered, tags stripped, truncated) in its place. |
| summary_size optional | integer | Maximum length of `summary` in characters (default 300). Only used with `summary=true`. |
| render optional | boolean | Also return the rendered HTML in `content_html`. |
| children optional | boolean | Include child pages in the response. |
| children_depth optional | integer | How many levels of children to include (default 1). Only used with `children=true`. |
| translations optional | boolean | Include translation metadata: `translated_languages`, `untranslated_languages`, `has_default_file` (whether an untyped `{template}.md` exists), `explicit_language_files` (the subset of languages backed by a real `{template}.{lang}.md` on disk). |
| lang optional | string | Return the page in a specific language (overrides the request's active language). |
{"data": {"route": "/blog/my-post", "slug": "my-post", "title": "My Post", "template": "post", "content": "# Hello World", "header": {"published": true}, "media": [], "has_default_file": true, "explicit_language_files": ["fr"]}}
Response Codes
Create Page
/pages
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The route where the page will be created |
| title required | string | The page title |
| template optional | string |
Page template to use
Default:
default |
| content optional | string | Markdown content for the page body |
| header optional | object | Page header/frontmatter values |
| order optional | integer | Numeric ordering prefix for the page folder. Pass `"auto"` to use the next free number among the siblings (or no prefix when no sibling has one). |
| kind optional | string |
One of `page` (default), `folder` (a folder with no `.md` file) or `module` (a modular section, its slug is prefixed with `_`). For `folder`, the response may carry only `route` and `kind`.
Default:
page |
| lang optional | string | Language code for multi-language sites (body field, or `?lang=` query parameter). Writes a `{template}.{lang}.md` file. |
{"route": "/blog/new-post", "title": "New Post", "template": "post", "content": "# New Post\nContent here"}
{"data": {"route": "/blog/new-post", "slug": "new-post", "title": "New Post", "template": "post", "published": true, "content": "# New Post\nContent here", "media": [], "permissions": {"create": true, "read": true, "update": true, "delete": true, "publish": true, "list": true}}}
Response Codes
Update Page
/pages/{route}
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path parameter) |
| title optional | string | Updated page title |
| content optional | string | Updated markdown content |
| header optional | object | Header values to merge into existing frontmatter. A `null` value removes that key. |
| header_mode optional | string | Set to `replace` to replace the whole frontmatter with `header` instead of merging (used by raw frontmatter editors). |
| template optional | string | Change the page template |
| published optional | boolean | Set the published state |
| visible optional | boolean | Set the visible state |
| lang optional | string | Query parameter. Update a specific language version; that translation must already exist (create it with `POST /pages/{route}/translate`). |
{"title": "Updated Title", "header": {"subtitle": "New subtitle"}}
{"data": {"route": "/blog/my-post", "slug": "my-post", "title": "Updated Title", "template": "post", "header": {"title": "Updated Title", "subtitle": "New subtitle"}, "content": "# Hello World", "media": [], "permissions": {"create": true, "read": true, "update": true, "delete": true, "publish": true, "list": true}}}
Response Codes
Supports optimistic concurrency control via the If-Match header. Include the ETag from your last GET request to prevent overwriting concurrent changes.
Delete Page
/pages/{route}
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route to delete |
| children optional | boolean |
Also delete child pages. `false` refuses to delete a page that has children.
Default:
true |
| lang optional | string | Delete only a specific language version (404 if the page has no such translation) |
Response Codes
Move Page
/pages/{route}/move
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The current page route (path parameter) |
| parent required | string | The target parent route (`/` for the top level) |
| slug optional | string | Optionally rename the slug during the move. Must be a single path segment. |
| order optional | integer | Numeric ordering prefix at the new location. Defaults to the current prefix; `null` removes it. |
{"parent": "/blog", "slug": "moved-post"}
{"data": {"route": "/blog/moved-post", "slug": "moved-post", "title": "My Post", "template": "post", "content": "# Hello World", "media": []}}
Response Codes
Reorder Pages
/pages/{route}/reorder
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The parent page route (path parameter) |
| order required | array | Array of child slugs in the desired order |
{"order": ["first-post", "second-post", "third-post"]}
{"data": [{"route": "/blog/first-post", "slug": "first-post", "title": "First Post", "order": "01."}, {"route": "/blog/second-post", "slug": "second-post", "title": "Second Post", "order": "02."}, {"route": "/blog/third-post", "slug": "third-post", "title": "Third Post", "order": "03."}]}
Response Codes
Copy Page
/pages/{route}/copy
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | Source page route (path param). |
| route required | string | Destination route for the copy (body field). |
{"route": "/blog/my-post-copy"}
{"data": {"route": "/blog/my-post-copy", "slug": "my-post-copy", "title": "My Post", "template": "post"}}
Response Codes
List Page Languages
/pages/{route}/languages
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
{"data": {"route": "/blog/my-post", "default_language": "en", "translated": {"en": "/blog/my-post", "fr": "/blog/mon-article"}, "untranslated": ["de", "es"]}}
Response Codes
Note
When the site has only a bare default.md (no language suffix) and multilang is enabled, Grav will report the default language in translated as a fallback. Use GET /pages/{route}?translations=true to get the has_default_file / explicit_language_files fields that disambiguate which languages are backed by a real on-disk file.
Create Translation
/pages/{route}/translate
` to fetch the translation. Requires multi-language to be enabled and `api.pages.write`, subject to the page's own `update` rule. Enabling `process.twig` in `header` also needs the Twig-in-content permission.
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
| lang required | string | Target language code (must match a configured site language). |
| title optional | string | Title for the translation (defaults to the source page title). |
| content optional | string | Raw markdown content (defaults to the source page content). |
| header optional | object | Frontmatter object (defaults to a copy of the source page header, with `title` merged in). Template blueprint defaults fill in anything missing. |
{"lang": "fr", "title": "Mon article", "content": "# Bonjour"}
{"data": {"route": "/blog/my-post", "slug": "my-post", "title": "Mon article", "template": "post", "language": "fr", "header": {"title": "Mon article"}, "content": "# Bonjour", "media": []}}
Response Codes
Adopt Language
/pages/{route}/adopt-language
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
| lang required | string | Language code to adopt the base file as. |
{"lang": "en"}
{"data": {"route": "/blog/my-post", "slug": "my-post", "title": "My Post", "template": "default", "language": "en", "content": "# Hello", "media": []}}
Response Codes
Sync Translation
/pages/{route}/sync
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
| source_lang required | string | Language code to copy content from. |
| target_lang required | string | Language code to overwrite. |
{"source_lang": "en", "target_lang": "fr"}
{"data": {"route": "/blog/my-post", "slug": "my-post", "title": "My Post", "template": "post", "language": "fr", "content": "# Hello", "media": []}}
Response Codes
Compare Translations
/pages/{route}/compare
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
| source required | string | Source language code (query param). |
| target required | string | Target language code (query param). |
{"data": {"route": "/blog/my-post", "source": {"lang": "en", "exists": true, "title": "My Post", "content": "# Hello", "header": {"title": "My Post"}, "modified": "2026-04-17T10:00:00+00:00"}, "target": {"lang": "fr", "exists": false, "title": "My Post", "content": "# Hello", "header": {"title": "My Post"}, "modified": null}}}
Response Codes
Batch Page Operations
/pages/batch
Parameters
| Name | Type | Description |
|---|---|---|
| operation required | string | One of: `publish`, `unpublish`, `delete`, `copy`. |
| routes required | array | Non-empty array of page routes. |
| options optional | object | Used by `copy` only: `destination` (parent route to copy into, `/` for the top level, defaults to each page's own parent) and `suffix` (appended to each slug, default `-copy`, must be a single path segment). |
{"operation": "publish", "routes": ["/blog/post-1", "/blog/post-2"]}
{"data": {"operation": "publish", "results": [{"route": "/blog/post-1", "status": "success"}, {"route": "/blog/post-2", "status": "error", "message": "Page permissions deny 'publish' on this page."}], "total": 2, "successful": 1, "failed": 1}}
Response Codes
Reorganize Pages
/pages/reorganize
Parameters
| Name | Type | Description |
|---|---|---|
| operations required | array | Non-empty array of operation objects. Each must have a `route`; optional fields are `parent` (new parent route, `/` for the top level; omit to stay under the current parent) and `position` (1-based integer, controls the numeric prefix on the folder). |
{"operations": [{"route": "/blog/post-1", "parent": "/archive", "position": 1}, {"route": "/blog/post-2", "position": 3}]}
{"data": [{"route": "/archive/post-1", "slug": "post-1", "title": "Post 1", "order": 1, "parent": "/archive"}, {"route": "/blog/post-2", "slug": "post-2", "title": "Post 2", "order": 3, "parent": "/blog"}]}
Response Codes
List Taxonomy
/taxonomy
{"data": {"category": ["blog", "docs"], "tag": ["php", "grav", "api"], "author": []}}
Response Codes
List Site Languages
/languages
{"data": {"enabled": true, "languages": [{"code": "en", "name": "English", "native_name": "English", "rtl": false, "is_default": true}, {"code": "fr", "name": "French", "native_name": "Français", "rtl": false, "is_default": false}], "default": "en", "active": "en"}}
Response Codes
Get Preview Token
/pages/{route}/preview-token
Parameters
| Name | Type | Description |
|---|---|---|
| route required | string | The page route (path param). |
| lang optional | string | Resolve the page in a specific language (query param). |
{"data": {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 300, "route": "/blog/my-post", "anchor": null}}