API Reference
Complete API reference for Scalar CMS
Authentication
All API requests require authentication using JWT tokens or API keys.
Getting an API Key
/admin
Authorization
headerAuthorization: Bearer your-api-key-here
REST API
The REST API follows RESTful conventions and returns JSON responses.
Base URL
https://your-domain.com/api
Content Operations
Get All Records
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 conditionspopulate
(string[]): Related fields to include
Example:
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 Single Record
GET /api/{model}/{id}
Example:
curl -X GET "https://your-domain.com/api/blogPost/1?populate=author" \
-H "Authorization: Bearer your-api-key"
Create Record
POST /api/{model}
Example:
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"
}'
Update Record
PUT /api/{model}/{id}
PATCH /api/{model}/{id}
Example:
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 Record
DELETE /api/{model}/{id}
Example:
curl -X DELETE "https://your-domain.com/api/blogPost/1" \
-H "Authorization: Bearer your-api-key"
Advanced Filtering
Use the filter
parameter for complex queries:
# 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
Filtering Operators
Operator | Description | Example |
---|---|---|
$eq | Equal to | filter[status][$eq]=published |
$ne | Not equal to | filter[status][$ne]=draft |
$gt | Greater than | filter[views][$gt]=100 |
$gte | Greater than or equal | filter[publishedAt][$gte]=2024-01-01 |
$lt | Less than | filter[views][$lt]=1000 |
$lte | Less than or equal | filter[publishedAt][$lte]=2024-12-31 |
$in | In array | filter[status][$in]=published,featured |
$nin | Not in array | filter[status][$nin]=draft,archived |
$contains | Contains substring | filter[title][$contains]=tutorial |
$startsWith | Starts with | filter[slug][$startsWith]=how-to |
$endsWith | Ends with | filter[slug][$endsWith]=guide |
GraphQL API
GraphQL endpoint provides a flexible query language for fetching exactly the data you need.
Endpoint
https://your-domain.com/graphql
Schema
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 Examples
Fetch Blog Posts
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
}
}
}
Fetch Single Post
query GetBlogPost($id: ID!) {
blogPost(id: $id) {
id
title
content
publishedAt
author {
name
bio
socialLinks
}
categories {
name
description
}
tags
}
}
Create New Post
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 with Different Clients
// 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);
import requests
url = 'https://your-domain.com/graphql'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-api-key',
}
query = """
query GetBlogPosts {
blogPosts(limit: 5) {
nodes {
id
title
publishedAt
}
}
}
"""
response = requests.post(url, json={'query': query}, headers=headers)
data = response.json()
print(data['data']['blogPosts']['nodes'])
<?php
$url = 'https://your-domain.com/graphql';
$headers = [
'Content-Type: application/json',
'Authorization: Bearer your-api-key',
];
$query = '
query GetBlogPosts {
blogPosts(limit: 5) {
nodes {
id
title
publishedAt
}
}
}
';
$data = json_encode(['query' => $query]);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
curl_close($curl);
$result = json_decode($response, true);
print_r($result['data']['blogPosts']['nodes']);
?>
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type GraphQLRequest struct {
Query string `json:"query"`
}
func main() {
url := "https://your-domain.com/graphql"
query := `
query GetBlogPosts {
blogPosts(limit: 5) {
nodes {
id
title
publishedAt
}
}
}
`
reqBody := GraphQLRequest{Query: query}
jsonData, _ := json.Marshal(reqBody)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer your-api-key")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["data"])
}
File Upload API
Handle file uploads for images, documents, and other media.
Upload Single File
POST /api/upload
Form Data:
file
: The file to uploadalt
: Alternative text (for images)folder
: Folder path (optional)
Example:
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"
}
}
Upload Multiple Files
POST /api/upload/multiple
Example:
curl -X POST "https://your-domain.com/api/upload/multiple" \
-H "Authorization: Bearer your-api-key" \
-F "files[]=@image1.jpg" \
-F "files[]=@image2.jpg"
Webhooks
Set up webhooks to receive real-time notifications when content changes.
Configuration
export default defineConfig({
webhooks: {
enabled: true,
endpoints: [
{
url: 'https://your-app.com/webhook',
events: ['create', 'update', 'delete'],
models: ['blogPost', 'page'],
secret: 'webhook-secret',
},
],
},
});
Webhook Payload
{
"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=..."
}
Verifying Webhooks
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}
Rate Limiting
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 Handling
Error Response Format
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "title",
"message": "Title is required"
}
]
}
}
Common Error Codes
Code | Status | Description |
---|---|---|
UNAUTHORIZED | 401 | Invalid or missing authentication |
FORBIDDEN | 403 | Insufficient permissions |
NOT_FOUND | 404 | Resource not found |
VALIDATION_ERROR | 422 | Request validation failed |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Server error |
For more advanced use cases, check out our SDK documentation and examples.