REST API
The Content REST API is a read-only API for delivering content from Statamic to your frontend, external apps, SPAs, and numerous other possible sources. Content is delivered as JSON data.
(If you're interested in GraphQL, we have that too.)
Enable the API#
To enable the REST API, add the following to your .env file:
STATAMIC_API_ENABLED=true
Or you can enable it for all environments in config/statamic/api.php:
'enabled' => true,
You will also need to enable the resources you want to be available. For security, they're all disabled by default.
Enable resources#
You can enable resources (ie. Collections, Taxonomies, etc.) in your config/statamic/api.php config:
'resources' => [
'collections' => true,
'taxonomies' => true,
// etc
]
Enable specific sub-resources#
If you want more granular control over which sub-resources are enabled within a resource type (ie. enabling specific Collection queries only), you can use array syntax:
'resources' => [
'collections' => [
'articles' => true,
'pages' => true,
// 'events' => false, // Sub-resources are disabled by default
],
'taxonomies' => true,
// etc.
]
Endpoints#
https://yourdomain.tld/api/{endpoint}
You may send requests to the following endpoints:
- Ping
- Sites
- Collections / Collection
- Entries / Entry
- Collection Tree / Navigation Tree
- Navs / Nav
- Taxonomies / Taxonomy
- Taxonomy Terms / Taxonomy Term
- Asset Containers / Asset Container
- Assets / Asset
- Globals / Global
- Forms / Form
- Users / User
Customizing the API URL#
You may customize the route in your API config file or with an environment variable.
// config/statamic/api.php
'route' => 'not_api',
STATAMIC_API_ROUTE=not_api
Filtering#
Enabling filters#
For security, filtering is disabled by default. To enable, you'll need to opt in by defining a list of allowed_filters for each sub-resource in your config/statamic/api.php config:
'resources' => [
'collections' => [
'articles' => [
'allowed_filters' => ['title', 'status'],
],
'pages' => [
'allowed_filters' => ['title'],
],
'events' => true, // Enable this collection without filters
'products' => true, // Enable this collection without filters
],
'taxonomies' => [
'topics' => [
'allowed_filters' => ['slug'],
],
'tags' => true, // Enable this taxonomy without filters
],
// etc.
],
For endpoints that don't have sub-resources (ie. users), you can define allowed_filters at the top level of that resource config:
'resources' => [
'users' => [
'allowed_filters' => ['name', 'email'],
],
],
Using filters#
You may filter results by using the filter query parameter.
/endpoint?filter[{field}:{condition}]={value}
You may use the conditions available to the collection tag. eg. contains, is, isnt (or not), etc. For example:
/endpoint?filter[title:contains]=awesome&filter[featured]=true
This would filter down the results to where the title value contains the string "awesome", and the featured
value is true. When you omit the condition, it defaults to is.
Advanced filtering config#
You can also allow filters on all enabled sub-resources using a * wildcard config. For example, here we'll enable only the articles, pages, and products collections, with title filtering enabled on each, in addition to status filtering on the articles collection specifically:
'resources' => [
'collections' => [
'*' => [
'allowed_filters' => ['title'], // Enabled for all collections
],
'articles' => [
'allowed_filters' => ['status'], // Also enable on articles
],
'pages' => true,
'products' => true,
],
],
If you've enabled filters using the * wildcard config, you can disable filters on a specific sub-resource by setting allowed_filters to false:
'resources' => [
'collections' => [
'*' => [
'allowed_filters' => ['title'], // Enabled for all collections
],
'articles' => [
'allowed_filters' => false, // Disable filters on articles
],
'pages' => true,
'products' => true,
],
],
Or you can enable endpoints and filters on all sub-resources at once by setting both enabled and allowed_filters within your * wildcard config:
'resources' => [
'collections' => [
'*' => [
'enabled' => true, // All collection endpoints enabled
'allowed_filters' => ['title'], // With filters enabled for all
],
],
],
Sorting#
You may sort results by using the sort query parameter:
/endpoint?sort=field
You can sort in reverse by prefixing the field with a -:
/endpoint?sort=-field
You may sort by multiple fields by comma separating them. The reverse flag can be combined with any field:
/endpoint?sort=one,-two,three
You can sort nested fields using the -> operator, like this:
/endpoint?sort=nested->field
Selecting fields#
You may specify which top level fields should be included in the response.
/endpoint?fields=id,title,content
You may also use array syntax:
/endpoint?fields[]=id&fields[]=title&fields[]=content
On the Entry and Asset endpoints, fields will also be honored when fetching a single item, not just when listing them.
Pagination#
Results will be paginated into 25 items per page by default. You may specify the items per page and which page you are viewing with the limit and page parameters:
/endpoint?limit=10&page=1
The response will contain your data, links to easily get next/previous URLs, and meta information for more easily creating a paginator.
{
"data": [
{...},
{...},
],
"links": {
"first": "/endpoint?limit=10&page=1",
"last": "/endpoint?limit=10&page=3",
"prev": null,
"next": "/endpoint?limit=10&page=2",
},
"meta": {
"current_page": 1,
"from": 1,
"to": 10,
"total": 29,
"per_page": 10,
"path": "/endpoint",
}
}
Ping#
GET /api/ping
A simple health check endpoint. It returns pong, and is available whenever the REST API is enabled, regardless of which resources are enabled.
{
"ping": "pong"
}
Sites#
GET /api/sites
Gets all configured sites. This resource is disabled by default. You can enable it with resources.sites in your config/statamic/api.php config:
'resources' => [
'sites' => true,
],
{
"data": [
{
"handle": "default",
"name": "Default",
"locale": "en_US",
"short_locale": "en",
"url": "http://example.com/"
}
]
}
Collections#
GET /api/collections
Gets all collections.
{
"data": [
{
"handle": "blog",
"title": "Blog",
"structure": null,
"mount": "9412926e-8d33-4f83-8db9-72cbc4c69bcf",
"api_url": "http://example.com/api/collections/blog"
}
]
}
Collection#
GET /api/collections/{collection}
Gets a single collection.
{
"data": {
"handle": "blog",
"title": "Blog",
"structure": null,
"mount": "9412926e-8d33-4f83-8db9-72cbc4c69bcf",
"api_url": "http://example.com/api/collections/blog"
}
}
If the collection is structured, structure will contain its max_depth and expects_root values instead of null.
Entries#
GET /api/collections/{collection}/entries
Gets entries within a collection.
{
"data": [
{
"title": "My First Day"
}
],
"links": {...},
"meta": {...}
}
If you are using Multi-Site, the entries endpoint will serve from all sites at once. If needed, you can limit the fetched data to a specific site with the site query parameter (ie. ?site=fr), or a site filter (ie. &filter[site]=fr).
Entry#
GET /api/collections/{collection}/entries/{id}
Gets a single entry by ID or slug. If both an ID and a slug match, the entry matched by ID takes priority. When resolving by slug on a multi-site collection, the site query parameter will be used if provided, otherwise the collection's first configured site will be used.
{
"data": {
"title": "My First Day"
}
}
Collection Tree#
GET /api/collections/{collection}/tree
Gets entry tree for a structured collection.
{
"data": [
{
"page": {
"title": "About",
"url": "/about"
},
"depth": 1,
"children": [
{
"page": {
"title": "Articles",
"url": "/about/articles"
},
"depth": 2,
"children": []
}
]
}
]
}
Params#
On this endpoint, the fields param will allow you to select fields within each page object. You may also set a max_depth to limit nesting depth, or site to choose the site.
/api/collections/{collection}/tree?fields=title,url&max_depth=2&site=fr
Navs#
GET /api/navs
Gets all navigations.
{
"data": [
{
"handle": "main",
"title": "Main Navigation",
"max_depth": null,
"expects_root": false,
"api_url": "http://example.com/api/navs/main"
}
]
}
Nav#
GET /api/navs/{nav}
Gets a single navigation.
{
"data": {
"handle": "main",
"title": "Main Navigation",
"max_depth": null,
"expects_root": false,
"api_url": "http://example.com/api/navs/main"
}
}
Navigation Tree#
GET /api/navs/{nav}/tree
Gets tree for a navigation structure.
{
"data": [
{
"page": {
"title": "Recommended Products",
"url": "https://rainforest.store/?cid=statamic",
},
"depth": 1,
"children": [
{
"page": {
"title": "Books",
"url": "https://rainforest.store/?cid=statamic&type=books",
},
"depth": 2,
"children": []
}
]
}
]
}
Params#
On this endpoint, the fields param will allow you to select fields within each page object. You may also set a max_depth to limit nesting depth, or site to choose the site.
/api/navs/{nav}/tree?fields=title,url&max_depth=2&site=fr
Taxonomies#
GET /api/taxonomies
Gets all taxonomies.
{
"data": [
{
"handle": "tags",
"title": "Tags",
"api_url": "http://example.com/api/taxonomies/tags"
}
]
}
Taxonomy#
GET /api/taxonomies/{taxonomy}
Gets a single taxonomy.
{
"data": {
"handle": "tags",
"title": "Tags",
"api_url": "http://example.com/api/taxonomies/tags"
}
}
Taxonomy Terms#
GET /api/taxonomies/{taxonomy}/terms
Gets terms in a taxonomy.
{
"data": [
{
"title": "Music",
}
],
"links": {...},
"meta": {...}
}
If you are using Multi-Site, you can select the site using the site query parameter (ie. ?site=fr), or a site filter (ie. &filter[site]=fr).
Taxonomy term#
GET /api/taxonomies/{taxonomy}/terms/{slug}
Gets a single taxonomy term.
{
"data": {
"title": "My First Day"
}
}
Globals#
GET /api/globals
Gets all globals.
{
"data": [
{
"handle": "global",
"api_url": "http://example.com/api/globals/global",
"foo": "bar",
},
{
"handle": "another",
"api_url": "http://example.com/api/globals/another",
"baz": "qux",
}
],
}
If you are using Multi-Site, you can select the site using the site parameter (ie. &site=fr).
Global#
GET /api/globals/{handle}
Gets a single global set's variables.
{
"data": {
"handle": "global",
"api_url": "http://example.com/api/globals/global",
"foo": "bar",
}
}
Forms#
GET /api/forms
Gets all forms.
{
"data": [
{
"handle": "contact",
"title": "Contact",
"fields": {
"name": {...},
"email": {...},
"inquiry": {...}
},
"api_url": "http://example.com/api/forms/contact",
},
{
"handle": "newsletter",
"title": "Subscribe to Newsletter",
"fields": {
"email": {...}
},
"api_url": "http://example.com/api/forms/newsletter",
}
],
}
Form#
GET /api/forms/{handle}
Gets a single form.
{
"data": {
"handle": "contact",
"title": "Contact",
"fields": {
"name": {...},
"email": {...},
"inquiry": {...}
},
"api_url": "http://example.com/api/forms/contact",
}
}
Users#
GET /api/users
Get users.
{
"data": [
{
"id": "1",
"api_url": "http://example.com/api/users/1"
}
],
"links": {...},
"meta": {...}
}
User#
GET /api/users/{id}
Get a single user.
{
"data": {
"id": "1",
"api_url": "http://example.com/api/users/1"
}
}
Asset Containers#
GET /api/asset-containers
Gets all asset containers. This uses the same resources.assets config as the Assets endpoint.
{
"data": [
{
"handle": "main",
"title": "Main",
"api_url": "http://example.com/api/asset-containers/main"
}
]
}
Asset Container#
GET /api/asset-containers/{container}
Gets a single asset container.
{
"data": {
"handle": "main",
"title": "Main",
"api_url": "http://example.com/api/asset-containers/main"
}
}
Assets#
GET /api/assets/{container}
Get a container's asset data.
{
"data": [
{
"id": "main::foo.jpg",
"url": "/assets/foo.jpg",
"api_url": "http://example.com/api/assets/main/foo.jpg",
"alt": "A picture of nothing."
}
],
"links": {...},
"meta": {...}
}
Asset#
GET /api/assets/{container}/{path}
Get a single asset's data.
The path in the URL should be the relative path from the container's root.
{
"data": {
"id": "main::foo.jpg",
"url": "/assets/foo.jpg",
"api_url": "http://example.com/api/assets/main/foo.jpg",
"alt": "A picture of nothing."
}
}
Customizing resources#
By default, the resources generally use the item's Augmented data.
You are free to override the resource classes with your own, in turn letting you customize the responses.
In a service provider, use the map method to define the overriding resources:
use App\Http\Resources\CustomEntryResource;
use Statamic\Http\Resources\API\Resource;
use Statamic\Http\Resources\API\EntryResource;
class AppServiceProvider extends Provider
{
public function boot()
{
Resource::map([
EntryResource::class => CustomEntryResource::class,
]);
}
}
<?php
namespace App\Http\Resources;
use Statamic\Http\Resources\API\EntryResource;
class CustomEntryResource extends EntryResource
{
public function toArray($request)
{
return [
'id' => $this->resource->id(),
'title' => $this->resource->value('title'),
];
}
}
Caching#
API responses are cached by default. You may customize the cache expiry in config/statamic/api.php.
'cache' => [
'expiry' => 60,
],
Cache invalidation#
Cached responses are automatically invalidated when content is changed. Depending on your API usage and blueprint schema, you may also wish to ignore specific events when invalidating.
'cache' => [
'expiry' => 60,
'ignored_events' => [
\Statamic\Events\UserSaved::class,
\Statamic\Events\UserDeleted::class,
],
],
Disabling caching#
If you wish to disable caching altogether, set cache to false.
'cache' => false,
Custom cache driver#
If you need a more intricate caching solution, you may reference a custom cache driver class and pass extra config along if necessary.
'cache' => [
'class' => CustomCacher::class,
'expiry' => 60,
'foo' => 'bar',
],
Be sure to extend Statamic\API\AbstractCacher and implement the required methods. You can access custom config via the config() method, ie. $this->config('foo').
use Statamic\API\AbstractCacher;
class CustomCacher extends AbstractCacher
{
public function get(Request $request)
{
//
}
public function put(Request $request, JsonResponse $response)
{
//
}
public function handleInvalidationEvent(Event $event)
{
//
}
}
Rate limiting#
The REST API is rate limited to 60 requests per minute by default.
You can change this configuration in your RouteServiceProvider. Learn more about rate limits in Laravel.
// app/Providers/RouteServiceProvider.php
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60);
});
}
Authentication#
Out of the box, the REST API is publicly accessible.
You can restrict access to the API by adding the STATAMIC_API_AUTH_TOKEN key to your .env file. It should be set to a long, random string.
STATAMIC_API_AUTH_TOKEN=a-long-random-string
Then, when you make requests to the REST API, you'll need to include the token in the Authorization header, like this:
curl -X GET "https://example.com/api/collections/pages/entries" \
-H "Authorization: Bearer a-long-random-string" \
-H "Accept: application/json"
Authenticating users#
If you want to authenticate based on users, we recommend using Laravel Sanctum instead.
To use Sanctum, you'll need to store users in the database and add the auth:sanctum middleware to the REST API's middleware group:
$this->app[\Illuminate\Contracts\Http\Kernel::class]->prependMiddlewareToGroup(config(‘statamic.api.middleware’), ���auth:sanctum’);