Skip to main content

REST API Overview

Benecaster exposes a REST API under the benecaster/v1 namespace on your WordPress site. These endpoints power the Benecaster admin UI and are also available for custom integrations — syncing subscribers to a CRM, building donor boards, triggering cache clears from external code, and similar tasks.

All endpoints live at:

https://yoursite.com/wp-json/benecaster/v1/

Authentication

Endpoints fall into two categories:

Public endpoints — no authentication required. Used for cross-site callbacks and listener-facing requests (listener account actions, donation submissions, Stripe payment intents). Public endpoints are rate-limited per IP to prevent abuse.

Protected endpoints — require manage_options capability. Used for all admin data reads and writes.

Integration actions — GET and POST /actions, the one route an outside service such as Zapier, Make or a CRM calls to tell an add-on that something happened. They require the benecaster_run_integration_actions capability, which the Benecaster Integration role holds (administrators hold it too). See Connecting Zapier, Make or a CRM.

Browser requests (nonce-based)

The Benecaster admin UI sends requests using WordPress cookie authentication. The nonce is included in the X-WP-Nonce header:

X-WP-Nonce: {nonce}

The nonce is generated server-side and exposed to JavaScript via wp_localize_script. This is handled automatically — you do not need to configure it unless you are building a custom admin extension.

Automation and server-to-server requests (Application Passwords)

Connecting Zapier, Make or a CRM to an add-on? Do not use an administrator for that. The integration actions endpoint, POST /wp-json/benecaster/v1/actions, is meant for a dedicated user with the Benecaster Integration role. That role can run integration actions and nothing else: it gets a 403 from every manage_options route, so a leaked password cannot reach your settings or subscribers. Connecting Zapier, Make or a CRM walks through it.

The steps below are for scripts and services that call the protected admin endpoints outside of the browser session. Those still need an administrator:

  1. Create a dedicated WordPress administrator account for API access. Do not use your own admin credentials — if the integration is compromised, a dedicated account can be revoked without locking yourself out.
  2. Go to Users → [dedicated user] → Application Passwords. Generate a new password and label it with the integration name (e.g. “Zapier sync”, “CRM export”).
  3. Use HTTP Basic Auth with your WordPress username and the generated Application Password:
Authorization: Basic base64(username:application-password)

The dedicated account must have the Administrator role — the protected endpoints require manage_options, which is an Administrator-only capability in a standard WordPress installation.

Revoke Application Passwords immediately when an integration is decommissioned. Each password is independent — revoking one does not affect others.

Rate Limiting

All benecaster/v1 endpoints are rate-limited per IP address. Exceeding a limit returns HTTP 429 with a Retry-After header indicating when you may retry:

{
  "code":    "benecaster_rate_limited",
  "message": "Too many requests. Please wait before retrying.",
  "data":    { "status": 429 }
}

Default limit: 300 requests per 5 minutes.

Tighter limits on specific endpoints:

Endpoint Limit
POST /account/reset-token 5 per hour (per user)
GET /account/qr-code 30 per 5 minutes
POST /shows/{id}/sync 10 per hour
POST /listener-support/donations 30 per 5 minutes
POST /shows/{id}/listener-support/intent 10 per 5 minutes
POST /shows/{id}/subscribe 10 per 5 minutes
POST /shows/{id}/subscribe/tax-preview 60 per 5 minutes
POST /actions 120 per 5 minutes (per calling user, since services such as Zapier call from shared addresses)

The donation intent route is the tightest of these because it is public and mints a Stripe PaymentIntent on the podcaster’s own account. Its companion GET /shows/{id}/listener-support/config stays on the default limit deliberately — it reads already-public configuration, and throttling it would break a page rendering several donation forms. See Listener Support Donation Intent REST API.

Developers can adjust limits using the benecaster_rest_rate_limit_buckets filter, or exempt specific integrations using benecaster_rest_rate_limit_skip.

Error Responses

All error responses follow the standard WordPress REST API format:

{
  "code":    "benecaster_error_slug",
  "message": "Human-readable description.",
  "data":    { "status": 400 }
}

Common HTTP status codes:

Code Meaning
400 Invalid or missing request parameter
401 Authentication required
403 Authenticated but insufficient capability
404 Resource not found
409 Conflict (e.g. a job is already running for this show)
429 Rate limit exceeded
500 Server error

Endpoint Groups

Group Docs Auth
Notices Notices REST API Protected
Episodes Episodes REST API Protected
Episode Categories Episode Categories REST API Protected
References References REST API Protected
Field Groups Field Groups REST API Protected
Bridge Levels Bridge Levels REST API Protected
Setup Wizard Setup Wizard REST API Protected
Email Queue Email Queue REST API Protected
Broadcasts Broadcasts REST API Protected
Account (subscriber-facing) Account REST API Mixed
Preview as Tier Preview as Tier REST API Protected
Listener Support Donations Listener Support Donations REST API Mixed
Listener Support Settings Listener Support Settings REST API Protected
Staging Staging REST API Protected
Support Mode Diagnostic Log Support Mode Diagnostic Log REST API Protected
Supporter Wall Supporter Wall REST API Protected
Tours Tours REST API Protected
Buy-ups (admin) Buy-ups Admin REST API Protected
Buy-ups (subscriber) Buy-ups Subscriber REST API Mixed
Bulk Enroll Bulk Enroll REST API Protected
Promote to Bridge Promote to Bridge REST API Protected
Follower Tier Follower Tier REST API Protected
Integration Actions Connecting Zapier, Make or a CRM Benecaster Integration role
Editor Pickers Editor Pickers below Editor (edit_posts)
Migration Wizard (add-on) Migration Wizard REST API — own namespace, benecaster-migration-wizard/v1 Protected

Shows. GET /shows/{id} and PUT /shows/{id} have no dedicated reference page yet; their fields are documented individually alongside the settings that expose them. Both carry enclosure_url_prefix (string) — the show’s Enclosure URL prefix (Show Settings → Feeds), '' when unset. On PUT, '' turns it off; null or an absent key leaves the stored value unchanged. See Show Settings Reference.


Editor Pickers

Four read-only routes that list what a block or widget can point at: shows, a show’s tiers, its seasons and its episodes. They need the edit_posts capability and the wp_rest nonce, so an Editor or Author building a page gets an answer. The other routes that list shows, tiers and episodes need manage_options, and an Author gets a 403 from them — which in a picker looks like a site with no shows.

These are pickers, not a general API. Each returns ids, names, slugs, season numbers and an episode’s status only, and the payloads will not grow on request: no subscriber counts, prices or feed URLs. The reason is concrete. The admin tier route reports each tier’s subscriber count, so any field added here would be shown to every Author on the site. If you need more, that is a manage_options route or a front-end shortcode.

Route Returns
GET /editor/shows { "shows": [ { "id", "title", "slug" } ] }, ordered by title. Draft shows are included, because pages are often built before a show goes live; archived shows are not.
GET /editor/shows/{id}/tiers { "tiers": [ { "slug", "name" } ] }, in the show’s tier order.
GET /editor/shows/{id}/seasons { "seasons": [ { "number", "display_name" } ] }, ascending, from published episodes.
GET /editor/shows/{id}/episodes { "episodes": [ { "id", "title", "status" } ], "total", "total_pages", "page" }, newest first. Takes search, page and per_page (default 20, at most 100). Published and scheduled episodes only — a block pointing at a draft renders nothing on the public page. status is WordPress’s own post status, publish or future: label a future episode as scheduled in your picker, because a block pointing at one renders blank for visitors until it goes out. It is the raw status, so an episode whose time has passed but whose publish cron has not run yet is still future, and still hidden.

The three per-show routes return 404 when the id is not a show.

Rank tiers by their position in the array. The list is already in the show’s tier order, and there is deliberately no tier_order field: the stored value is 0 for every tier created in the Memberships admin, so sorting by it ranks nothing.

An empty tier list is a normal answer. The tier route answers for shows using Benecaster’s built-in memberships and for shows connected to a membership plugin alike. A show on neither returns [], not an error — render an empty state.

From PHP, benecaster_get_show_tiers() gives the same tier answer without a request. See Populate a block or widget picker without needing an administrator.


Adding Your Own Endpoints (Add-on Authors)

Register your routes with WordPress directly and borrow Benecaster’s admin permission callback:

add_action( 'rest_api_init', function (): void {
    register_rest_route( 'my-addon/v1', '/my-resource', [
        'methods'             => 'GET',
        'callback'            => 'my_addon_get_resource',
        'permission_callback' => 'benecaster_rest_permission_admin',
    ] );
} );

benecaster_rest_permission_admin() checks manage_options and then a valid wp_rest nonce, exactly as Benecaster’s own protected endpoints do — the two call the same implementation, so they cannot drift apart. Pass the function name and nothing else: WordPress hands the request to a permission_callback for you.

Use your own namespace, not benecaster/v1. my-addon/v1 keeps your endpoints distinguishable from Benecaster’s in the route list — which matters to you when a Benecaster update changes one of ours, and to us when we are deciding whether a path is safe to change.

Do not extend Benecaster\REST\RestController. Extending a base class is the most brittle coupling available: every protected method, property and constructor signature silently becomes API that can never change. The permission check was the only part an add-on ever needed from it, and it is now a plain function. Code that extends the class still runs today, which is exactly why this is easy to leave in place — but it is not supported and will not be kept working.

This covers the admin check only. There is no public function for the editor (edit_posts) or subscriber (logged-in) permission callbacks — a deliberate narrowing, not an omission to work around. If you need one, raise it rather than reaching for the base class.

Rate limiting does not apply to your routes. The per-IP limiter described above runs on benecaster/v1 paths. Routes in your own namespace are outside it, so apply your own throttling to anything a listener can reach unauthenticated.

Worked example: Add a custom REST endpoint from your add-on.

See Also

Need this built rather than just documented? See our services →