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.
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)
For scripts, integrations, and external services that call protected endpoints outside of the browser session:
- 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.
- Go to Users → [dedicated user] → Application Passwords. Generate a new password and label it with the integration name (e.g. “Zapier sync”, “CRM export”).
- 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 |
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 |
|---|---|---|
| License | License REST API | Protected |
| 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 |
| License Push | License Push REST API | Protected |
| Follower Tier | Follower Tier REST API | Protected |
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 BenecasterRESTRestController. 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.