Skip to content

Powered by Grav + Helios

Getting Started

Getting Started with the Grav API

The Grav API Plugin adds a RESTful API to your Grav site, providing full headless access to pages, media, configuration, users, and system management.

Requirements

  • Grav CMS 2.0+
  • PHP 8.3+
  • Login Plugin 3.8+

Installation

Install via GPM:

BASH
bin/grav install api

Or manually: download the plugin into user/plugins/api and run composer install.

Quick Setup

1. Enable the Plugin

YAML
# user/config/plugins/api.yaml
enabled: true

2. Generate an API Key

Via CLI (recommended for initial setup):

BASH
bin/plugin api keys:generate --user=admin --name="My First Key"

Via Admin Panel: Navigate to a user's profile and use the API Keys section.

The key is shown once — save it immediately.

3. Make Your First Request

BASH
curl https://yoursite.com/api/v1/pages \
  -H "X-API-Key: grav_abc123..."

Configuration

The API is configured via user/config/plugins/api.yaml:

YAML
enabled: true
route: /api                    # Base route for all API endpoints
version_prefix: v1             # Version prefix

auth:
  api_keys_enabled: true       # Enable API key authentication
  jwt_enabled: true            # Enable JWT token authentication
  session_enabled: true        # Enable session passthrough

cors:
  enabled: true                # Enable CORS headers
  origin: '*'                  # Allowed origins
  credentials: false           # Allow credentials

rate_limit:
  enabled: true                # Enable rate limiting
  requests_per_minute: 120     # Requests per minute per user/IP

pagination:
  default_per_page: 20         # Default items per page
  max_per_page: 100            # Maximum items per page

Environments

Grav supports multiple environments. The API respects this via the X-Grav-Environment header:

BASH
curl -H "X-Grav-Environment: mysite.com" \
     -H "X-API-Key: ..." \
     https://yoursite.com/api/v1/pages

Response Format

All API responses follow a consistent structure:

JSON
{
  "data": { ... }
}

Paginated responses include metadata:

JSON
{
  "data": [ ... ],
  "meta": {
    "total": 42,
    "page": 1,
    "per_page": 20,
    "total_pages": 3
  },
  "links": {
    "self": "/api/v1/pages?page=1",
    "next": "/api/v1/pages?page=2",
    "last": "/api/v1/pages?page=3"
  }
}

Errors use RFC 7807 format:

JSON
{
  "status": 404,
  "title": "Not Found",
  "detail": "Page '/missing' not found."
}

Concurrency Control

The API uses ETags for optimistic concurrency. When updating resources, include the If-Match header with the ETag from the GET response:

BASH
# Fetch with ETag
curl -H "X-API-Key: ..." https://yoursite.com/api/v1/pages/blog
# Response includes: ETag: "abc123"

# Update with If-Match
curl -X PATCH \
  -H "X-API-Key: ..." \
  -H "If-Match: \"abc123\"" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title"}' \
  https://yoursite.com/api/v1/pages/blog

If the resource was modified since your last fetch, you'll receive a 409 Conflict response.

Using the API with AI

The API is what powers AI integration in Grav 2.0. The first-party Grav MCP Server connects agentic AI clients (Claude Code, Claude Desktop, Cursor, and other Model Context Protocol clients) to your site, exposing pages, media, configuration, users, and system management as AI-callable tools. It authenticates with the same API keys described here and respects the same permission model. See the MCP Server documentation to get started.