Custom REST API Endpoint Development for WordPress

Custom REST API Endpoint Development for WordPress

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

Custom REST API Endpoint Development for WordPress

For 5+ years, our specialization has been developing custom REST API endpoints for WordPress. A mobile app SPA frontend requires WordPress to deliver aggregated data with filtering by taxonomies and meta fields. The standard /wp/v2/posts cannot output a customer's total orders over a period or a project list with combined sorting. A typical situation: without caching, at 5,000 requests per hour, the server crashes. Developers often resort to direct SQL queries, inviting N+1 problems and security vulnerabilities. Our experience shows that a well-designed custom endpoint solves these tasks in 2–5 days, reducing database load by a factor of 3–5. In this article, we’ll break down typical scenarios, the tech stack, and architecture.

To avoid such issues, we use a unified interface for all data: a custom REST API endpoint aggregates posts, meta fields, and taxonomies in a single request. This cuts HTTP calls by 5–10 times and simplifies frontend maintenance. For example, for one project (a catalog with 20,000 products) we implemented the /my-plugin/v1/products endpoint with filtering by categories, price, and attributes — response time dropped from 4 to 1 second.

Limitations of the Standard WordPress REST API

The default routes /wp/v2/posts and /wp/v2/pages are fine for reading posts, but not for:

  • Aggregated data — total customer orders for the last month.
  • Complex filters — combination of meta fields and taxonomies with sorting.
  • Custom operations — create an order with stock validation and email sending.

Without a custom endpoint, the client has to make multiple requests or use unsafe SQL queries. A custom endpoint with caching allows reducing response time from 4 to 1 second.

Registering a Custom REST API Endpoint

Registering an endpoint with GET and POST methods. More details in REST API Handbook.

add_action('rest_api_init', function () { register_rest_route('my-plugin/v1', '/projects', [ [ 'methods' => WP_REST_Server::READABLE, 'callback' => 'my_plugin_get_projects', 'permission_callback' => '__return_true', 'args' => [ 'category' => [ 'type' => 'string', 'sanitize_callback' => 'sanitize_title', ], 'tech' => [ 'type' => 'array', 'items' => ['type' => 'string'], 'sanitize_callback' => function ($value) { return array_map('sanitize_title', (array) $value); }, ], 'per_page' => [ 'type' => 'integer', 'default' => 12, 'minimum' => 1, 'maximum' => 100, 'sanitize_callback' => 'absint', ], 'page' => [ 'type' => 'integer', 'default' => 1, 'minimum' => 1, 'sanitize_callback' => 'absint', ], ], ], [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => 'my_plugin_create_project', 'permission_callback' => function () { return current_user_can('edit_posts'); }, ], ]); register_rest_route('my-plugin/v1', '/projects/(?P<id>\d+)', [ 'methods' => WP_REST_Server::READABLE, 'callback' => 'my_plugin_get_project', 'permission_callback' => '__return_true', 'args' => [ 'id' => [ 'validate_callback' => function ($param) { return is_numeric($param) && $param > 0; }, ], ], ]); }); 

GET request handler with taxonomy filtering:

function my_plugin_get_projects(WP_REST_Request $request): WP_REST_Response|WP_Error { $per_page = $request->get_param('per_page'); $page = $request->get_param('page'); $category = $request->get_param('category'); $techs = $request->get_param('tech'); $query_args = [ 'post_type' => 'project', 'post_status' => 'publish', 'posts_per_page' => $per_page, 'paged' => $page, 'no_found_rows' => false, ]; $tax_queries = []; if ($category) { $tax_queries[] = [ 'taxonomy' => 'project_category', 'field' => 'slug', 'terms' => $category, ]; } if (!empty($techs)) { $tax_queries[] = [ 'taxonomy' => 'tech_stack', 'field' => 'slug', 'terms' => $techs, 'operator' => 'IN', ]; } if (!empty($tax_queries)) { $query_args['tax_query'] = array_merge(['relation' => 'AND'], $tax_queries); } $query = new WP_Query($query_args); $projects = []; foreach ($query->posts as $post) { $projects[] = my_plugin_format_project($post); } $response = new WP_REST_Response($projects, 200); $response->header('X-WP-Total', $query->found_posts); $response->header('X-WP-TotalPages', $query->max_num_pages); return $response; } function my_plugin_format_project(WP_Post $post): array { $thumbnail_id = get_post_thumbnail_id($post->ID); $thumbnail_url = $thumbnail_id ? wp_get_attachment_image_url($thumbnail_id, 'large') : null; return [ 'id' => $post->ID, 'slug' => $post->post_name, 'title' => wp_strip_all_tags($post->post_title), 'excerpt' => wp_strip_all_tags(get_the_excerpt($post)), 'url' => get_permalink($post->ID), 'thumbnail' => $thumbnail_url, 'client' => get_post_meta($post->ID, 'project_client', true), 'year' => (int) get_post_meta($post->ID, 'project_year', true), 'categories' => wp_get_post_terms($post->ID, 'project_category', ['fields' => 'slugs']), 'tech_stack' => wp_get_post_terms($post->ID, 'tech_stack', ['fields' => 'slugs']), 'modified' => get_post_modified_time('c', true, $post), ]; } 

POST handler with validation:

function my_plugin_create_project(WP_REST_Request $request): WP_REST_Response|WP_Error { $body = $request->get_json_params(); if (empty($body['title'])) { return new WP_Error('missing_title', 'Title is required', ['status' => 422]); } $post_id = wp_insert_post([ 'post_type' => 'project', 'post_title' => sanitize_text_field($body['title']), 'post_content' => wp_kses_post($body['content'] ?? ''), 'post_status' => 'draft', 'post_author' => get_current_user_id(), ], true); if (is_wp_error($post_id)) { return new WP_Error('insert_failed', $post_id->get_error_message(), ['status' => 500]); } if (!empty($body['client'])) { update_post_meta($post_id, 'project_client', sanitize_text_field($body['client'])); } return new WP_REST_Response( ['id' => $post_id, 'url' => get_permalink($post_id)], 201 ); } 

Which Authentication Method to Choose?

For GET endpoints, public access is sufficient. For creating or modifying data, rights checking is needed. Compare the methods:

Method Scenario Complexity
Cookie Requests from the admin area Zero (built-in)
Application Passwords External server-side clients Low (official plugin)
JWT SPAs, mobile apps Medium (plugin or custom code)

Example of intercepting a Bearer token:

add_filter('rest_authentication_errors', function ($result) { if (!empty($result)) return $result; $auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (!str_starts_with($auth_header, 'Bearer ')) { return $result; } $token = substr($auth_header, 7); $user_id = my_plugin_validate_jwt($token); if (is_wp_error($user_id)) { return $user_id; } wp_set_current_user($user_id); return true; }); 

Caching REST API Responses

For heavy requests, we use the Transients API. This reduces DB load by a factor of 3–5. Example:

function my_plugin_get_projects(WP_REST_Request $request): WP_REST_Response { $cache_key = 'projects_' . md5(serialize($request->get_params())); $cached = get_transient($cache_key); if ($cached !== false) { $response = new WP_REST_Response($cached['data'], 200); $response->header('X-WP-Total', $cached['total']); $response->header('X-Cache', 'HIT'); return $response; } // ... main logic ... set_transient($cache_key, ['data' => $projects, 'total' => $total], 5 * MINUTE_IN_SECONDS); return $response; } add_action('save_post_project', function (int $post_id): void { global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_projects_%'"); }); 

What's Included in Endpoint Development

Stage Result
Analysis Determine endpoints, data types, authentication methods
Design Route schema, response structure, parameter validation
Implementation Writing code, registering routes, handlers, caching
Testing Unit tests (PHPUnit), manual testing with curl
Deployment Deploy to production server, set up monitoring
Documentation OpenAPI schema or developer instructions

We guarantee schedule adherence and provide post-release support for 30 days. Contact us for your project assessment — we'll consult on architecture and scope of work. Order custom endpoint development starting from $2,000 and cut integration time by 2x.

Custom REST API Development Workflow

  1. Audit — determine the list of endpoints, methods (GET/POST/PUT/DELETE), and response structures.
  2. Route schema design — versioning (/my-plugin/v1/), arguments, parameter validation.
  3. Handler implementation — write callback functions, format data, handle errors.
  4. Authentication setup — Cookie (for admin), Application Passwords or JWT (for SPA/mobile).
  5. Caching — Transients API or Redis, invalidation on data changes.
  6. Testing with curl and PHPUnit, documentation in OpenAPI format.
How to debug a custom WordPress REST API?

Use curl -X GET https://site.com/wp-json/my-plugin/v1/projects -v for basic checks. Enable WP_DEBUG and WP_DEBUG_LOG in wp-config.php — PHP errors go to debug.log. The Query Monitor plugin shows all SQL queries executed during the endpoint call and helps identify N+1 problems. Check response headers: X-WP-Total should contain the record count, Content-Type: application/json. On a 401 error, ensure permission_callback returns true or correctly checks user permissions.

Why Custom Endpoints Are Better Than Direct SQL Queries

A custom REST API endpoint ensures security (filtering via WP API), caching (Transients/Redis), and versioning. According to our data, switching to custom endpoints cuts integration time by 2x and reduces errors by 60%. We have been developing WordPress solutions for over 5 years, with 30+ projects featuring custom REST APIs. Get a consultation — we'll assess your project and offer the optimal solution. Typical investment ranges from $1,500 to $5,000 per endpoint set, with an average saving of $3,000 per month in server costs after migration.