PolyCMS REST API Guide
REST API Overview
PolyCMS includes a robust REST API, allowing you to use it as a headless CMS. You can fetch content to display on external platforms, mobile apps, or separate frontend frameworks.
The API provides full CRUD capabilities for Posts, Pages, Categories, Tags, and Media. To test endpoints interactively without any external tools, use the built-in API Explorer.
Live Demo: Try the API Explorer on the official demo site at polycms.polyxgo.com/admin/polycms/api_explorer.
Authentication
All API requests must be authenticated using an API Key.
To enable the API and generate a key:
- Navigate to Blog > Settings.
- Check the Enable REST API box.
- Check the Generate New API Key box and save settings.
- Copy the generated API key.
Pass the API key in your requests using either:
- HTTP Header:
X-PolyCMS-API-Key: pcms_your_key_here - Query Parameter:
?api_key=pcms_your_key_here
Rate Limiting
The API includes configurable rate limiting to protect your server from abuse. The limits are tracked per IP address.
By default:
- Read Operations (GET): 60 requests per minute.
- Write Operations (POST, PUT, DELETE): 30 requests per minute.
These limits can be adjusted in Blog > Settings. The API responds with standard rate-limiting headers:
X-RateLimit-LimitX-RateLimit-RemainingRetry-After
If limits are exceeded, you will receive a 429 Too Many Requests HTTP status code.
Endpoints
All endpoints are prefixed with /cms/api/v1/.
Posts
List Posts:
GET /cms/api/v1/posts
Query Parameters:
page(default: 1)per_page(default: 10, max: 100)status(e.g., published, draft)category_idauthor_idsearchsort_by(default: created_at)sort_order(default: DESC)
Get Single Post:
GET /cms/api/v1/posts/{id}
Create Post:
POST /cms/api/v1/posts
Request Body (JSON):
{
"title": "My New Post",
"content": "This is the content...",
"status": "published",
"category_ids": [1, 2]
}
Update Post:
PUT /cms/api/v1/posts/{id}
Delete Post:
DELETE /cms/api/v1/posts/{id}
Pages
List Pages:
GET /cms/api/v1/pages
Get Single Page:
GET /cms/api/v1/pages/{id}
Create / Update / Delete Pages:
POST /cms/api/v1/pages | PUT /cms/api/v1/pages/{id} | DELETE /cms/api/v1/pages/{id} |
|---|
Categories
List Categories (Returns Tree Structure):
GET /cms/api/v1/categories
Full CRUD:
POST /cms/api/v1/categories | PUT /cms/api/v1/categories/{id} | DELETE /cms/api/v1/categories/{id} |
|---|
Tags
List Tags:
GET /cms/api/v1/tags
Full CRUD:
POST /cms/api/v1/tags | PUT /cms/api/v1/tags/{id} | DELETE /cms/api/v1/tags/{id} |
|---|
Media
List Media (Database-backed, Paginated):
GET /cms/api/v1/media
Query Parameters:
page(default: 1)per_page(default: 20, max: 100)searchβ Search by filename, alt_text, or titlemimeβ Filter by MIME type prefix (e.g.,image)
All media are stored in the blog_media database table with metadata: filename, path, URL, alt_text, title, caption, MIME type, file_size, width, height, and author.
Upload Media:
POST /cms/api/v1/media
Upload images via multipart/form-data with the field name file. The API enforces 6 layers of security:
- Extension whitelist
- MIME validation
- Magic bytes verification
- Malicious code scanning
- Double-extension blocking
- GD image reprocessing to strip embedded payloads
On success, the response includes a media_id for linking to posts/pages via feature_image_id.
Delete Media:
DELETE /cms/api/v1/media/{path}
Performs a 3-layer cleanup:
- Physical file removal β deletes the original file and its thumbnail
- Database record deletion β removes entry from
blog_media - Reference cascade β clears
feature_imageandfeature_image_idfrom any posts/pages that referenced this media
Media Database Architecture
New in v1.1.0: All uploaded media are tracked in the blog_media table.
Feature Image Relationship
Posts and Pages now have a feature_image_id column linking to blog_media.id. The original feature_image path string is retained for fast rendering without JOIN queries.
Auto-Sync Migration
When the module is activated (or reactivated), existing files are automatically synced into the database and feature_image_id is populated for matching posts. This migration is idempotent β safe to run multiple times.
Response Format
All successful API responses return a standard JSON structure:
{
"success": true,
"message": "Posts retrieved",
"data": [
{
"id": 1,
"title": "Hello World",
...
}
],
"meta": {
"total": 1,
"page": 1,
"per_page": 10,
"pages": 1
}
}
Errors return success: false with an appropriate message and HTTP status code.
Security & Data Isolation
Data Scope β CMS Tables Only
The REST API operates exclusively on PolyCMS data tables. It has zero access to any Perfex CRM core data (clients, invoices, projects, leads, staff passwords, etc.).
All API endpoints interact only with the following dedicated PolyCMS tables:
blog_posts/blog_post_meta/blog_post_tagsblog_pages/blog_page_metablog_categoriesblog_tagsblog_media
The only CRM table referenced is staff (read-only, first/last name only) to display author names. No sensitive CRM data is ever exposed or writable through the API.
Authentication
- API Key Required: Every request must include a valid API key via
X-PolyCMS-API-KeyHTTP header or?api_key=query parameter. - Constant-Time Comparison: API key verification uses PHP
hash_equals()to prevent timing attacks β the comparison takes the same amount of time regardless of how many characters match. - Cryptographic Key Generation: API keys are generated using
random_bytes(24)(48 hex characters), prefixed withpcms_, providing 192 bits of entropy. - No Key = No Access: If no API key has been generated in Settings, all API requests are rejected.
SQL Injection Prevention
- Zero Raw SQL: All database queries use CodeIgniter Active Record / Query Builder, which automatically escapes all values.
- Parameter Binding: Filter values (status, category_id, author_id, search) are passed through CI's built-in escaping layer β never concatenated into query strings.
- Integer Casting: All numeric parameters (IDs, pagination) are cast to
(int)before use. - Whitelist Sorting: Sort columns are restricted by the model layer.
XSS & Content Injection Prevention
- HTMLPurifier: All HTML content (post body, descriptions) submitted via API is processed through
polycms_purify_content(), which uses:
- Perfex CRM's
html_purify()powered by HTMLPurifier library - Additional regex stripping of
<script>tags - Removal of inline JavaScript event handlers (
onclick,onerror, etc.)
- strip_tags(): All text-only fields (title, slug, status, visibility) are sanitized with
strip_tags()β no HTML allowed. - JSON Output Encoding: All responses use
json_encode()withJSON_UNESCAPED_UNICODE, preventing output injection.
Additional Security Measures
- Global Kill Switch: The entire API can be disabled instantly via Settings toggle β all endpoints return
403 Forbiddenwhen disabled. - BASEPATH Protection: The controller file includes
defined('BASEPATH') or exit()to prevent direct script execution. - 6-Layer Media Upload Security: Extension whitelist, MIME validation, magic bytes, code scanning, double-extension blocking, GD reprocessing.
- No Admin Operations: The API cannot modify settings, permissions, themes, plugins, or any system configuration.
Related Documentation
- API Explorer β Interactive REST API Sandbox β Test all endpoints interactively from your admin panel.
- Building a Custom Plugin β Create plugins that leverage the REST API.
- Hooks & Filters Reference β Extend API behavior with custom hooks.
- Media Library Guide β Understanding the media system and
blog_mediatable. - Architecture Overview β PolyCMS file structure, database schema, and request lifecycle.
Launch Your CMS for Perfex CRM β Lifetime Access for Just $19
Create blogs, pages, themes, plugins, and manage everything from one powerful unified admin panel.
Early Adopter Advantage
Current pricing reflects the moduleβs present feature set. As additional plugins, themes, multilingual capabilities, builders (MTBuilder), and ecosystem integrations are released, pricing may be adjusted. Early customers secure todayβs price while benefiting from future updates.