Skip to content

APIv4 Usage

API Input

Every API call consists of three elements: the entity, action, and parameters:

Entity: The "name" of the API. CiviCRM entities include Contacts, Activities, Contributions, Events, etc. Each API entity usually (but not always) corresponds to a table in the database (e.g. the Contact entity is the civicrm_contact table).

Supported Entities

The ongoing effort to port APIv3 entities to v4 is nearing completion. For updates or to help port a new entity, see this tracking issue.

Action: The "verb" of the API. The list of available actions varies for each entity, but most support a set of CRUD actions to create, get, update & delete, etc.

Parameters: Settings or data to pass to the api function. Each action accepts a different set of parameters.

Consider these samples with commonly-used actions and parameters for the Contact entity:

Action Parameters Description
create {"values":{"contact_type":"Individual", "first_name":"Jane", "last_name":"Doe"}} Create a new contact of type "Individual" with first name "Jane" and last name "Doe"
get {"where":[["last_name", "=", "Doe"]], "limit":25} Fetch the first 25 contacts with the last name "Doe"
delete {"where":[["id", "=", 42]]} Delete the contact with id "42"

API Explorer

For full, up-to-date details about specific entities, actions and parameters, use the API Explorer.

API Output

The API returns an array, with each item representing one record (also an array). In PHP, this output is contained in an ArrayObject, which can be accessed like an array using e.g. $result[0] or foreach ($result as ...).

See the php docs for a full list of methods ArrayObjects support

It also has the following methods in addition to or overriding the base ArrayObject:

  • $result->first(): returns the first item, or NULL if not found.
  • $result->single(): returns a single item, or throws an exception if not exactly one item was returned.
  • $result->last(): returns the last item, or NULL if not found.
  • $result->itemAt($index): returns the item at a given index, or NULL if not found.
  • $result->indexBy($field): reindexes the array by the value of a field.
  • $result->column($field): flattens the array, discarding all but a single field (call this second if using in conjunction with indexBy to create a key=>value array).
  • $result->count(): get the number of fetched (if LIMIT used) or matching results (if row_count included in select). See below.
  • From CiviCRM 5.50+ $result->countFetched(): get the number of fetched results. If you used LIMIT this won't exceed that number (but could be less).
  • From CiviCRM 5.50+ $result->countMatched(): get the total number of matching results. If you only fetched a subset (with LIMIT) you are required to add row_count to the select clause.


If you need to use functions such as is_array on the result you can cast it you cast it to an array, (array) $result, or use the getArrayCopy() method.

Counting results

Prior to 5.50, there was just one count available via count(). From 5.50+ it is clearer to use one of the new methods instead: countFetched() and countMatched() because count() can return different counts based on a number of factors (see below). Note that the rowCount property should not be considered a public interface and any code using it directly should be migrated to use a method instead.

TL;DR: If you need to know the number of matched rows regardless of any fetch limit, you must add row_count to the select. You can do this by calling selectRowCount() (note that this will replace any prior selects).

e.g. consider the database contains 100 contacts and you're using the Contact.get API.

$result = $api->addSelect('display_name')->execute() will fetch the names of all contacts.

  • $result->countFetched() will return 100.

  • $result->countMatched() will return 100 too, since this is known as there was no limit.

  • $result->count() will return 100.

$result = $api->addSelect('display_name')->setLimit(10)->execute() will fetch the names of 10 contacts. - $result->countFetched() will return 10. - $result->countMatched() will return throw an exception because it cannot be calculated from the result. - $result->count() will return 10.

$result = $api->selectRowCount()->addSelect('display_name')->setLimit(10)->execute() will fetch the names of 10 contacts and also calculate the total matched. - $result->countFetched() will return 10. - $result->countMatched() will return 100. - $result->count() will return 100.

$result = $api->selectRowCount()->execute() will not fetch any records, but will make the count available: - $result->countFetched() will return 0 (since we did not fetch any data rows). - $result->countMatched() will return 100. - $result->count() will return 100.

$result = $api->selectRowCount()->setLimit(10)->execute() is a funny thing to do (set a limit for records without asking for records anyway) but it will behave as if you had not included the setLimit() clause, i.e. exactly as the last example.

$result = $api->selectRowCount()->addSelect('display_name')->execute() is strictly unnecessary since it fetches all records anyway, so the selectRowCount() is superfluous. - $result->countFetched() will return 10. - $result->countMatched() will return 100. - $result->count() will return 100.


API Security

By default, APIv4 validates every action according to the permission level of the logged-in user. This can be disabled only when calling via PHP code. API Permissions are also able to be altered via hook.

More information on API Security can be found in the Security Documentation.

API Interfaces

The API is available in many environments (such as PHP, CLI, and JavaScript), and the notation differs slightly in each language. However, if you understand the canonical notation, then others will appear as small adaptations.

Use Outside of CiviCRM

If you're writing a Drupal module, a Joomla extension, a WordPress plugin, or a standalone script, then you may need to bootstrap CiviCRM before using the API.


This is the canonical API; all other environments are essentially wrappers around the PHP API.

There are two ways to call the api from PHP - which one you choose is a matter of convenience and personal preference. For example, you may prefer OOP syntax because IDE code editors provide autocompletion. Or if you need to work with the parameters as an array, traditional syntax will be more convenient.

APIv4 PHP Examples

Traditional (Procedural)

The function civicrm_api4($entity, $action, [$params], [$index]) accepts an array of parameters and returns the Result.

$result = civicrm_api4('Contact', 'get', [
  'where' => [
    ['last_name', '=', 'Adams'],
    // Option transformation.
    ['gender_id:name', '=', 'Male'],
    // Implicit join.
    ['employer_id.is_opt_out', '=', FALSE],
  'limit' => 25,

$index provides a convenient shorthand for reformatting the Result array. It has different modes depending on the variable type passed:

  • Integer: return a single result array; e.g. $index = 0 will return the first result, 1 will return the second, and -1 will return the last.
  • String: index the results by a field value; e.g. $index = "name" will return an associative array with the field 'name' as keys.
  • Non-associative array: return a single value from each result; e.g. $index = ['title'] will return a non-associative array of strings - the 'title' field from each result.
  • Associative array: a combination of the previous two modes; e.g. $index = ['name' => 'title'] will return an array of strings - the 'title' field keyed by the 'name' field.

Object-Oriented (OOP)

An Action class provides setter methods for each parameter. The execute() method returns the Result.

$result = \Civi\Api4\Contact::get()
  ->addWhere('last_name', '=', 'Adams')
  ->addWhere('gender_id:name', '=', 'Male')

Warning: Class names don't always match entity names!

For example, the entity "Case" is a reserved word in PHP and therefore could not be the name of a class, so it had to be named something else ("CiviCase"). Internally, calling civicrm_api4('Case', 'getFields') will translate to Civi\Api4\CiviCase::getFields. Custom data entities are another example. When writing generic functions that work with multiple entities, we strongly recommend against clever code like this: call_user_func(['\Civi\Api4\' . $entity, 'get'], ...), and suggest using this more reliable alternative: civicrm_api4($entity, 'get', ...).


The AJAX interface is automatically available for web-pages generated through CiviCRM (such as standard CiviCRM web-pages, CiviCRM extensions and custom CiviCRM templates).

Inputs are identical to the traditional PHP syntax:

CRM.api4('entity', 'action', [params], [index])

From an Angular app, use the service crmApi4() which has an identical signature but works within the $scope.digest lifecycle.

Both functions return a Promise, which resolves to a Result array.

CRM.api4('Contact', 'get', {
  where: [
    ['last_name', '=', 'Adams'],
  limit: 25
}).then(function(results) {
  // do something with results array
}, function(failure) {
  // handle failure

Use on Non-CiviCRM Pages

The AJAX interface could be made available to other parts of the same website (e.g. a Drupal module or WordPress widget) by calling Civi::resources()->addCoreResources() from PHP. Please note that the AJAX interface is subject to API Security and Same Origin Policy.

Command line


cv supports multiple input formats for APIv4. The API Explorer uses the JSON format in generated code:

cv api4 Contact.get '{"where":[["first_name", "=", "Alice"], ["last_name", "=", "Roberts"]]}'

This format may be cumbersome to enter manually, so the same API request could also be written like this:

cv api4 Contact.get +w 'first_name = "Alice"' +w 'last_name = "Roberts"'

For more examples, refer to the output of cv --help api4.


Adding version=4 to a drush command will cause v4 api to be called rather than v3.


APIv4 is not yet available as a wp-cli command.


Because Smarty is an older technology, there are no plans to port the APIv3 smarty function to APIv4.

Scheduled jobs

APIv4 is not yet available for scheduled jobs.