Custom REST API Builder
WordPress plugin that allows you to create fully customizable REST API endpoints
by dadashzadeh · github.com/dadashzadeh/create-custom-api-in-wordpress · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/dadashzadeh/create-custom-api-in-wordpress/archive/refs/heads/main.zipReadme
Custom REST API Builder
A powerful WordPress plugin that allows you to create fully customizable REST API endpoints for your posts, custom post types, and taxonomies with complete CRUD operations and API key authentication.
Description
Custom REST API Builder (CRAB) gives you complete control over your WordPress REST API. Unlike the default WordPress REST API, this plugin lets you choose exactly which fields to expose, in what order, and with granular permission controls.
Key Features
- 🔧 Fully Customizable Fields - Select and reorder exactly which fields appear in your API responses
- 🔐 API Key Authentication - Secure your endpoints with API keys and granular permissions
- 📝 Full CRUD Operations - Create, Read, Update, and Delete posts and terms via API
- 🎯 Custom Post Type Support - Works with all public post types and taxonomies
- 🔍 Advanced Filtering - Filter by date, meta fields, taxonomies, and search
- ⚡ ACF Compatible - Full support for Advanced Custom Fields
- 🧪 Built-in API Tester - Test your endpoints directly from the admin panel
- 📱 Mobile App Ready - Perfect for headless WordPress and mobile applications
Installation
From WordPress Admin
- Download the plugin ZIP file
- Go to Plugins → Add New → Upload Plugin
- Upload the ZIP file and click Install Now
- Activate the plugin
Manual Installation
- Download and extract the plugin
- Upload the
custom-rest-api-builderfolder to/wp-content/plugins/ - Activate the plugin through the Plugins menu in WordPress
Requirements
- WordPress 4.5 or higher
- PHP 5.6 or higher
Quick Start
1. Configure Your Fields
- Navigate to REST API Builder in your WordPress admin menu
- Click on a post type tab (e.g., "Post", "Page")
- Check the fields you want to include in API responses
- Drag to reorder fields
- Click Save All Settings
2. Generate an API Key
- Go to the API Keys tab
- Enter a name for your key (e.g., "Mobile App")
- Select permissions (Read, Write, Delete)
- Click Generate API Key
- Copy and save the key immediately - it won't be shown again!
3. Make Your First Request
curl -H "X-API-Key: crab_YOUR_API_KEY" \
https://yoursite.com/wp-json/custom/v1/type/post
API Endpoints
Base URL
https://yoursite.com/wp-json/custom/v1
Posts & Custom Post Types
| Method | Endpoint | Description |
|---|---|---|
GET |
/type/{post_type} |
List all items |
POST |
/type/{post_type} |
Create new item |
GET |
/item/{id} |
Get single item |
PUT |
/item/{id} |
Update item |
DELETE |
/item/{id} |
Delete item |
GET |
/search |
Search content |
Taxonomies
| Method | Endpoint | Description |
|---|---|---|
GET |
/taxonomy/{taxonomy} |
List all terms |
POST |
/taxonomy/{taxonomy} |
Create new term |
GET |
/term/{id} |
Get single term |
PUT |
/term/{id} |
Update term |
DELETE |
/term/{id} |
Delete term |
Authentication
API Key Methods
Method 1: HTTP Header (Recommended)
curl -H "X-API-Key: crab_YOUR_API_KEY" \
https://yoursite.com/wp-json/custom/v1/type/post
Method 2: Query Parameter
https://yoursite.com/wp-json/custom/v1/type/post?api_key=crab_YOUR_API_KEY
Permission Levels
| Permission | Operations |
|---|---|
| Read | GET requests |
| Write | POST, PUT, PATCH requests |
| Delete | DELETE requests |
Authentication Settings
By default, GET requests are public. Enable "Require API Key for Read" in the API Keys tab to require authentication for all requests.
Query Parameters
Collection Endpoints (/type/{post_type})
| Parameter | Type | Default | Description |
|---|---|---|---|
per_page |
integer | 10 | Items per page (max 100) |
page |
integer | 1 | Page number |
search |
string | - | Search keyword |
orderby |
string | date | Order by field |
order |
string | DESC | Sort order (ASC/DESC) |
date_after |
string | - | Filter posts after date (YYYY-MM-DD) |
date_before |
string | - | Filter posts before date (YYYY-MM-DD) |
meta_key |
string | - | Meta key to filter by |
meta_value |
string | - | Meta value to match |
meta_compare |
string | = | Comparison operator |
tax_query |
JSON | - | Taxonomy filter |
Taxonomy Endpoints (/taxonomy/{taxonomy})
| Parameter | Type | Default | Description |
|---|---|---|---|
per_page |
integer | 100 | Terms per page |
page |
integer | 1 | Page number |
hide_empty |
boolean | false | Hide terms with no posts |
parent |
integer | - | Filter by parent term ID |
search |
string | - | Search terms |
Search Endpoint (/search)
| Parameter | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | Search query |
post_type |
string | No | Limit to specific post type |
per_page |
integer | No | Results per page |
page |
integer | No | Page number |
Request & Response Examples
Get All Posts
Request:
GET /wp-json/custom/v1/type/post?per_page=10&page=1
Response:
{
"success": true,
"post_type": "post",
"total": 42,
"total_pages": 5,
"current_page": 1,
"per_page": 10,
"count": 10,
"items": [
{
"id": 123,
"title": "Hello World",
"slug": "hello-world",
"date": "2026-07-20 10:30:00",
"excerpt": "Welcome to WordPress...",
"content": "<p>Welcome to WordPress...</p>"
}
]
}
Get Single Item
Request:
GET /wp-json/custom/v1/item/123
Response:
{
"success": true,
"item": {
"id": 123,
"title": "Hello World",
"slug": "hello-world",
"date": "2026-07-20 10:30:00",
"modified": "2026-07-20 11:00:00",
"content": "<p>Welcome to WordPress...</p>",
"featured_image": "https://yoursite.com/wp-content/uploads/image.jpg",
"meta:custom_field": "Custom value"
}
}
Create New Post
Request:
POST /wp-json/custom/v1/type/post
Content-Type: application/json
X-API-Key: crab_YOUR_API_KEY
{
"fields": {
"post_title": "My New Post",
"post_content": "<p>This is the content.</p>",
"post_status": "publish",
"post_excerpt": "A short summary",
"meta:custom_field": "Custom value",
"taxonomies": {
"category": [1, 5],
"post_tag": [10, 15]
},
"featured_image": 456
}
}
Response:
{
"success": true,
"message": "Item created successfully.",
"id": 789,
"item": {
"id": 789,
"title": "My New Post",
"slug": "my-new-post",
"date": "2026-07-20 11:43:00"
}
}
Update Existing Post
Request:
PUT /wp-json/custom/v1/item/123
Content-Type: application/json
X-API-Key: crab_YOUR_API_KEY
{
"fields": {
"post_title": "Updated Title",
"meta:price": "99.99"
}
}
Response:
{
"success": true,
"message": "Item updated successfully.",
"id": 123,
"updated_fields": ["post_title", "meta:price"],
"item": {
"id": 123,
"title": "Updated Title",
"modified": "2026-07-20 11:43:00",
"meta:price": "99.99"
}
}
Note: Only the fields you send will be updated. All other fields remain unchanged.
Delete Post
Request:
DELETE /wp-json/custom/v1/item/123?force=true
X-API-Key: crab_YOUR_API_KEY
Response:
{
"success": true,
"message": "Item permanently deleted.",
"id": 123
}
| Parameter | Description |
|---|---|
force=false |
Move to trash (default) |
force=true |
Permanently delete |
Advanced Filtering
Filter by Date
GET /wp-json/custom/v1/type/post?date_after=2026-01-01&date_before=2026-12-31
Filter by Meta Field
GET /wp-json/custom/v1/type/product?meta_key=_price&meta_value=100&meta_compare=>=
Available Comparisons: =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN
Filter by Taxonomy
GET /wp-json/custom/v1/type/post?tax_query=[{"taxonomy":"category","terms":[5,10],"operator":"IN"}]
Tax Query Structure:
[
{
"taxonomy": "category",
"field": "term_id",
"terms": [5, 10],
"operator": "IN"
}
]
Available Fields
Post Fields
| Field Key | Description |
|---|---|
id |
Post ID |
title |
Post title |
slug |
URL slug |
date |
Publish date |
date_gmt |
Publish date (GMT) |
modified |
Last modified date |
modified_gmt |
Last modified date (GMT) |
status |
Post status |
type |
Post type |
excerpt |
Post excerpt |
content |
Rendered content |
content_raw |
Raw content (no filters) |
author_id |
Author user ID |
author_name |
Author display name |
author_email |
Author email |
author_avatar |
Author avatar URL |
featured_image |
Featured image URL |
featured_image_id |
Featured image attachment ID |
featured_image_alt |
Featured image alt text |
featured_image_sizes |
All image sizes with URLs |
permalink |
Full post URL |
parent |
Parent post ID |
menu_order |
Menu order |
comment_count |
Number of comments |
comment_status |
Comment status |
ping_status |
Ping status |
guid |
Global unique identifier |
tax:{taxonomy} |
Taxonomy terms |
meta:{key} |
Custom meta field |
acf:{field} |
ACF field (if ACF is active) |
Taxonomy Term Fields
| Field Key | Description |
|---|---|
term_id |
Term ID |
name |
Term name |
slug |
Term slug |
description |
Term description |
count |
Post count |
parent |
Parent term ID |
taxonomy |
Taxonomy name |
link |
Term archive URL |
meta:{key} |
Term meta field |
acf:{field} |
ACF field for term |
Writable Fields
| Field Key | Description |
|---|---|
post_title |
Post title |
post_content |
Post content |
post_excerpt |
Post excerpt |
post_name |
Post slug |
post_status |
Status (publish, draft, pending, private) |
post_parent |
Parent post ID |
menu_order |
Menu order |
post_date |
Publish date |
meta:{key} |
Any meta field |
taxonomies |
Object with taxonomy => term IDs |
featured_image |
Attachment ID |
Code Examples
JavaScript (Fetch)
// Get posts
const getPosts = async () => {
const response = await fetch('https://yoursite.com/wp-json/custom/v1/type/post', {
headers: {
'X-API-Key': 'crab_YOUR_API_KEY'
}
});
return response.json();
};
// Create post
const createPost = async (title, content) => {
const response = await fetch('https://yoursite.com/wp-json/custom/v1/type/post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'crab_YOUR_API_KEY'
},
body: JSON.stringify({
fields: {
post_title: title,
post_content: content,
post_status: 'publish'
}
})
});
return response.json();
};
// Update post
const updatePost = async (id, fields) => {
const response = await fetch(`https://yoursite.com/wp-json/custom/v1/item/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'crab_YOUR_API_KEY'
},
body: JSON.stringify({ fields })
});
return response.json();
};
// Delete post
const deletePost = async (id, force = false) => {
const response = await fetch(`https://yoursite.com/wp-json/custom/v1/item/${id}?force=${force}`, {
method: 'DELETE',
headers: {
'X-API-Key': 'crab_YOUR_API_KEY'
}
});
return response.json();
};
PHP (WordPress)
<?php
$api_key = 'crab_YOUR_API_KEY';
$base_url = 'https://yoursite.com/wp-json/custom/v1';
// Get posts
$response = wp_remote_get($base_url . '/type/post', array(
'headers' => array(
'X-API-Key' => $api_key
)
));
$posts = json_decode(wp_remote_retrieve_body($response), true);
// Create post
$response = wp_remote_post($base_url . '/type/post', array(
'headers' => array(
'Content-Type' => 'application/json',
'X-API-Key' => $api_key
),
'body' => json_encode(array(
'fields' => array(
'post_title' => 'New Post',
'post_content' => 'Content here',
'post_status' => 'publish'
)
))
));
// Update post
$response = wp_remote_request($base_url . '/item/123', array(
'method' => 'PUT',
'headers' => array(
'Content-Type' => 'application/json',
'X-API-Key' => $api_key
),
'body' => json_encode(array(
'fields' => array(
'post_title' => 'Updated Title'
)
))
));
// Delete post
$response = wp_remote_request($base_url . '/item/123?force=true', array(
'method' => 'DELETE',
'headers' => array(
'X-API-Key' => $api_key
)
));
Python (Requests)
import requests
import json
API_KEY = 'crab_YOUR_API_KEY'
BASE_URL = 'https://yoursite.com/wp-json/custom/v1'
headers = {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
}
# Get posts
response = requests.get(f'{BASE_URL}/type/post', headers=headers)
posts = response.json()
# Create post
data = {
'fields': {
'post_title': 'New Post from Python',
'post_content': 'Content here',
'post_status': 'publish'
}
}
response = requests.post(f'{BASE_URL}/type/post', headers=headers, json=data)
result = response.json()
# Update post
data = {
'fields': {
'post_title': 'Updated Title'
}
}
response = requests.put(f'{BASE_URL}/item/123', headers=headers, json=data)
result = response.json()