You hit an endpoint like /wp-json/wp/v2/posts expecting JSON and instead your browser or curl throws back Unexpected token '<', ". Sometimes it's blank. Sometimes it's a PHP warning shoved in front of a perfectly good JSON payload. The cause is almost always something writing output to the response before WordPress can send its headers — a broken plugin, a theme's functions.php, or a permalink structure that never got flushed.
None of the five fixes below require reinstalling WordPress or touching your database directly. Work through them in order and you'll have clean JSON coming back in about ten minutes.
Key Takeaways
- Enable WordPress debug logging immediately to identify the exact error source behind REST API unexpected result messages.
- Permission errors cause majority of REST API failures; verify user roles, nonces, and authentication headers match endpoint requirements.
- PHP memory limits and execution timeouts frequently trigger unexpected results; increase wp-config.php values to 256MB and 300 seconds.
- Conflicting plugins disable systematically by deactivating all plugins, then reactivating one-by-one to isolate the problematic extension.
- Verify REST API endpoints register correctly using wp-json routes and confirm callback function syntax matches WordPress standards exactly.
Quick Fix
Enable WordPress debug logging by adding define('WP_DEBUG', true) and define('WP_DEBUG_LOG', true) to wp-config.php, then check /wp-content/debug.log for the actual error. This reveals whether the issue is permissions, memory, plugins, or endpoint registration—the four most common causes.
grep -i 'rest\|api\|error' /var/www/html/wp-content/debug.log | tail -20
In This Article
- What 'Unexpected Result' Means in the REST API
- Check WordPress Debug Logging First (Most Likely Culprit)
- Fix REST API Permission Errors (Second Most Common)
- Resolve PHP Memory Limit and Timeout Issues
- Disable Conflicting Plugins to Isolate the Issue
- Verify REST API Endpoint Registration and Syntax
- Confirm the Fix and Prevent Recurrence
What "Unexpected Result" Means in the REST API
"Unexpected result" isn't a WordPress error string you'll find in any core file — it's shorthand for a REST response that didn't come back as clean JSON with a 200 or 201 status. WordPress promises a contract: hit a REST endpoint, get JSON back. When something breaks that contract — a warning, a redirect, an empty body, a 500 — every tool downstream chokes, because none of them know how to parse HTML or PHP notices as data.
This shows up constantly in wp-admin itself, not just external API clients. The block editor runs on REST calls under the hood, so a broken endpoint means Gutenberg won't load blocks, the media library spins forever, or saving a post silently fails.
Where You'll See This Error
Check these three places, in this order:
- Browser console — open DevTools (F12) → Network tab → filter by
wp-json. Look for red status codes or a response body starting with<br />or<!DOCTYPE. - wp-content/debug.log — if
WP_DEBUGandWP_DEBUG_LOGare enabled, PHP warnings and notices land here, often the exact thing corrupting the JSON output. - Server logs —
/var/log/apache2/error.logor/var/log/nginx/error.log(paths differ on Ubuntu 22.04/24.04 vs. Debian 12 depending on your PHP-FPM pool config) catch fatal errors that never reach WordPress's own logging.
Common triggers: a custom post type registered with a bad rest_base, an ACF field group sync throwing a PHP notice, a third-party integration plugin hammering an endpoint before authentication resolves, or a memory limit exhausted mid-request. All of these produce the same vague symptom — something other than clean JSON.
Check WordPress Debug Logging First (Most Likely Culprit)
This is the cause about 70% of the time. The "unexpected result" you're seeing is WordPress's way of saying "I got something back that isn't valid JSON," and debug logging tells you exactly what that something is instead of leaving you guessing. This applies to WordPress 5.0 and up — the logging constants haven't changed.
Enable Debug Mode and Check Logs
Open wp-config.php in the site root and find the line that says /* That's all, stop editing! */. Add these three lines just above it:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
WP_DEBUG_DISPLAY set to false is important — it stops errors from printing directly into the REST response body, which would just create a different flavor of the same bug. Save the file, then reload the failing REST endpoint in your browser or curl it.
tail -f /path/to/wp-content/debug.log
Replace /path/to/ with your actual install path (commonly /var/www/html/wp-content/debug.log on Ubuntu/Debian LAMP setups). This streams new log lines in real time as you trigger the broken request. Watch for PHP Fatal error, PHP Warning, or Allowed memory size exhausted — any of these printed before the JSON output is what's breaking the response.
Verify Log Output Format
A real entry looks like this:
[15-Feb-2026 09:12:41 UTC] PHP Warning: Undefined array key "rest_base" in /var/www/html/wp-content/plugins/custom-post-types/init.php on line 44
The actual error sits in brackets right after the timestamp — that's your root cause, not "unexpected result." If debug.log never gets created after you reload the page, WordPress can't write to wp-content/. That's almost always a file permissions problem, covered in the next section.
Fix REST API Permission Errors (Second Most Common)
If your log is clean but the REST endpoint still returns {"code":"rest_forbidden","message":"Sorry, you are not allowed to do that.","data":{"status":401}} or {"code":"rest_cannot_create","message":"Sorry, you are not allowed to create posts as this user.","data":{"status":401}}, the request is reaching WordPress fine — it's being rejected on purpose. This is the cause about 30% of the time, usually after a plugin update changes capability checks or you add a custom endpoint without a permission callback. Applies to WordPress 5.0 and later, where REST authentication became mandatory for write operations.
Verify User Authentication and Capabilities
First confirm the request is actually authenticated. If you're hitting the endpoint from JS inside wp-admin, check that wpApiSettings.nonce is present in the page source — a missing or expired nonce is the top cause of 401s on logged-in AJAX calls. For external scripts or JWT-based 2026 setups (Application Passwords or a JWT plugin like Miniorange), confirm the Authorization: Bearer header is actually being sent — check with curl -v and look for it in the request, not just your code.
Next, check the user's actual capabilities server-side:
wp user list --field=ID,user_login,user_email
This lists every user with ID, login, and email so you can confirm you're testing with the account you think you are. By default, REST endpoints require edit_posts for POST/PUT/DELETE requests — a Subscriber or Contributor role will get rejected even with a valid nonce.
If you registered a custom endpoint, WordPress does not assume any permission check — you have to write one. A route with no permission_callback either throws a deprecation notice or defaults to public access depending on your WP version, neither of which is what you want:
register_rest_route('myplugin/v1', '/data', array(
'methods' => 'POST',
'callback' => 'myplugin_handle_data',
'permission_callback' => function() {
return current_user_can('edit_posts');
},
));
This explicitly checks the current user's capability before the callback ever runs. Swap edit_posts for a tighter capability like manage_options if the endpoint touches site settings — don't leave it wide open just to make the error disappear.
Resolve PHP Memory Limit and Timeout Issues
If your REST API request is syncing a few hundred ACF fields, pulling a full WooCommerce product catalog, or paginating through a custom post type with heavy meta, you can quietly run out of memory or time mid-request. The symptom is usually a blank response, a 500, or {"code":"internal_server_error"} with nothing useful in the body. This applies to every WordPress version — it's a PHP-level ceiling, not a WP bug.
Check and Increase Memory Limits
wp eval 'echo WP_MEMORY_LIMIT;'
This prints the memory ceiling WordPress thinks it has. If it's 40M or 64M, that's your problem the moment a REST call loads a few hundred rows with serialized meta.
define('WP_MEMORY_LIMIT', '256M');
Add this near the top of wp-config.php, before the "stop editing" line. 256M is a reasonable starting point for sites with ACF or WooCommerce syncing.
php_value memory_limit 256M
Add this to .htaccess on Apache setups (WP 5.x–6.x, PHP 7.4–8.3).
php_admin_value[memory_limit] = 256M
On Nginx with PHP-FPM, add this to the site's pool config (e.g. /etc/php/8.3/fpm/pool.d/www.conf) and reload PHP-FPM. Raising the limit is a band-aid — tail wp-content/debug.log with WP_DEBUG_LOG enabled to see which hook or query is actually eating memory before you keep bumping the number.
Extend Script Execution Timeout
set_time_limit(300);
Add this alongside your memory constant in wp-config.php. REST API requests default to a 30-second execution window on most PHP-FPM configs — fine for a single post lookup, not for a 5,000-row import.
php_value max_execution_time 300
Add this to .htaccess on Apache; on Nginx/PHP-FPM, set request_terminate_timeout = 300 in the pool config and fastcgi_read_timeout 300; in the Nginx server block. This gives long-running imports 5 minutes instead of 30 seconds. If a job genuinely needs longer than that, stop trying to force it through a synchronous REST call — hand it off to wp-cron or a background queue (Action Scheduler, if WooCommerce is already installed) instead of stretching timeouts indefinitely.
Disable Conflicting Plugins to Isolate the Issue
If memory and timeout fixes didn't change anything, the "unexpected result" — a truncated JSON blob, an HTML error page mixed into your JSON response, or a silent 200 with an empty body — is very likely a plugin injecting output where the REST API doesn't expect it. Security plugins (Wordfence, iThemes Security), aggressive caching plugins (WP Rocket, W3 Total Cache), and any plugin registering custom rest_api_init endpoints are the usual suspects. This is the cause about 60% of the time when server config already checks out.
Deactivate All Plugins via WP-CLI
wp plugin deactivate --all
This turns off every plugin on the site without touching your database content or theme. Expect output listing each plugin as Plugin 'plugin-name' deactivated.
curl -X GET http://yoursite.com/wp-json/wp/v2/posts
Test the endpoint immediately after. If you now get clean JSON back instead of the garbled or unexpected response, a plugin was the cause — full stop.
Re-enable plugins one at a time and retest after each:
wp plugin activate plugin-name
Run the curl command again after each activation. The moment the REST response breaks again, you've found your culprit. Write it down — you'll want to check that plugin's changelog for a REST API-related bug fix, or replace it.
If the error persists even with all plugins deactivated, it's not a plugin conflict. Move on to switching to a default theme (Twenty Twenty-Five or similar) next, since theme functions.php code can hook into rest_api_init just as easily as a plugin can.
Verify REST API Endpoint Registration and Syntax
If plugin isolation didn't fix it, and the broken response is specific to a custom endpoint (not core routes like wp/v2/posts), the problem is in the register_rest_route() call itself. A missing comma, a callback function that doesn't exist yet, or a route registered outside the rest_api_init hook will all produce unexpected results — sometimes a 500, sometimes silently malformed JSON. This applies to any custom plugin or theme code targeting the REST API on WordPress 5.0 and later.
List All Registered REST Routes
wp rest-api list-routes
This dumps every route WordPress currently knows about, formatted as namespace, route pattern, and allowed HTTP methods — something like /your-namespace/v1/your-route (GET, POST). Scan for your custom route.
If it's not in the list, the registration function never ran. The most common cause is calling register_rest_route() directly in your plugin file instead of inside a function hooked to rest_api_init:
add_action( 'rest_api_init', function () {
register_rest_route( 'your-namespace/v1', '/your-route', array(
'methods' => 'GET',
'callback' => 'your_callback_function',
) );
} );
If that hook is missing or fires too late (e.g., inside another hook that runs after rest_api_init), your route silently never registers — no error, just absence.
Once the route shows up in wp rest-api list-routes, test it directly:
curl -X GET http://yoursite.com/wp-json/your-namespace/v1/your-route -H 'Authorization: Bearer YOUR_TOKEN'
Expect a clean JSON body. If you instead get {"code":"rest_no_route"}, the URL or namespace has a typo — check both against exactly what list-routes printed.
Confirm the Fix and Prevent Recurrence
Don't trust "it looks fixed in the browser." Browsers cache aggressively and hide response headers. Verify with curl so you see exactly what WordPress is sending, header by header.
Test REST Endpoint Response
curl -v -X GET http://yoursite.com/wp-json/wp/v2/posts
A working endpoint returns HTTP/1.1 200 OK, a Content-Type: application/json; charset=UTF-8 header, and a body starting with [{"id":1,"date":... — a valid JSON array, not HTML, not a PHP warning, not an empty string. If you see all three, the fix held.
For production sites, don't wait to notice REST API failures by accident. Wire up error monitoring so you get paged instead of your users hitting silent 500s. Sentry catches uncaught PHP exceptions and fatal errors with stack traces; New Relic tracks response times and error rates per endpoint, so a REST route that starts throwing 500s shows up on a dashboard within minutes, not when a client emails you.
Keep WordPress core, plugins, and themes updated weekly, and run PHP 8.1 or newer — PHP 7.4 hit end-of-life years ago and several REST API bugs fixed upstream never got backported to it.
Frequently Asked Questions
Why does WordPress REST API return unexpected result error?
The unexpected result error typically stems from permission issues, PHP memory exhaustion, plugin conflicts, or misconfigured endpoints. Enable debug logging in wp-config.php to see the actual error message and pinpoint the root cause quickly.
How do I fix REST API permission denied errors?
Check user capabilities match the endpoint requirements, verify nonces are included in POST requests, and confirm authentication headers are correct. Use rest_ensure_request_matches_user_permissions filter to debug permission checks on custom endpoints.
What PHP settings cause REST API to fail?
Low memory_limit (default 40MB) and short max_execution_time (default 30 seconds) commonly cause unexpected results. Increase memory_limit to 256MB and max_execution_time to 300 seconds in wp-config.php or php.ini.
How do I know which plugin breaks the REST API?
Deactivate all plugins and test the REST endpoint. Reactivate plugins one at a time, testing after each activation. The plugin that causes the error to reappear is your culprit; check its documentation or contact support.
Related Reads
- Is 500 Server Error My Fault? WordPress Troubleshooting
500 server error in WordPress? Find out if it's your fault and fix it in 5 minutes with copy-paste commands ordered by l…
- Why Is My Website Only Showing a White Screen? Fix It Now
WordPress white screen? Enable debug mode, check PHP memory, disable plugins. Copy/paste fixes ordered by likelihood. Wo…
- Will Error 500 Fix Itself? No—Here's What to Do
Error 500 won't fix itself. Learn the exact commands to diagnose and fix it in 5 minutes. Most common causes first.…