This can be done using the publisher/user/search API endpoint
In order to filter based on custom fields, it is necessary to first add the parameter source=CF, followed by a custom_fields parameter containing the (encoded) criteria used to filter on the relevant custom field(s).
Each item in the custom_fields array requires:
-
field_name- the custom field's ID -
data_type- the field's configured data type -
condition- the operator and value(s) to filter on -
response_time- typically{"type":"CURRENT"}
Supported data types and their condition types
|
Condition type |
Parameters |
Works with |
|---|---|---|
|
|
|
TEXT, NUMBER, BOOLEAN, ISO_DATE, SINGLE_SELECT_LIST |
|
|
|
TEXT |
|
|
|
NUMBER, ISO_DATE |
|
|
|
NUMBER, ISO_DATE |
|
|
|
NUMBER, ISO_DATE |
|
|
|
MULTI_SELECT_LIST |
|
|
- |
TEXT, NUMBER, ISO_DATE, SINGLE_SELECT_LIST, MULTI_SELECT_LIST |
|
|
- |
TEXT, NUMBER, BOOLEAN, ISO_DATE, SINGLE_SELECT_LIST, MULTI_SELECT_LIST |
NOT EQUAL is not supported as a condition type. To exclude a value, run two separate queries (e.g., one for EMPTY and one for EQUAL":"false") and reconcile the results outside the API.
Examples by field type
Boolean (Checkbox)
[{"field_name":"custom_field","data_type":"BOOLEAN","condition":{"type":"EQUAL","equal":"true"},"response_time":{"type":"CURRENT"}}]
Number
[{"field_name":"mobile","data_type":"NUMBER","condition":{"type":"EQUAL","equal":"0503027844"},"response_time":{"type":"CURRENT"}}]
Text
[{"field_name":"comments","data_type":"TEXT","condition":{"type":"LIKE","like":"refund"},"response_time":{"type":"CURRENT"}}]
Date (ISO_DATE)
[{"field_name":"birthdate","data_type":"ISO_DATE","condition":{"type":"BETWEEN","more":"1980-02-02","less":"2011-02-01"},"response_time":{"type":"CURRENT"}}]
Single-select list
[{"field_name":"occupation_status","data_type":"SINGLE_SELECT_LIST","condition":{"type":"EQUAL","optionsEqual":{"value":"Full-time"}},"response_time":{"type":"CURRENT"}}]
Multi-select list
[{"field_name":"interests","data_type":"MULTI_SELECT_LIST","condition":{"type":"IN","optionsIn":[{"value":"Antilles"},{"value":"Antigua and Barbuda"}]},"response_time":{"type":"CURRENT"}}]
Filtering for empty or any value
-
ANYreturns all users who have a value set for that field (regardless of what it is). Works with every data type, including Boolean. -
EMPTYreturns users who were presented with the field but left it blank. It does not match users who never saw the field at all.
[{"field_name":"account_status","data_type":"TEXT","condition":{"type":"EMPTY"},"response_time":{"type":"CURRENT"}}]
Important - "Empty" vs. "NULL": A field can be blank in two different ways:
-
Empty: the user saw the field on a form and submitted it without a value.
-
NULL: the user never encountered the field at all - for example, the field was created after the user registered, or the user was created via API/import without that field being sent.
The EMPTY condition (both in Mine Users and via this API) only returns the first group. There is currently no operator to search directly for NULL values. To identify users with NULL custom field data:
-
Run an
ANYsearch to see how many users do have a value. -
Compare against your total user count - the difference are users with NULL values.
-
Alternatively, generate a Users Export and filter for blank values in the file. This bypasses the search index and reads directly from the primary database.
Combining multiple conditions
You can pass several objects in the custom_fields array to filter on more than one custom field at once. All conditions are combined with AND logic; a user must match every condition to be returned.
{"field_name":"birthday","data_type":"NUMBER","condition":{"type":"ANY"},"response_time":{"type":"MORE","more":"2020-10-28"}},
{"field_name":"age","data_type":"NUMBER","condition":{"type":"ANY"},"response_time":{"type":"EQUAL","equal":"2020-10-28"}}
Custom field conditions cannot be combined with system field conditions (e.g., subscription status, payment method) in the same request; run separate queries for each.
Sample curl request
curl --location --request POST 'https://sandbox.piano.io/api/v3/publisher/user/search?api_token=TOKEN_VALUE' \\
--header 'Content-Type: application/x-www-form-urlencoded' \\
--data-urlencode 'aid=vwpqa2omsu' \\
--data-urlencode 'exclude_cf_metadata=true' \\
--data-urlencode 'source=CF' \\
--data-urlencode 'custom_fields=[{"field_name": "mobile","data_type": "NUMBER","condition": {"type": "EQUAL","equal": "0503027844"},"response_time": {"type": "CURRENT"}}]'
Once the API call is executed, it will return the matching user registries.
Good to know
-
Results are limited to a maximum of 10,000 users per query (
offset + limit ≤ 10000). For larger populations, use the asynchronous/publisher/export/create/userExportendpoint instead. -
Custom field data can take a few seconds up to a few hours to sync to the search index (up to a minute on Sandbox), so recently updated values may briefly not appear in results.