You can use formulas to create smarter forms with ArcGIS Survey123.

Previous questions must always be referred to in formulas with the format ${field_name}.


The following operators are supported in Survey123:



The current answer




${question_one} + 4



${question_one} - 4



${question_one} * 4



${question_one} div 4





Not equal



Less than



Less than or equal to



Greater than



Greater than or equal to




${price}=9.80 or ${price}=9.70



${price}>9.00 and ${price}<9.90


Modulus (division remainder)

${question_one} mod ${question_two}


The following functions are supported in Survey123:


selected(question, value)

Checks whether answer is selected. This function is used for select_one and select_multiple questions.

selected(${question_one}, 'a')


Returns the number of selected answers for select_one and select_multiple questions. This function also returns the number of attached files for image, audio, and file questions using the multiline appearance.


string-length(question, expression, or value)

Returns the length of a nonempty string.


substr(question, start, end)

Returns the substring beginning at the specified start and extends to the character at index end -1, where start and end begin at 0.

substr(${question_one}, 1, 2)


Returns a false value if the expression would return true, and a true value if the expression would return false.

not(selected(., 'yes'))

if(condition, a, b)

If the condition evaluates to true, returns a; otherwise, returns b.

if(selected(${question_one}, 'yes') and selected(${question_two}, 'yes'), 'yes', 'no')








Returns a random UUID string.



Returns a random value between 0 (inclusive) and 1 (exclusive).



Returns today's date, internally stored as local midday. This function is used in date questions.



Returns a time stamp for this instant. This function is used in time and dateTime questions. It behaves the same as today() in date questions.



If a question already has a value, returns the existing value. This function is useful when using random() or uuid() in a repeated question to ensure the value doesn't change when you browse through the repeat records in the form.


boolean(question, expression, or value)

Returns true if the value provided is not null.

It's recommended that you use boolean-from-string() instead.


This function always returns true in the Survey123 web app. For alternatives, see Empty values.


number(question, expression, or value)

Converts to number. Conversion varies depending on data type.


If this function is empty, it will return NaN and the question will remain empty.


int(question, expression, or value)

Converts to integer. Conversion varies depending on data type.


If this function is empty, it will return NaN and the question will remain empty.


string(question, expression, or value)

Converts to string. Conversion varies depending on data type.


date(question, expression, or value)

Converts a number or string to a date object, without preserving time.


date-time(question, expression, or string)

Converts a number or string to a date object.


decimal-date-time(question, expression, or string)

Converts a date object to a decimal date-time number.


decimal-time(question, expression, or string)

Converts a time object to a number representing a fractional day in the device's time zone.



Fits an existing date or time value to a defined format.

format-date(${previous_time}, '%H:%M')

coalesce(value1, value2)

Returns the first nonempty value. This function supports only two values.

coalesce(${question_one}, ${question_two})

concat(value1, value2, …)

Returns the concatenation of the string values.

concat(${question_one}, ' and ', ${question_two})

max(value1, value2, ...)

Returns the maximum value in a given range, or to a single question across repeats.

max(${question_one}, ${question_two})

min(value1, value2, ...)

Returns the minimum value in a given range, or to a single question across repeats.

min(${question_one}, ${question_two})


Returns the sum of all responses to a given question across repeats. For more information, see Aggregate functions.


When used in the Survey123 field app, this function can be placed inside or outside of the repeat. If the function is to be used in the Survey123 web app, it must be placed outside of the repeat. A sum value from outside the repeat can be referenced in a calculation inside the repeat.



Returns the amount of responses to a given question across repeats. For more information, see Aggregate functions.


When used in the Survey123 field app, this function can be placed inside or outside of the repeat. If the function is to be used in the Survey123 web app, it must be placed outside of the repeat. A count value from outside the repeat can be referenced in a calculation inside the repeat.



Returns a value from an external CSV file. For more information, see Retrieve a value from CSV.

pulldata('users', 'email', 'name', ${respondent_name})


Returns a value from the EXIF metadata in an image. For more information, see Extract image metadata.

pulldata("@exif", ${photo}, "GpsLatitude")


Returns a value from a geopoint question. For more information, see Extract geopoint values.

pulldata("@geopoint", ${location}, "horizontalAccuracy")


Runs a JavaScript function in the form and returns the result. For more information, see JavaScript functions in survey forms.

pulldata("@javascript", "functions.js", "uniqueID", ${buildings})


Returns a value from a JSON object. For more information, see Retrieve a value from JSON.

pulldata("@json", ${json_output}, "attributes.ZIP_CODE")


Queries an ArcGIS layer or table and returns the result. For more information, see Query a feature layer.

pulldata("@layer", "getRecordAt", "", ${location})


Returns information about the device or signed-in user. For more information, see Device and user properties.

pulldata("@property", 'username')


Returns the version of the survey defined in the settings worksheet.



Returns true if the string provided is 'true' or '1'. Otherwise, returns false.


contains(string, substring)

Returns true if the given string contains the substring.

contains(${question_one}, 'red')

starts-with(string, substring)

Returns true if the given string starts with the substring.

starts-with(${question_one}, 'The')

ends-with(string, substring)

Returns true if the given string ends with the substring.

ends-with(${question_one}, 'hand.')

selected-at(question, number)

Used for select_multiple questions. It returns the name of the choice selected for the given number, counted starting from zero; for example, '2' will return the third selected choice.

selected-at(${question_one}, 2)

jr:choice-name(choice_name, 'question')

Used for select_one questions. It returns the label associated with the name of the choice in the given question. Be aware that the question must be defined inside quotes.

jr:choice-name(${select_one}, '${select_one}')

Used for select_multiple questions. It returns the label associated with the name of the choice in the given question. The selected-at() function must be used to extract the label for individual answers. Be aware that the question must be defined inside quotes.

jr:choice-name(selected-at(${select_multiple}, 3), '${select_multiple}')

join(separator, question)

Concatenates all answers to a given question in a repeat, separated by the given separator.

join(',', ${question_in_repeat})


Applies a regular expression to the question's input. For more information, see Regular expressions.

regex(., '^\d{5}$')


Returns the index of the current record in a repeat. For more information, see Repeats.


indexed-repeat(question, repeat, index number)

Returns the value from a specific question in a repeat record. For more information, see Repeats.

indexed-repeat(${room_no}, ${floor}, 3)

The following mathematical functions are supported in Survey123:



Returns pi.



Returns the arc cosine of the value.



Returns the arc sine of the value.



Returns the arc tangent of the value.



Returns the cosine of the value as an angle in radians.



Returns the sine of the value as an angle in radians.



Returns the tangent of the value as an angle in radians.



Returns the natural exponent of the value.



Returns 10 to the power of the value.



Returns the natural logarithm of the value.



Returns the base-ten logarithm of the value.



Returns the square root of the value.


atan2(value1, value2)

Returns the arc tangent of the quotient of the values.

atan2(${question_one}, ${question_two})

round(value, power)

Returns the rounded value.

round(${question_one}, 5)

pow(value, power)

Returns the value to the power specified.

pow(${question_one}, 3)


Adding a constraint to a survey question restricts the accepted inputs for a response. This can include a specific range of numbers, combinations of letters and numbers, or general pattern matching. In your spreadsheet, the constraint expression is entered into the constraint field and informational text is entered into the constraint_message column of the survey worksheet. In the constraint expression, the input for the question is always represented by a period.

For example, you can use the following formula to restrict the input of an integer field to positive numbers only:

.>= 0

This formula, when applied to a date field, prevents the user from entering a value earlier than today:

.>= today()

You can also use calculations in constraints. This formula performs a calculation to only allow the user to select dates between today and 14 days from today:

(.>= today()) and (.<=(today() + (1000 * 60 * 60 * 24 * 14)))


To avoid unexpected errors, if your formula includes decimal values between -1 and 1, include a leading zero in your values. Without the leading zero, the decimal can be mistaken for the operation character . (or -.). For example, the following expression will fail:

.> .25 and .< 24.25

The following expression will execute as expected:

.> 0.25 and .< 24.25

Regular expressions

You can use regular expressions in a variety of ways for pattern matching to constrain valid responses to a given format or to ensure it contains specific content. This example requires that the response to the question include the word road somewhere in it:

regex(., 'road')

The period used at the start of these examples applies the expression to the current field. Adding the name of another field in its place instead applies the regular expression to that field, which is ideal for relevant expressions. The comma acts as a separator between this field definition and the expression.

This regular expression ensures that the response exactly matches the word road, with nothing before or after it:

regex(., '^road$')

Regular expressions are ideal for confining the input of a question to a standard format. This example accepts only the input of a United States five-digit ZIP Code:

regex(., '^\d{5}$')

This example matches the response to the current format of Indonesian license plates, as in the following photo:

Standard Indonesian license plate

regex(., '^[A-Z]{1,2}\d{4}[A-Z]{2,3}$')

More loosely standardized formats can require complex regular expressions. This regular expression used by the Survey123 web app constrains the input of a string field to match the format of an email address:

regex(., '^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')

This modification of the above expression constrains input to match the format of an email address, while accepting non-English characters:

regex(., '^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\u0400-\uffff\-0-9]+\.)+[a-zA-Z\u0400-\uffff]{2,}))$')

This example, also using a regular expression from the Survey123 web app, limits a field to only accept responses that match the format of a web address:

regex(., '^((https?|ftps?)\://|)(((?:(?:[\da-zA-Z](?:[-\da-zA-Z]{0,61}[\da-zA-Z])?)\.)+(?:[a-zA-Z](?:[-\da-zA-Z]{0,61}[\da-zA-Z])?)\.?)|localhost)(\:\d+)?(/(?:[^?#\s/]+/)*(?:[^?#\s/]+(?:\?[^?#\s/]*)?(?:#[A-Za-z][\w.:-]*)?)?)?')

For more information about regular expressions, refer to the Mozilla Developer Network documentation. For a list of characters and their functions in regular expressions, see the Reference worksheet in the Survey123 templates or refer to the Quick reference.


Calculations are performed in the calculation column for a question. Calculations are often associated with the calculate type of question, but they can also be applied to integer, decimal, text, and select_one questions. The outcome of the calculation can be used to populate relevant or constraint expressions by referring to the field name of the calculate question.

The calculate question type is hidden and doesn't display on a form. This means it can also be used to hold values that don't need to be displayed on the form but are included in the feature layer.

For example, you can create a question of type calculate, name it calc, and insert the following expression in its calculation column:

${question_1} + ${question_2} + ${question_3}

Use the result to set the relevance for the next question:

${calc} <= 100

Calculations can be used with the responses in date fields. This calculation estimates the number of years between a date entered and today, ideal for calculating somebody's age:

int((today() - ${birth_date}) div (1000 * 24 * 60 * 60 * 365.25))

Sometimes you only need part of a value, or a truncated version of a complete answer. The substr operator returns only part of a string, defined by the numbers after it. The first character determines the starting point of the selection, while the second value determines the length (if no second value is present, it continues until the end of the string). In this example, the substr operator removes all but the string from characters 10 to 15:

substr(${previous_question}, 10, 15)

When the first number is negative, substr starts counting from the end of the string instead of the beginning. This example returns only the last five characters of the answer:

substr(${previous_question}, -5)

You can use the calculationMode parameter to control when calculations are calculated in the form. For more information, see Calculation mode.

You can also use the calculation column for aggregate functions in repeats. For more information, see Repeats.

Adhere to the following best practices when using calculations:

  • When using random(), consider adding a constant to avoid getting zero (0) as a result, for example, random()+0.5. A value of 0 may result in a blank answer.
  • As with constraints, ensure that all decimal values between -1 and 1 in your formula have a leading 0, as leading with a decimal point causes errors.
  • The data type of a calculation result is dependent on the data type of each element of the calculation. If a calculation is performed on two integers, the calculation result is an integer. If a calculation includes a string data type, you may find that a + operator concatenates values instead of adding them. To avoid unexpected results, use the number() function to ensure string values in a calculation are treated as numbers. For example, the calculation to add question1 (of type integer) to question2 (of type text) is ${question1} + number(${question2}).

The XLSForm default binding type for a calculate question is string. To overwrite this default, enter the required type (for example, int or decimal) in the bind::type column for your question. Alternatively, you can use the desired question type (for example, integer or decimal) and set the appearance for this question to hidden.


Using mathematical functions or operators with text questions returns a result of NaN in the Survey123 web app. To concatenate text questions, use the concat() function instead of the + operator.

Complex mathematical functions

The calculation column can also handle more complex mathematical operations. This example determines the area of a plot given its radius, using the pi and power functions:

pi() * pow(${plot_radius}, 2)

A common method of measuring the height of a tree is to measure the angle from eye-level at an observation point to the top of the tree, and the distance from the same observation point to the tree base. If the angle to the top of the tree is measured in degrees, the following calculation would be used to convert it to radians:

${angle_to_top_degrees} * (pi() div 180)

With the angle measurement now in radians, the height of the tree (rounded to two decimal places) can be determined with the following calculation:

round(((tan(${angle_to_top_radians}) * ${distance_to_tree}) + ${height_to_eyes}),2)

Date formatting

You can use the format-date function in the calculation field to format date and time values. This can be useful when you want to display portions of dates to users or persist them as strings.

In this example, the value contained in a previous time or dateTime question is returned in 24-hour time:


The qualifiers you can use in the format-date function are as follows:



4-digit year


2-digit year


0-padded month


Numeric month


Month name abbreviated


0-padded day of month


Day of month


0-padded hour (24-hour time)


Hour (24-hour time)


0-padded minute


0-padded second


0-padded millisecond ticks (000-999)


Three-letter short text day


Week number


The %W qualifier can't be used in a format-date function in which the date question uses a calculation. This qualifier works with a date question that has a default.


Be aware that dateTime questions don't support time resolutions below one minute. To capture a time resolution with dates that is lower than one minute, consider using the start and end questions.

Empty values

When using constraints and calculations that refer to other questions, consider what occurs when that question is empty (that is, it has no response). Empty values are represented as follows:

  • NaN (not a number) for integer and decimal questions. This is a special value that represents the absence of a valid value.
  • '' (an empty string) for text questions. The default data type for select_one, select_multiple, rank, and hidden questions is also text. When select_one, select_multiple, and hidden question types are empty, or when a rank question hasn't been altered by the user, they contain an empty string.

Depending on whether the value is a number or text, the behaviors in calculations differ.

In integer or decimal questions, the following behaviors occur:

  • Any mathematical expression with a value of NaN fails to complete, and the question remains empty.
  • The min() and max() functions complete and ignore any NaN values.
  • The NaN value compared to any other value is only true in a calculation of an is not equal value comparison. All other expressions result in false.

In text questions, the following behaviors occur:

  • The concatenation of text questions completes if empty values are present. For example, "Hello" + ${firstName} + ", how are you?" results in "Hello , how are you?" when the question firstName is empty.
  • The min() and max() functions complete and ignore any empty strings.
  • An empty text answer is equal to another empty text answer and is always less than any nonempty text.

You can determine whether a question is empty using the string-length function. The string-length function can be used with all question types. For example, string-length(${Question1}) returns 0 if Question1 is empty.

You can also determine whether a select_one or select_multiple question is empty using the count-selected function. For example, count-selected(${question2}) returns 0 if no selection has been made for question2.

Select_multiple and rank questions

The responses to select_multiple and rank question types are stored differently than all other question types. Each checked answer in a select_multiple question is entered in the order it was selected, separated by commas. For example, selecting answers 'A' and 'B' in that order presents the response as 'A,B'. A rank question also stores its answers in a comma-separated list in order from highest to lowest rank at the time of submission.

Some XLSForm features do not work with select_multiple and rank questions. For example, if you enter the answer 'A' in the relevant column of a question referring to your select_multiple question, and the survey response is 'A,B', the response is not considered relevant. In this case, the solution is to use the selected() function, which determines whether any one of the values appears in the list.

The following expression, when used in the relevant column of a question, displays the question if the user has selected 'A' as one of the responses in the referenced select_multiple question. Additional responses in the select_multiple question do not change this behavior.

selected(${previous_question}, 'A')

Retrieve a value from CSV

You can use the pulldata() function in the calculation column of a question to preload data from a CSV file. There are two ways to include a CSV file: manually place the file in the survey's media folder or link to a .csv file hosted in ArcGIS.

The pulldata() function requires the following four parameters to be specified in order:

  1. The name of the CSV file that contains the list of values. The name does not include the .csv file name suffix.
  2. The name of the column in the CSV file that contains the value you want to return.
  3. The name of the key field in the CSV file that you will use to look up the value.
  4. The key value to look up in the key field.

These values can be defined directly or through variables defined elsewhere in the survey. In the following example, the calculation returns the email address of somebody named in a previous question from a CSV file named info:

pulldata('info', 'email', 'name', ${respondent_name})

The same pulldata() function also works in the constraints column, preventing the user from submitting answers that are not in the CSV file. In the constraints column, this same formula prevents the form from accepting any values that are not in the CSV file's name column.

There are some limitations to the pulldata() function. The name of the key field has the same limitation as the name column in the choices worksheet, meaning that these values can't have spaces or non-ASCII characters. Additionally, because these are CSV files, using a comma in any of these fields causes the pulldata() function to produce incorrect results.

If values in your CSV file exceed 255 characters, you must enter a higher value in the bind::esri:fieldLength column for both the question you're populating with the CSV content and any questions being used as input to the pulldata() function. If your CSV file includes values that are larger than the maximum length of one of these fields, a survey response fails to submit and displays a Code 1000 error.


The pulldata() function can't be used to populate the values of select_multiple questions.

When using the pulldata() function, your CSV column names cannot contain spaces, hyphens, or other special characters.

For best results, encode the .csv file using UTF-8 character encoding. If you're using Microsoft Excel to create your .csv file, save it as CSV UTF-8.

Retrieve a value from JSON

You can use the pulldata("@json") function to extract individual properties from a JSON object. This function is often used to complement other functions, such as pulldata("@javascript") and pulldata("@layer"). The function has the following syntax:

pulldata("@json", <question name>, "<JSON property>")

The parameters for pulldata("@json") are as follows:

  • question name—The name of the question that contains the JSON object. For example: ${json_response}.
  • JSON property—The property you want to retrieve from the JSON. Use periods to specify the location of the property in the JSON structure. For example, to retrieve the City property from the address object, use "address.City".

In the following example, the result of a reverse geocode operation is returned as JSON in a text question named json_response:

XLSForm with pulldata("@json") calculations

A response from the geocoder similar to the following is returned:

	"address": {
		"Match_addr": "Eiffel Tower",
		"LongLabel": "Eiffel Tower, Paris, Île-de-France, FRA",
		"ShortLabel": "Eiffel Tower",
		"Addr_type": "POI",
		"Type": "Historical Monument",
		"PlaceName": "Eiffel Tower",
		"AddNum": "",
		"Address": "",
		"Block": "",
		"Sector": "",
		"Neighborhood": "Paris 07",
		"District": "Paris",
		"City": "Paris",
		"MetroArea": "",
		"Subregion": "Paris",
		"Region": "Île-de-France",
		"RegionAbbr": "",
		"Territory": "",
		"Postal": "",
		"PostalExt": "",
		"CntryName": "France",
		"CountryCode": "FRA"
	"location": {
		"x": 2.294520000000034,
		"y": 48.85832000000005,
		"spatialReference": {
			"wkid": 4326,
			"latestWkid": 4326

The pulldata("@json") function is used to retrieve the City property from the address object:

pulldata("@json", ${json_response}, "address.City")

The latitude and longitude are retrieved from the location object:

pulldata("@json", ${json_response}, "location.x")

pulldata("@json", ${json_response}, "location.y")

Query a feature layer

You can use the pulldata("@layer") function to query a feature layer or table. You can perform an attribute query or a spatial query. An attribute query uses a getRecord or getValue operation:

pulldata("@layer", "getRecord", "<URL>", "<WHERE clause>")
pulldata("@layer", "getValue", "<JSON property>", "<URL>", "<WHERE clause>")

A spatial query uses a getRecordAt or getValueAt operation:

pulldata("@layer", "getRecordAt", "<URL>", <location>, "<WHERE clause>")
pulldata("@layer", "getValueAt", "<JSON property>", "<URL>", <location>, "<WHERE clause>")

The getRecord and getRecordAt operations return a JSON feature object containing a single feature and all of its attributes. The getValue and getValueAt operations return a single value from the feature object, instead of the entire query response.

The parameters for pulldata("@layer") are as follows:


Required. The URL of the feature layer or table you want to query. Accepts additional request parameters.

Example: ""

JSON property

Required for the getValue and getValueAt operations. The value you want to retrieve from the query response.

Example: "attributes.COUNTRY"


Required for the getRecordAt and getValueAt operations. The point location you want to query with the feature layer. This must be a geopoint question.

Example: ${location}

WHERE clause

Optional. A WHERE expression that filters the feature layer or table.

Example: "COUNTRY='Canada'"


The pulldata("@layer") function returns the first record in the query response. Design and test your query to ensure you get the desired results. You can refine your query using a WHERE clause and the additional request parameters described below.

The following example queries a polygon feature layer of world time zones and returns the time zone that the geopoint is located in:

XLSForm with pulldata("@layer") calculation

The getRecordAt operation is used to retrieve the time zone that the geopoint intersects using the following syntax:

pulldata("@layer", "getRecordAt", "", ${location})

The ZONE attribute is then extracted from the query response using the pulldata("@json") function. Alternatively, you can use a getValueAt operation in the pulldata("@layer") calculation to retrieve the ZONE attribute directly, without the need for a separate question to store the query response. See the following example:

pulldata("@layer", "getValueAt", "attributes.ZONE", "", ${location})

You can use pulldata("@layer") in constraints. For example, you can apply a constraint to a geopoint question to prevent users from submitting locations outside an area of interest.

XLSForm with pulldata("@layer") constraint

The getRecordAt operation returns the JSON feature object for the country that the geopoint is in using the following syntax:

pulldata("@layer", "getRecordAt", "", ${location}, "COUNTRY='Canada'")

The name of the country is extracted in a text question using pulldata("@json"). A constraint of ${country}='Canada' is then applied to the geopoint question to ensure the location falls within the region of Canada.

Request parameters

You can refine a pulldata("@layer") query with additional request parameters, such as distance, orderByFields, and resultOffset. For more information on request parameters, see Query (Feature Service/Layer).

To include these parameters in a query, append them to the URL after a question mark. Additional parameters are separated by ampersands. In the following example, the orderByFields and resultOffset parameters are appended to the feature layer URL to return the name of the tenth most populous county in California:

pulldata("@layer", "getValue", "attributes.NAME", " DESC&resultOffset=10", "STATE_NAME = 'California'")

The following request parameters are not supported in pulldata("@layer") calculations and will be ignored:

  • f
  • outFields
  • outSR
  • resultRecordCount
  • returnGeometry
  • returnIDsOnly
  • token