Skip to content

API Reference

All API requests require authentication using JWT tokens or API keys.

  1. Navigate to the admin panel at /admin
  2. Go to Settings → API Keys
  3. Click “Generate New Key” and copy the generated key
  4. Include the key in your requests using the Authorization header
Terminal window
Authorization: Bearer your-api-key-here

The REST API follows RESTful conventions and returns JSON responses.

https://your-domain.com/api
GET /api/{model}

Parameters:

  • limit (number): Number of records to return (default: 20, max: 100)
  • offset (number): Number of records to skip (default: 0)
  • sort (string): Field to sort by (prefix with - for descending)
  • filter (object): Filtering conditions
  • populate (string[]): Related fields to include

Example:

Terminal window
curl -X GET "https://your-domain.com/api/blogPost?limit=10&sort=-publishedAt&populate=author,categories" \
-H "Authorization: Bearer your-api-key"

Response:

{
"data": [
{
"id": "1",
"title": "My First Blog Post",
"slug": "my-first-blog-post",
"content": "<p>This is the content...</p>",
"publishedAt": "2024-01-15T10:00:00Z",
"author": {
"id": "1",
"name": "John Doe",
"email": "john@example.com"
},
"categories": [
{
"id": "1",
"name": "Technology",
"slug": "technology"
}
]
}
],
"meta": {
"total": 150,
"limit": 10,
"offset": 0,
"hasMore": true
}
}
GET /api/{model}/{id}

Example:

Terminal window
curl -X GET "https://your-domain.com/api/blogPost/1?populate=author" \
-H "Authorization: Bearer your-api-key"
POST /api/{model}

Example:

Terminal window
curl -X POST "https://your-domain.com/api/blogPost" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"title": "New Blog Post",
"content": "<p>This is the content</p>",
"status": "published"
}'
PUT /api/{model}/{id}
PATCH /api/{model}/{id}

Example:

Terminal window
curl -X PATCH "https://your-domain.com/api/blogPost/1" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"title": "Updated Blog Post Title"
}'
DELETE /api/{model}/{id}

Example:

Terminal window
curl -X DELETE "https://your-domain.com/api/blogPost/1" \
-H "Authorization: Bearer your-api-key"

Use the filter parameter for complex queries:

Terminal window
# Filter by status
GET /api/blogPost?filter[status]=published
# Filter by date range
GET /api/blogPost?filter[publishedAt][$gte]=2024-01-01&filter[publishedAt][$lt]=2024-02-01
# Filter by related field
GET /api/blogPost?filter[author.name]=John Doe
# Multiple conditions
GET /api/blogPost?filter[status]=published&filter[featured]=true
OperatorDescriptionExample
$eqEqual tofilter[status][$eq]=published
$neNot equal tofilter[status][$ne]=draft
$gtGreater thanfilter[views][$gt]=100
$gteGreater than or equalfilter[publishedAt][$gte]=2024-01-01
$ltLess thanfilter[views][$lt]=1000
$lteLess than or equalfilter[publishedAt][$lte]=2024-12-31
$inIn arrayfilter[status][$in]=published,featured
$ninNot in arrayfilter[status][$nin]=draft,archived
$containsContains substringfilter[title][$contains]=tutorial
$startsWithStarts withfilter[slug][$startsWith]=how-to
$endsWithEnds withfilter[slug][$endsWith]=guide

GraphQL endpoint provides a flexible query language for fetching exactly the data you need.

https://your-domain.com/graphql

The GraphQL schema is automatically generated from your content models:

type BlogPost {
id: ID!
title: String!
slug: String!
content: String
excerpt: String
featuredImage: Image
author: User
categories: [Category!]!
tags: [String!]!
publishedAt: DateTime
status: BlogPostStatus!
featured: Boolean!
createdAt: DateTime!
updatedAt: DateTime!
}
type Query {
blogPost(id: ID!): BlogPost
blogPosts(
limit: Int = 20
offset: Int = 0
sort: String
filter: BlogPostFilter
): BlogPostConnection!
}
type Mutation {
createBlogPost(data: CreateBlogPostInput!): BlogPost!
updateBlogPost(id: ID!, data: UpdateBlogPostInput!): BlogPost!
deleteBlogPost(id: ID!): Boolean!
}
query GetBlogPosts {
blogPosts(limit: 10, sort: "-publishedAt") {
nodes {
id
title
slug
excerpt
publishedAt
author {
id
name
avatar
}
categories {
id
name
slug
}
}
pageInfo {
hasNextPage
hasPreviousPage
total
}
}
}
query GetBlogPost($id: ID!) {
blogPost(id: $id) {
id
title
content
publishedAt
author {
name
bio
socialLinks
}
categories {
name
description
}
tags
}
}
mutation CreateBlogPost($data: CreateBlogPostInput!) {
createBlogPost(data: $data) {
id
title
slug
status
createdAt
}
}

Variables:

{
"data": {
"title": "My New Blog Post",
"content": "<p>This is the content</p>",
"status": "PUBLISHED",
"authorId": "1"
}
}
// Using fetch
const response = await fetch('https://your-domain.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-api-key',
},
body: JSON.stringify({
query: `
query GetBlogPosts {
blogPosts(limit: 5) {
nodes {
id
title
publishedAt
}
}
}
`,
}),
});
const data = await response.json();
console.log(data.data.blogPosts.nodes);

Handle file uploads for images, documents, and other media.

POST /api/upload

Form Data:

  • file: The file to upload
  • alt: Alternative text (for images)
  • folder: Folder path (optional)

Example:

Terminal window
curl -X POST "https://your-domain.com/api/upload" \
-H "Authorization: Bearer your-api-key" \
-F "file=@image.jpg" \
-F "alt=Sample image" \
-F "folder=blog/images"

Response:

{
"data": {
"id": "file_123",
"filename": "image.jpg",
"originalName": "my-image.jpg",
"mimeType": "image/jpeg",
"size": 2048576,
"url": "https://your-domain.com/uploads/image.jpg",
"alt": "Sample image",
"folder": "blog/images",
"createdAt": "2024-01-15T10:00:00Z"
}
}
POST /api/upload/multiple

Example:

Terminal window
curl -X POST "https://your-domain.com/api/upload/multiple" \
-H "Authorization: Bearer your-api-key" \
-F "files[]=@image1.jpg" \
-F "files[]=@image2.jpg"

Set up webhooks to receive real-time notifications when content changes.

scalar.config.ts
export default defineConfig({
webhooks: {
enabled: true,
endpoints: [
{
url: 'https://your-app.com/webhook',
events: ['create', 'update', 'delete'],
models: ['blogPost', 'page'],
secret: 'webhook-secret',
},
],
},
});
{
"event": "create",
"model": "blogPost",
"data": {
"id": "1",
"title": "New Blog Post",
"status": "published",
"createdAt": "2024-01-15T10:00:00Z"
},
"timestamp": "2024-01-15T10:00:00Z",
"signature": "sha256=..."
}
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}

API requests are rate-limited to prevent abuse:

  • Default: 100 requests per 15 minutes per IP
  • Authenticated: 1000 requests per 15 minutes per API key

Rate limit headers are included in responses:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642252800
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "title",
"message": "Title is required"
}
]
}
}
CodeStatusDescription
UNAUTHORIZED401Invalid or missing authentication
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
VALIDATION_ERROR422Request validation failed
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Server error