Service REST API

Custom WordPress REST endpoints, built right.

Authenticated, validated, cached, rate limited, documented. The way the WordPress REST API was meant to be used.

REST APIs shipped in 100+ production WordPress plugins
api.example.com/wp-json/wbcom/v1
register-route.php php
    
      
          
          <?php
        
          
          add_action( 'rest_api_init', function () {
        
          
              register_rest_route( 'wbcom/v1', '/scans/(?P<id>\d+)', [
        
          
                  'methods'  => WP_REST_Server::READABLE,
        
          
                  'callback' => 'wbcom_get_scan',
        
          
                  'args'     => [
        
          
                      'id' => [
        
          
                          'validate_callback' => fn( $v ) => is_numeric( $v ),
        
          
                          'sanitize_callback' => 'absint',
        
          
                      ],
        
          
                  ],
        
          
                  'permission_callback' => function ( WP_REST_Request $req ) {
        
          
                      return current_user_can( 'edit_posts' )
        
          
                          && wp_verify_nonce( $req->get_header( 'X-WP-Nonce' ), 'wp_rest' );
        
          
                  },
        
          
                  'schema' => 'wbcom_scan_schema',
        
          
              ] );
        
          
          } );
        
    
  

Why this matters

Most WordPress REST endpoints in the wild fail a basic security review.

Permission callbacks set to __return_true. No schema validation. Cache invalidation missing. Rate limits unset. Client teams pinging Slack at midnight because the API contract changed without warning. The WordPress REST API is excellent. Most implementations of it are not.

We build endpoints the way WordPress core intends them. Schema first, auth enforced, cached at the right layer, rate limited at the route, tested in CI, and documented before the frontend team starts integrating.

What we build

REST endpoints the frontend team can rely on.

Schema validated, properly authenticated, cached where it matters, rate limited where it counts. PHPUnit tests in CI. OpenAPI docs auto-generated. Versioned so the contract holds.

01

Auth that survives security review

Application passwords for service accounts, JWT for headless frontends, nonce + capability checks for browser calls. Permission callbacks on every route. No public endpoint that should be authenticated.

Endpoints pass enterprise security audits.

02

Schema validation, not duck typing

Every endpoint declares its request schema and response schema. WP_REST_Request validates inputs before they hit the callback. OpenAPI spec generated from the schema for client teams.

Frontend never gets a surprise response shape.

03

Object cache aware

Endpoints use WordPress transients or object cache for expensive queries. Cache keys versioned, invalidation tied to the underlying post or option saves. Redis or Memcached when the host supports it.

API stays fast under traffic spikes.

04

Rate limited at the route

Per IP and per user rate limits, configurable per route. 429 responses with proper Retry-After headers. Optional integration with Cloudflare Rate Limiting at the edge for the routes that need it.

Abuse blocked without breaking real clients.

05

Tested with PHPUnit and Postman

PHPUnit tests with WP_UnitTestCase covering happy path, auth failures, validation errors, and edge cases. Postman or Bruno collections committed to the repo for manual testing and client teams.

Refactor without breaking the contract.

06

Documented for client teams

Auto-generated OpenAPI spec, plus a human-readable README per endpoint group. Examples in curl and JavaScript fetch. Versioning policy documented from day one.

Frontend team integrates without Slack pings.

0 incidents

across our shipped REST endpoints in WordPress.org plugin reviews

100+ plugins shipped, every endpoint passes WordPress.org security review.

Pattern we ship

Controller-style endpoints, the way WordPress core does it.

Extending WP_REST_Controller gives you the right hook points for permissions, schema, and serialization. We follow the same pattern WordPress core uses for posts, users, and comments.

ScansController.php php
    
      
          
          <?php
        
          
          namespace Wbcom\REST;
        
          
           
        
          
          class ScansController extends \WP_REST_Controller {
        
          
              public function register_routes(): void {
        
          
                  register_rest_route( 'wbcom/v1', '/scans', [
        
          
                      [
        
          
                          'methods'  => \WP_REST_Server::CREATABLE,
        
          
                          'callback' => [ $this, 'create_item' ],
        
          
                          'permission_callback' => [ $this, 'create_item_permissions_check' ],
        
          
                          'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
        
          
                      ],
        
          
                      'schema' => [ $this, 'get_public_item_schema' ],
        
          
                  ] );
        
          
              }
        
          
           
        
          
              public function create_item_permissions_check( \WP_REST_Request $request ): bool {
        
          
                  return current_user_can( 'manage_options' );
        
          
              }
        
          
           
        
          
              public function create_item( \WP_REST_Request $request ) {
        
          
                  $params = $request->get_json_params();
        
          
                  $scan_id = wp_insert_post( [
        
          
                      'post_type'   => 'wbcom_scan',
        
          
                      'post_status' => 'pending',
        
          
                      'meta_input'  => [ 'site_url' => esc_url_raw( $params['url'] ) ],
        
          
                  ] );
        
          
           
        
          
                  return rest_ensure_response( $this->prepare_item_for_response( get_post( $scan_id ), $request ) );
        
          
              }
        
          
          }
        
    
  

Process

How a REST API engagement runs.

01

Spec

One week. Endpoint inventory, request and response schemas, auth model, rate limit map, cache strategy. Output is an OpenAPI draft and fixed price quote.

Frontend team can start mocking on day three.

02

Build and test

Two to four weeks. Endpoints written one at a time, PHPUnit tests alongside. Postman or Bruno collection updated as we go. Code review per route.

Every route ships green.

03

Document and hand over

One week. OpenAPI spec finalized, README per endpoint group, examples in curl and JavaScript. Optional client SDK generated.

Client teams onboard without Slack pings.

Common questions

Frequently asked

  1. Why custom REST endpoints instead of GraphQL?

    REST is built into WordPress core. No extra plugin to maintain, no resolver layer to debug, no schema sync issues. For most use cases REST is faster to ship and easier to cache. We build GraphQL when the data shape needs it, not by default.

  2. How do you authenticate REST calls?

    Three patterns. Browser calls use nonce + capability checks. Server-to-server uses application passwords. Headless frontends use JWT. Each has the right permission callback and rate limit. We pick the pattern in the discovery call.

  3. How do you handle versioning?

    Namespace per major version. wbcom/v1, wbcom/v2. Old versions kept live for 12 months minimum after a v2 ships, with deprecation headers. No breaking changes inside a version, ever.

  4. Can you wrap an existing third-party plugin's data in custom endpoints?

    Yes. Common pattern: WooCommerce or BuddyPress data exposed through cleaner, faster, project-specific endpoints. We do not modify the upstream plugin, we layer on top with proper caching and schema validation.

  5. Do you write the client too?

    When asked. We have shipped TypeScript client SDKs and React hooks generated from the OpenAPI spec. Frontend teams consume the API the same way they would consume Stripe or Twilio.

  6. What does it cost?

    A focused REST endpoint suite (5 to 10 routes with auth, caching, tests, docs) are scoped per project. Larger API surfaces with versioning, rate limiting, and client SDK generation are scoped after discovery. Discovery call is free.

Need REST endpoints that hold up?

Tell us what you want to build.

Discovery call is free. Fixed-price quote within 48 hours. Projects are scope-dependent.