The Call API feature lets a survey field make an HTTP request to an external service and use the response to populate a calculated value or validate user input. This enables real-time lookups, ID verification, barcode lookups, and any other server-side check during data collection.
There are two functions:
callapi()— fetches a value from an API and stores it in acalculate,text,integer,decimal, orselect_onefieldcallapi-verify()— calls an API and blocks progress if the response does not match the expected value; can be applied to any input field type (used inconstraint)
callapi() — Fetch and store an API response
Syntax
Place callapi() in the calculation column of a calculate, text, integer, decimal, or select_one field:
callapi(method, url, allowed_auto, max_retry, no_overwrite, extract_expr, timeout, lifetime, response_q, call_type, post_body)Parameters
| # | Parameter | Description |
|---|---|---|
| 1 | method | HTTP method: 'GET' or 'POST' |
| 2 | url | API endpoint URL |
| 3 | allowed_auto | 1 to call automatically when the field is reached; 0 to require a manual trigger button |
| 4 | max_retry | Maximum number of retry attempts on failure |
| 5 | no_overwrite | 1 to keep the existing value if the field already has one; 0 to always overwrite |
| 6 | extract_expr | Controls what gets stored. See extract_expr modes below. |
| 7 | timeout | Request timeout in milliseconds |
| 8 | lifetime | How long (ms) the cached response remains valid before re-fetching. 0 = no cache |
| 9 | response_q | Name of another field to receive the HTTP status code (e.g. 'response_status'). Leave '' if not needed. |
| 10 | call_type | (Optional) Special call mode: 'lazy-upload' or 'lazy-upload-multiple' |
| 11 | post_body | (Optional) POST body string. Inject field values with ##fieldname## |
extract_expr modes
| Value | What gets stored |
|---|---|
'$.path.to.value' | Extracts a single value from the JSON response using JSONPath |
'json:' | Stores the full raw JSON string — use with substr-jsonpath() to extract multiple fields |
'' (empty) | Stores the raw response body as a plain string |
Example: GET request to look up a household by ID
| type | name | label | appearance | calculation |
|---|---|---|---|---|
| text | household_id | Household ID | ||
| calculate | hh_name | callapi | callapi('GET', concat('https://api.example.com/households/', ${household_id}), 1, 3, 0, '$.name', 10000, 0, '') | |
| note | hh_display | Household: ${hh_name} |
Fetching multiple values from one API call
When you need several fields from the same response, set extract_expr to 'json:' to store the raw JSON string in the field, then extract individual values using substr-jsonpath() in separate calculate fields.
This avoids making a separate HTTP request for each field — the response is fetched once and reused.
| type | name | label | appearance | calculation |
|---|---|---|---|---|
| text | product_id | Product ID | ||
| calculate | product_raw | callapi | callapi('GET', concat('https://api.example.com/products/', ${product_id}), 1, 3, 0, 'json:', 10000, 0, '') | |
| calculate | product_name | substr-jsonpath(${product_raw}, '$.name') | ||
| calculate | product_price | substr-jsonpath(${product_raw}, '$.price') | ||
| calculate | product_category | substr-jsonpath(${product_raw}, '$.category') | ||
| note | product_display | ${product_name} — ${product_price} (${product_category}) |
The 'json:' prefix tells rtSurvey to store the full JSON response string instead of extracting a single value. See substr-jsonpath() for extraction syntax.
Example: POST request with a JSON body
callapi('POST', 'https://api.example.com/lookup', 1, 2, 0, '$.result.value', 15000, 0, '', '', '{"id": "##household_id##"}')In the post_body, use ##fieldname## to inject the current value of any form field.
Appearance: callapi
Add callapi to the appearance column of the field to enable the API call integration:
| type | name | label | appearance | calculation |
|---|---|---|---|---|
| calculate | api_result | callapi | callapi('GET', ...) |
When allowed_auto is 0, rtSurvey shows a "Fetch" button that the enumerator taps manually.
callapi-verify() — Validate a value against an API
callapi-verify() blocks submission of a field until an API confirms the entered value is valid. Use it in the constraint column.
Syntax
callapi-verify(method, url, extract_expr, match_expr, timeout, constraint_mode, post_body, message)Parameters
| # | Parameter | Description |
|---|---|---|
| 1 | method | HTTP method: 'GET' or 'POST' |
| 2 | url | Verification API endpoint |
| 3 | extract_expr | JSONPath to extract the expected value from the response |
| 4 | match_expr | Expression comparing extracted value to the field value (e.g., $.status = 'valid') |
| 5 | timeout | Request timeout in milliseconds |
| 6 | constraint_mode | 'soft' for a warning only; 'hard' to block progression |
| 7 | post_body | (Optional) POST body with ##fieldname## substitutions |
| 8 | message | (Optional) Error message. Supports multi-language: <en>Invalid!</en><vi>Không hợp lệ!</vi> |
Example: Verify a barcode against an asset registry
| type | name | label | appearance | constraint | constraint_message |
|---|---|---|---|---|---|
| barcode | asset_code | Scan the asset barcode | callapi-verify | callapi-verify('POST', 'https://api.example.com/assets/verify', '$.valid', ". = 'true'", 10000, 'hard', '{"code": "##asset_code##"}') | Asset not found in registry |
Appearance: callapi-verify(...)
The appearance column on a field with callapi-verify() in constraint should contain callapi-verify(params) or callapi-verify(dynamicParams) to signal to rtSurvey that this field uses API verification.
Handling API errors
Use response_q (param 9) to capture the HTTP status code in a separate field, then show conditional content based on whether the call succeeded.
| type | name | label | appearance | calculation | relevant |
|---|---|---|---|---|---|
| calculate | product_raw | callapi | callapi('GET', concat('https://api.example.com/products/', ${product_id}), 1, 3, 0, 'json:', 10000, 0, 'api_status') | ||
| calculate | api_status | ||||
| calculate | product_name | substr-jsonpath(${product_raw}, '$.name') | ${api_status} = '200' | ||
| note | error_msg | Product not found. | ${api_status} != '200' and ${api_status} != '' |
response_q takes the field name as a string (without ${}). The HTTP status is written to that field after the call completes.
To guard against missing fields in the response, use coalesce():
coalesce(substr-jsonpath(${product_raw}, '$.price'), 'N/A')Using App API data alongside callapi
Combine callapi() with pulldata('app-api', 'user.token') to inject the authenticated user's token into your API request:
callapi('POST', 'https://api.example.com/data', 1, 2, 0, '$.value', 10000, 0, '', '',
concat('{"token":"', pulldata('app-api','user.token'), '","id":"##item_id##"}'))Best Practices
- Always set a reasonable
timeout(5000–15000 ms) — do not use 0 or very high values. - Set
allowed_auto=0for verifications the enumerator should trigger consciously (e.g., ID checks). - Set
no_overwrite=1when the field may be edited later and you do not want re-fetching to overwrite manual corrections. - Use
extract_exprwith a specific JSONPath rather than relying on raw response text. - Test API calls in offline mode — callapi will fail gracefully and show an error, but the form should still be completable for non-critical fields.
Limitations
- Requires network connectivity at the time of the call — calls will fail when the device is offline.
- API responses must be JSON — XML or plain-text responses require additional parsing.
- High call frequency (e.g., one API call per repeat instance) can slow down form navigation.