WP Manifestindependent plugin directory
manifest / integrations / create-custom-api-in-wordpress

Custom REST API Builder

WordPress plugin that allows you to create fully customizable REST API endpoints

★ 1stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/dadashzadeh/create-custom-api-in-wordpress/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

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

  1. Download the plugin ZIP file
  2. Go to Plugins → Add New → Upload Plugin
  3. Upload the ZIP file and click Install Now
  4. Activate the plugin

Manual Installation

  1. Download and extract the plugin
  2. Upload the custom-rest-api-builder folder to /wp-content/plugins/
  3. 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

  1. Navigate to REST API Builder in your WordPress admin menu
  2. Click on a post type tab (e.g., "Post", "Page")
  3. Check the fields you want to include in API responses
  4. Drag to reorder fields
  5. Click Save All Settings

2. Generate an API Key

  1. Go to the API Keys tab
  2. Enter a name for your key (e.g., "Mobile App")
  3. Select permissions (Read, Write, Delete)
  4. Click Generate API Key
  5. 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()

# Delete post
response = requests.delete(f'{BASE_URL}/item/123?force=true', headers=headers)
result = response.json()

React Native / Axios

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://yoursite.com/wp-json/custom/v1',
  headers: {
    'X-API-Key': 'crab_YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
});

// Get posts
export const getPosts = (page = 1, perPage = 10) => 
  api.get(`/type/post?page=${page}&per_page=${perPage}`);

// Get single post
export const getPost = (id) => 
  api.get(`/item/${id}`);

// Create post
export const createPost = (fields) => 
  api.post('/type/post', { fields });

// Update post
export const updatePost = (id, fields) => 
  api.put(`/item/${id}`, { fields });

// Delete post
export const deletePost = (id, force = false) => 
  api.delete(`/item/${id}?force=${force}`);

// Search
export const search = (query, postType = 'any') => 
  api.get(`/search?q=${encodeURIComponent(query)}&post_type=${postType}`);

Error Handling

Error Response Format

{
  "code": "error_code",
  "message": "Human readable error message",
  "data": {
    "status": 400
  }
}

Common Error Codes

HTTP Code Error Code Description
400 invalid_fields Invalid or missing fields in request body
400 missing_title Post title is required for creation
401 missing_api_key API key not provided
401 invalid_api_key API key is invalid
403 forbidden No permission for this operation
404 not_found Item or resource not found
404 invalid_post_type Post type does not exist
404 invalid_taxonomy Taxonomy does not exist
500 create_failed Failed to create item
500 update_failed Failed to update item
500 delete_failed Failed to delete item

Hooks & Filters

Available Filters

// Modify fields before saving
add_filter('crab_before_save_settings', function($settings) {
    // Modify $settings
    return $settings;
});

// Modify API response
add_filter('crab_api_response', function($response, $post_type) {
    // Modify $response
    return $response;
}, 10, 2);

// Customize available fields
add_filter('crab_post_type_fields', function($fields, $post_type) {
    // Add custom fields
    $fields['my_custom'] = 'My Custom Field';
    return $fields;
}, 10, 2);

FAQ

Q: Why don't I see all my custom fields?

A: Click the "Clear Meta Fields Cache" button in the Endpoints tab. The plugin caches meta keys for performance. After adding new custom fields, clear the cache to see them.

Q: Does updating a post change the modified date?

A: Yes, WordPress automatically updates post_modified and post_modified_gmt whenever a post is updated via the API.

Q: Can I use this with WooCommerce products?

A: Yes! WooCommerce products are a custom post type (product). Select the fields you need in the Products tab.

Q: Is it compatible with ACF (Advanced Custom Fields)?

A: Yes, the plugin automatically detects ACF fields and shows them with the acf: prefix.

Q: What happens when I deactivate the plugin?

A: All plugin data (settings, API keys) is deleted from the database when deactivated.

Q: How do I update only one field without affecting others?

A: Send only the field you want to update in the fields object. Other fields remain unchanged:

{
  "fields": {
    "meta:price": "99.99"
  }
}

Q: Can I create posts with a future publish date?

A: Yes, use post_status: "future" and set post_date:

{
  "fields": {
    "post_title": "Scheduled Post",
    "post_status": "future",
    "post_date": "2026-12-25 10:00:00"
  }
}

Changelog

1.0.0 (2026-07-20)

  • Initial release
  • Full CRUD support for posts and taxonomies
  • API key authentication with permissions
  • Customizable field selection and ordering
  • Advanced filtering (date, meta, taxonomy)
  • Built-in API tester
  • ACF integration
  • Live preview in admin

License

This plugin is licensed under the GPL-2.0+ license. See LICENSE for details.


Credits

Author: dadashzadeh

Contributors: Community contributions welcome!


Made with ❤️ for the WordPress community