The pulldata() function retrieves a value from a CSV file attached to the form. Given a filename, a column to return, a column to match on, and a key value, it returns the cell at the intersection of the matching row and the result column.
pulldata('filename.csv', 'result_column', 'key_column', ${key_field})| Position | Parameter | Description |
|---|---|---|
| 1 | Filename | Name of the CSV media file (same name used in select_one_from_file) |
| 2 | Result column | The column whose value you want to return |
| 3 | Key column | The column to match against |
| 4 | Key value | The value to match — usually a field reference like ${field} |
The function returns an empty string if no match is found.
Prerequisites
The CSV file must be attached to the form as a media file. The same file referenced by pulldata() can also be used as the choice list for select_one_from_file.
Example CSV — staff.csv:
name,id,department,phone
Alice,S001,Health,+1234567890
Bob,S002,Education,+0987654321
Carol,S003,Health,+1122334455Basic example
Look up a staff member's department and phone from their ID:
survey:
| type | name | label | calculation |
|---|---|---|---|
| select_one_from_file staff.csv | staff_id | Select staff member | |
| calculate | dept | pulldata('staff.csv', 'department', 'name', ${staff_id}) | |
| calculate | phone | pulldata('staff.csv', 'phone', 'name', ${staff_id}) | |
| note | dept_note | Department: ${dept} — Phone: ${phone} |
The name column in a select_one_from_file CSV is the stored value. When the enumerator selects "Alice", ${staff_id} holds "Alice", which pulldata() uses as the key to retrieve her department and phone.
Retrieving the label of a selected choice
A select_one_from_file question stores the name value. To display the human-readable label, use pulldata() to look it up:
| type | name | label | calculation |
|---|---|---|---|
| select_one_from_file villages.csv | village | Select village | |
| calculate | village_label | pulldata('villages.csv', 'label', 'name', ${village}) | |
| note | confirm | You selected: ${village_label} |
Multiple lookups from one selection
You can call pulldata() multiple times on the same file to retrieve different columns for the same selected row:
| type | name | label | calculation |
|---|---|---|---|
| select_one_from_file facilities.csv | facility | Select facility | |
| calculate | fac_type | pulldata('facilities.csv', 'type', 'name', ${facility}) | |
| calculate | fac_district | pulldata('facilities.csv', 'district', 'name', ${facility}) | |
| calculate | fac_beds | pulldata('facilities.csv', 'beds', 'name', ${facility}) |
Using pulldata in relevant and constraint
pulldata() can be used in relevant and constraint columns to apply logic based on looked-up values:
| type | name | label | relevant | constraint |
|---|---|---|---|---|
| select_one_from_file facilities.csv | facility | Select facility | ||
| integer | bed_count | Number of beds occupied | . <= number(pulldata('facilities.csv', 'beds', 'name', ${facility})) |
Using pulldata with once()
To evaluate pulldata() only on first load (preventing re-evaluation when the key field changes after initial entry), wrap it in once():
| type | name | label | calculation |
|---|---|---|---|
| calculate | initial_dept | once(pulldata('staff.csv', 'department', 'name', ${staff_id})) |
Best Practices
- The key column in the CSV must match the
namecolumn used inselect_one_from_file— these must be the same value. - Column names in the CSV are case-sensitive —
Departmentanddepartmentare different columns. - If the key value is not found,
pulldata()returns an empty string — useif(${field} != '', pulldata(...), '')to guard against empty selections. - Keep CSV files small where possible — large files increase form load time on low-bandwidth connections.
- For data that changes between data collection rounds, update the CSV media file by uploading a new form version.
Limitations
pulldata()only matches the first row where the key column equals the key value — duplicate key values in the CSV are not supported.- The CSV file must be attached to the form before deployment; it cannot be fetched from a URL at runtime.
- Numeric values returned by
pulldata()are strings — wrap innumber()when used in arithmetic:number(pulldata(...)).
Extended pulldata() variants
Beyond CSV lookups, pulldata() supports several additional data sources by changing the first argument.
pulldata('app-api', key)
Read device metadata, user attributes, and app context. See the full reference on the App API page.
pulldata('app-api', 'user.name')
pulldata('app-api', 'serverTime')
pulldata('app-api', 'projectCode')pulldata('json', jsonString, jsonpath)
Parse an inline JSON string and extract a value using a JSONPath expression. Useful when you have a JSON literal embedded in the form.
pulldata('json', '{"status":"active","code":42}', '$.code')Returns the matched value as a string, or empty string if not found.
pulldata('jsonpath', fieldRef, jsonpath)
Extract a value from a JSON string stored in another field using a JSONPath expression. This is equivalent to substr-jsonpath(${field}, jsonpath).
pulldata('jsonpath', ${api_raw}, '$.data.id')pulldata('jwt', tokenField, claimName)
Decode a JWT token stored in a field and return the value of a specific claim from the payload.
pulldata('jwt', ${auth_token}, 'sub')
pulldata('jwt', ${auth_token}, 'exp')Returns empty string if the token is invalid or the claim is not present.
pulldata('instanceid')
Returns the unique instance ID of the current submission. Equivalent to the instanceID meta field.
pulldata('instanceid')pulldata('mapping', value, mappingName)
Map a value to another using a named mapping table defined in the form configuration.
pulldata('mapping', ${district_code}, 'district_names')Returns the mapped value, or empty string if no mapping matches.
pulldata('options', optionName)
Read a form configuration option by name.
pulldata('options', 'max_household_size')pulldata('rawquery', path, sql, param1, ...)
Execute a SQL SELECT against a bundled SQLite .db file and return the value from the first column of the first matching row. Parameters after the SQL string are substituted in order for ? placeholders.
| Position | Parameter | Description |
|---|---|---|
| 1 | 'rawquery' | Literal string identifying this variant |
| 2 | path | File path in concat(${family_path}, '/file.db::tableName') format |
| 3 | SQL | A SELECT statement using ? as positional placeholders |
| 4+ | params | Values substituted for each ? in order — usually field references like ${field} |
Returns an empty string if no row matches.
Example — look up a province name by ID:
pulldata('rawquery',
concat(${family_path}, '/locations.db::provinces'),
'SELECT name FROM provinces WHERE id = ?',
${province_id})Example — look up a ward name filtered by both province and district codes:
pulldata('rawquery',
concat(${family_path}, '/locations.db::wards'),
'SELECT name FROM wards WHERE province_code = ? AND district_code = ?',
${province_code},
${district_code})Unlike the standard four-argument pulldata() which matches a single key column in a CSV file, the rawquery variant executes a full SQL SELECT against a SQLite database, allowing multi-column WHERE conditions and any column as the return value.
rawquery is commonly used with autocomplete fields for hierarchical location lookups. See Local Database Search for details on building .db files and wiring them to pulldata('rawquery', ...).