WP AJAX is not working, always returns 0

I’ve checked any threads here regarding this and did a debug test which worked, I’d like to understand why my own code doesn’t work as it presents the same functionality explained in every possible thread created about this issue.

I’m trying to send AJAX request when clicking on a checkbox and do some work with MySQL.

Here are some details to understand the scope and what was done:

var $ = jQuery;
$(function() {
    $('table.table').on('click', '[name="term-id"]', function() {
        var t = $(this),
            id = t.val(),
            checked = t.is(':checked');

        checkCategory(id, checked);

function checkCategory(id, checked) {
        url: ajaxurl,
        type: 'POST',
        dataType: 'json',
        data: {action: 'setAsyncCheckProduct', id: id, checked: checked},
        success: function(response) {
        error: function(errorThrown) {

This script is included in my plugin via:

wp_enqueue_script('settings-manager', $this->pluginUrl . '/assets/js/SettingsManager.js');

In my main (abstract) plugin class (It is instantiated and is getting called, so I omit this code sample), but I add my async events assigned to admin_init:

add_action('admin_init', [$this, 'assignAsyncEvents']);

In my concrete classes, I implement this method like:

public function setAsyncCheckProduct()
    echo 123123123;

public function assignAsyncEvents()
    add_action('wp_ajax_setAsyncCheckProduct', [$this, 'setAsyncCheckProduct']);

Which basically means:

Send AJAX request to whatever is “ajaxurl” (which returns correct: /wp-admin/admin-ajax.php), action: setAsyncCheckProduct is sent along to the server, on the server, the assignAsyncEvents should be already done and a wp_ajax_MY_ACTION_NAME assigned.

Why do I still get a 0 in response?


I’ve been manually putting debug output in admin-ajax.php just for the sake of curiosity.

if ( is_user_logged_in() ) {
     * Fires authenticated AJAX actions for logged-in users.
     * The dynamic portion of the hook name, `$_REQUEST['action']`,
     * refers to the name of the AJAX action callback being fired.
     * @since 2.1.0
    do_action( 'wp_ajax_' . $_REQUEST['action'] );
    var_dump('wp_ajax_' . $_REQUEST['action']);
} else {
     * Fires non-authenticated AJAX actions for logged-out users.
     * The dynamic portion of the hook name, `$_REQUEST['action']`,
     * refers to the name of the AJAX action callback being fired.
     * @since 2.8.0
    do_action( 'wp_ajax_nopriv_' . $_REQUEST['action'] );
// Default status
die( '0' );

This is the core functionality with my var_dump added. My AJAX call GETS THERE and the output is CORRECT ACTION NAME defined in add_action in my plugin files, thought nothing is returned from my action/it’s not being called and I get to the default die( '0' );

Bummer. Don’t know what to do further.

Solutions Collecting From Web of "WP AJAX is not working, always returns 0"

The problem is where you place the add_action of your wp_ajax_*

Try to put it at the start of everything, and ensuring that it is being loaded in a /wp-admin/ route.

Try your url: http://xxx/wp-admin/admin-ajax.php?action=your_action until it gives you something different than zero.

Placing this answer for those people who’re trying to fix the same issue.

It’s important where

add_action('wp_ajax_setAsyncCheckProduct', [$this, 'setAsyncCheckProduct']);


In my case the issue was finally fixed moving a similar line to the main plugin file, where all concrete classes are included and initialised. Of course the line had to be changed to this form:

add_action('wp_ajax_setAsyncCheckProduct', ['ClassName', 'setAsyncCheckProduct']);