In the MX8 platform, surveys are written in Python using the Survey class.
Surveys should always start with the code block:
from survey import Survey
s = Survey(**globals())
You can import other Python libraries that are part of the standard library, e.g. datetime and math, but not modules that are not in the standard library, like pandas.
Response value types
Question methods return typed response values such as StringResponse, IntResponse, ListResponse,
and DictResponse. These behave like the corresponding Python str, int, list, and dict values,
so they can be compared, indexed, and iterated normally. BoolResponse supports normal boolean checks.
Response values also retain survey metadata. Tags passed to a question are available through the
response's topics dictionary and as attributes:
answer = s.select_question(
"Which brand?",
options=["Acme", "Example"],
tags=s.tag(category="brand"),
)
if answer.category == "brand":
s.store_value("selected_brand", answer)
Every response value exposes is_valid and error_message. The response classes named in signatures
describe values returned by the survey runtime; survey scripts do not need to import those classes.
Survey
Survey class for creating surveys.
exposure_sources
@property
exposure_sources: list[str]
Return exposure sources, tolerating decoded objects without the new state.
respondent
@property
respondent: Respondent
Return normalized context for the current respondent.
Use this property when survey logic needs to branch based on normalized respondent metadata supplied at launch time.
Automatic reporting
Reading a respondent field adds a calculated survey variable so that the value is available
in reporting. Its stable reporting ID is respondent_{field}, such as
respondent_device_type. Question names replace underscores with spaces, such as
Respondent device type. Repeated access updates the same calculated element. languages
is reported as a multi-value field; an empty value is reported as an empty multi-value.
Other missing respondent fields are reported as empty strings.
The source field is added automatically when its launch-time name is available, so
survey authors do not need to access it explicitly for reporting.
Matching helpers such as is_country(...) and has_language(...) report the underlying
field they access. get_provider_metadata(...) does not automatically report arbitrary
provider values. Access does not change launch metadata or overwrite historical stored
responses.
Available fields
The respondent context exposes:
source: source name captured at launch time, falling back to the source type.country: canonical MX8 country code, or None when unavailable.languages: tuple of canonical MX8 language codes in preference order.browser: browser family parsed from the user agent, or None.device: device family parsed from the user agent, or None.os: operating system family parsed from the user agent, or None.device_type: one of "mobile", "tablet", "pc", "bot", "other", or None.
Matching helpers
Use is_country(...), is_any_country(...), has_language(...), and
has_any_language(...) to match country or language values by code, name, or synonym.
Use get_provider_metadata(...) to read provider metadata values as strings.
Example
from survey import Survey
s = Survey(**globals())
if s.respondent.is_country("United States"):
s.select_question(
"Which region do you live in?",
options=["Northeast", "Midwest", "South", "West"],
tags=s.tag(country=s.respondent.country),
)
if s.respondent.device_type == "mobile":
s.show_message("Thanks for taking this on your phone.")
style
@property
style: Literal['traditional', 'chat']
Return the style of the survey.
Can be either 'traditional' or 'chat' depending on the configuration of the survey theme.
disable_bot_detection
disable_bot_detection() -> None
Disable bot detection mechanisms for this survey.
Appropriate use
Use this method when respondents are interacting with an interviewer who completes the survey, rather than responding on their own devices at home or work.
Bot detection tradeoff
This disables bot detection for the whole survey. Only use it when the survey collection mode makes automated bot checks inappropriate.
tag
tag(**kwargs: Any) -> dict[str, Any]
Create tags for questions, piping, or temporary tag contexts.
Context manager use
When used in a with s.tag(...): block, the tags are attached to every question created
inside that block.
Direct use
When passed as tags=s.tag(...), the tags are attached only to that question.
Piping
Tags can also provide values for placeholders such as {brand} in question text.
Example
from survey import Survey
s = Survey(**globals())
# Using as a context manager
with s.tag(brand="Ford", category="SUV"):
s.text_question("What do you like about {brand} {category}s?")
s.numeric_question("How many {brand} {category}s have you owned?")
# Passing directly to a question
s.text_question(
"Why is {brand} your favorite?",
tags=s.tag(brand=brand_name),
)
Parameters
| Name | Type | Description |
|---|---|---|
**kwargs | Any | Key-value pairs to use as tags. |
standard_screener
standard_screener(
country: str | None = None,
name: str = 'GenPop',
id: str | None = None,
) -> None
Add a standard screener to the survey.
For many countries, MX8 has a standard screener that can be used to screen respondents. This is a standard set of questions that are used to screen respondents for the survey. The questions are standard demo questions and include weights to national averages.
Availability
Standard screeners are available only for supported country and screener-name combinations.
Weighting
Standard screener questions include weights to national averages where the selected screener supports them.
Parameters
| Name | Type | Description |
|---|---|---|
country | str | None | The country to use for the screener. This is the country code in the MX8 system. For example, "US" for the United States, "GB" for Great Britain, or "AU" for Australia. |
name | str | The name of the screener to use. This is the name of the screener in the MX8 system. The default is "GenPop". |
id | str | None | Optional stable identifier prefix for generated screener questions. If not set the question text is used. |
show_message
show_message(
message: str,
image: MediaItem | None = None,
require_response: bool = True,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Show a message to the user.
Presents a message to the user. This could be an introductory message, a warning, or any text based piece of information.
Response requirement
By default respondents must acknowledge the message before continuing. Set
require_response=False to show the message without requiring acknowledgement.
Example
from survey import Survey
s = Survey(**globals())
s.show_message("Welcome to our market research survey! Your feedback is"
"important to us as it helps us understand market trends"
"and consumer behavior. This survey is designed to gather"
"information about your experiences and preferences related"
"to our industry and company. Your responses will be kept"
"confidential and used only for research purposes. Thank"
"you for taking the time to participate!")
Parameters
| Name | Type | Description |
|---|---|---|
message | str | The message to present to the user |
require_response | bool | Whether the user must acknowledge the message |
image | MediaItem | None | An image to show with the message, from the s.media collection |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
get_consent
get_consent(
consent_text: str | None = None,
topic: str | None = None,
client: str | None = None,
image: MediaItem | None = None,
number_seconds: int = 0,
id: str | None = None,
) -> BoolResponse
Get consent for the survey.
Asks the user the standard MX8 consent question. Terminates the respondent if they do not agree to the consent.
Consent termination
Respondents who do not agree to the consent question are terminated automatically.
Custom consent text
Pass consent_text to replace the standard MX8 consent text. topic, client,
and image customize the standard consent question.
Example
from survey import Survey
s = Survey(**globals())
s.get_consent()
Parameters
| Name | Type | Description |
|---|---|---|
consent_text | str | None | Custom consent text to show instead of the standard MX8 consent text. |
topic | str | None | Topic to display in the standard consent question. |
client | str | None | Client description to use in the standard consent question. |
image | MediaItem | None | Image to show with the consent question, from the s.media collection. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
text_question
text_question(
question: str,
image: MediaItem | None = None,
default: str | list[str] | list[list[str]] | None = None,
short: bool = False,
recodes: dict[str, str] | None = None,
validation_instructions: str | None = None,
quality_threshold: int = 3,
termination_threshold: int = 1,
max_attempts: int = 3,
custom_validator: Callable[[str | list[str]], str | None] | None = None,
number_of_responses: int = 1,
min_responses: int | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> StringResponse | ListResponse
Add a text question to the survey.
Asks an open ended question to the respondent.
Clarification and termination
AI validation scores responses from 1 to 5. A response below quality_threshold asks the
respondent to clarify. On the final allowed attempt, a response at or above the lower
termination_threshold is accepted; a response below it terminates the survey. With the
default termination_threshold=1, no score on that scale causes termination.
Multiple responses
number_of_responses and min_responses are only supported in landscape orientation. For
multiple responses, AI validation is unavailable: leave quality_threshold,
termination_threshold, and max_attempts at their defaults and omit
validation_instructions. A custom_validator may still be used.
Example
from survey import Survey
s = Survey(**globals())
s.text_question("When you think about cars, which brands immediately come to mind?")
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.text_question(
"What do you like about {brand} cars?",
tags=s.tag(brand=brand),
)
Custom Errors
Text questions by default are validated by AI to ensure that the response is a valid response. You can provide custom instructions for the validation by using the validation_instructions parameter.
By default, users are given three attempts to meet quality_threshold. On the final attempt,
termination_threshold decides whether a lower-quality response is accepted or the survey
terminates. Set termination_threshold=1 to accept every score produced by the 1-to-5 validator.
If you do not want to use the AI validation, you can provide a custom_validator function that takes the response as a parameter and returns a string if the response is invalid. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if x == "Sleepy Cows" else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
default | str | list[str] | list[list[str]] | None | the simulated answer. For a single response, use a string or a list of candidate strings. For multiple responses, use one full list response or a list of list-response candidates. If omitted, test data uses generated text. |
short | bool | whether to use a short text input field |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
validation_instructions | str | None | a string describing AI validation rules for the question. Responses are scored on a scale of 1 to 5. |
quality_threshold | int | the normal minimum passing score on a scale of 1 to 5; defaults to 3 |
termination_threshold | int | the minimum score accepted on the final attempt; a lower score terminates the survey. Defaults to 1, which prevents score-based termination. |
max_attempts | int | the maximum number of attempts before applying termination_threshold |
custom_validator | Callable[[str | list[str]], str | None] | None | function called with the parsed response (a string for one response or a list of strings for multiple responses); return an error message to reject it, otherwise None |
number_of_responses | int | the number of responses to collect. Only supported in landscape orientation. |
min_responses | int | None | the minimum number of responses to collect. Only supported in landscape orientation. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
qualitative_text_question
qualitative_text_question(
question: str,
instructions: str | None = None,
max_follow_ups: int = 3,
default: str | list[str] | None = None,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> StringResponse
Run a bounded qualitative interview starting from one open question.
The opening answer is returned as a normal :class:StringResponse. Any
AI-generated follow-up answers are stored as additional string responses
with the same reporting ID and ordered transcript metadata.
Example
from survey import Survey
s = Survey(**globals())
response = s.qualitative_text_question(
"What makes choosing a current account difficult?",
instructions="Understand the moments that create uncertainty and the trade-offs people make.",
max_follow_ups=3,
)
Returns: The opening turn's ordinary string response.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | Required respondent-facing opening question. |
instructions | str | None | Optional researcher brief used only to guide AI probing. |
max_follow_ups | int | Maximum number of AI-generated questions after the opening turn. |
default | str | list[str] | None | Optional simulated response or list of candidate responses. |
tags | dict[str, Any] | None | Tags attached to every answered interview turn. |
id | str | None | Optional stable identifier shared as the reporting ID for every interview turn. |
text_highlighter_question
text_highlighter_question(
question: str,
text_to_highlight: str,
color: str = '#FFFF00',
default: str | list[str] | list[list[str]] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> ListResponse
Add a text highlighter question to the survey.
Asks the respondent to highlight one or more parts of a given text. Every selection must
be a nonempty, case-sensitive substring of text_to_highlight; empty strings, missing
substrings, and non-string selection items are rejected.
Example
from survey import Survey
s = Survey(**globals())
s.text_highlighter_question(
"Please highlight the parts of the following text that you find most interesting:",
text_to_highlight="The quick brown fox jumps over the lazy dog."
)
Raises
SurveyValidationError if color is not in six-digit #RRGGBB form.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
text_to_highlight | str | the text that the respondent will highlight parts of |
color | str | highlight color as exactly six hexadecimal digits prefixed by #, for example #FFFF00; hexadecimal letter case is ignored. |
default | str | list[str] | list[list[str]] | None | default highlights to use when generating test data. Pass a string for one highlight, a list of strings for one full highlight response, or a list of highlight-list candidates. If omitted, test data uses random highlights. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
numeric_question
numeric_question(
question: str,
image: MediaItem | None = None,
min_max: tuple[int, int] = (1, 10),
default: int | list[int] | None = None,
recodes: dict[str, str] | None = None,
style: Literal['input', 'button'] = 'input',
custom_validator: Callable[[int], str | None] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> IntResponse
Add a numeric question to the survey.
This presents the user with a numeric input for choosing a value between a range of numbers.
Range validation
The respondent must enter a number between the inclusive minimum and maximum values in
min_max.
Example
from survey import Survey
s = Survey(**globals())
s.numeric_question("What year were you born in?",
min_max=(1900, 2024))
Recodes
Recodes can be used to change the values of the responses.
Recodes for numeric question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Set style="button" with explicit finite range recodes to show the
ranges as buttons. The selected range stores the integer midpoint.
Percentage and open-ended recode keys such as "0-50%" and "65+"
are not valid in button style.
Example
from survey import Survey
s = Survey(**globals())
s.numeric_question("How old are you?",
min_max=(18, 100),
recodes={
"18-25": "18-25",
"26-54": "26-54",
"55-74": "55-74",
"75-100": "75+"
})
s.numeric_question("How many times have you visited our website?",
min_max=(0, 100),
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High"
})
Default values
If no default value is specified, then the test data will consist of random values between the min and max value.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.numeric_question(
"How many {brand} cars have you owned?",
min_max=(0, 10),
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High",
},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention, e.g.
custom_validator=lambda x: "Are you sure that you're 150 years old" if x > 150 else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
min_max | tuple[int, int] | the inclusive minimum and maximum numeric values allowed |
default | int | list[int] | None | the default value or candidate values to use when generating test data |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
style | Literal['input', 'button'] | "input" for numeric entry or "button" for recode range buttons |
custom_validator | Callable[[int], str | None] | None | function called with the parsed integer response; return an error message to reject it, otherwise None |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
select_question
select_question(
question: str,
options: list[str | MediaItem],
image: MediaItem | None = None,
randomize: bool = False,
other_options: list[str] | None = None,
disabled_options: list[str] | None = None,
fixed_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = 'Please specify',
default: str | list[str] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[str | int], str | None] | None = None,
skip_empty: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
style: Literal['default', 'button', 'list', 'dropdown'] = 'default',
sorted: Literal['ascending', 'descending'] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> StringResponse | IntResponse
Add a multiple-choice question to the survey.
This presents the user with a number of choices from which the user can choose exactly one.
Example
from survey import Survey
s = Survey(**globals())
s.select_question(
"What is your gender?",
options=["Male", "Female", "Non-Binary"],
)
Randomization
Set randomize to True to randomize the order of the options. If you have any additional options you do not want to have randomized, then you can specify these in the fixed_options parameter.
Default values
If no default value is specified, then the test data will consist of random values chosen from the options. If a list is provided, one value is sampled once when the question is created.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question on a five point scale, but want to store the response on a three point scale, you can use recodes to map the response to the correct value.
Disabled Options
Options can be disabled using the disabled_options parameter. This is useful if you want to disable an option based on a previous response. For example, if you want to ask a question about what they enjoyed about the experience, and then ask about what they didn't enjoy, you can disable the options they selected in the first question.
Other, please specify
If you want to allow the user to specify an option that is not in the list, you can set the specify_option parameter. This will present the user with an additional option to specify their own value.
from survey import Survey
s = Survey(**globals())
s.select_question("On a scale of 1 to 5, how much do you like this product?",
options=["1", "2", "3", "4", "5"],
recodes={
"1": "dislike",
"2": "dislike",
"3": "neutral",
"4": "like",
"5": "like"})
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.select_question(
"How many {brand} cars have you owned?",
options=["0", "1", "2", "3", "4+"],
recodes={
"0": "never owned",
"1": "single owner",
"2": "loyalist",
"3": "loyalist",
"4+": "enthusiast",
},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if x == "Sleepy Cows" else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or items from s.media containing the options |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | whether to randomize the order of the options |
other_options | list[str] | None | a list of additional options to present to the user |
disabled_options | list[str] | None | a list of strings containing options to be disabled |
fixed_options | list[str] | None | a list of strings containing the fixed options |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option; defaults to "Please specify" |
default | str | list[str] | None | the default value or candidate values to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[str | int], str | None] | None | function called with the parsed selection (an integer when sorted is set); return an error message to reject it, otherwise None |
image_label_field | str | None | the field to use as the label for the image if using media items in the options |
show_image_label | bool | whether to show the image label or just to show the image |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
style | Literal['default', 'button', 'list', 'dropdown'] | presentation style for the options. |
sorted | Literal['ascending', 'descending'] | None | Optional reporting sort direction, either "ascending" or "descending". When set, reporting stores numeric scale values using option order while the respondent UI remains a select question. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
multi_select_question
multi_select_question(
question: str,
options: list[str | MediaItem],
max_options: int | None = None,
image: MediaItem | None = None,
randomize: bool = False,
other_options: list[str] | None = None,
disabled_options: list[str] | None = None,
fixed_options: list[str] | None = None,
exclusive_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = 'Please specify',
default: list[str] | list[list[str]] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[list[str]], str | None] | None = None,
skip_empty: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
style: Literal['default', 'button', 'list', 'dropdown'] = 'default',
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> ListResponse
Add a multi-select question to the survey.
This presents the user with a number of choices from which the user can choose one or more.
Example
from survey import Survey
s = Survey(**globals())
s.multi_select_question(
"Which types of transport do you use?",
options=["Car", "Bus", "Train", "Bicycle"],
)
Randomization
Set randomize to True to randomize the order of the options. If you have any additional options you do not want to have randomized, then you can specify these in the fixed_options parameter.
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Disabled Options
Options can be disabled using the disabled_options parameter. This is useful if you want to disable an option based on a previous response. For example, if you want to ask a question about what they enjoyed about the experience, and then ask about what they didn't enjoy, you can disable the options they selected in the first question.
Exclusive options
Options listed in exclusive_options are mutually exclusive with other selected options.
Other, please specify
If you want to allow the user to specify an option that is not in the list, you can set the specify_option parameter. This will present the user with an additional option to specify their own value.
from survey import Survey
s = Survey(**globals())
enjoyment = s.multi_select_question("What did you enjoy about the experience?",
options=["The food", "The service", "The atmosphere", "The price"],
fixed_options=["Other"],
exclusive_options=["Other"],
recodes={
"The food": "food",
"The service": "service",
"The atmosphere": "atmosphere",
"The price": "price",
"Other": "other"})
if "Other" in enjoyment:
s.text_question("What else did you enjoy about the experience?")
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.select_question(
"Which of the following do you associate with {brand} cars?",
options=["Reliability", "Safety", "Performance", "Luxury"],
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or items from s.media containing the options |
max_options | int | None | the maximum number of options to be selected, defaults to all options |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | whether to randomize the order of the options |
other_options | list[str] | None | a list of additional options to present to the user |
disabled_options | list[str] | None | a list of strings containing options to be disabled |
fixed_options | list[str] | None | a list of strings containing the fixed options |
exclusive_options | list[str] | None | a list of strings containing the exclusive options |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option; defaults to "Please specify" |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
default | list[str] | list[list[str]] | None | the default selection or candidate selections to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[list[str]], str | None] | None | function called with the parsed selections; return an error message to reject them, otherwise None |
image_label_field | str | None | the field to use as the label for the image if using media items in the options |
show_image_label | bool | whether to show the image label or just to show the image |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
style | Literal['default', 'button', 'list', 'dropdown'] | presentation style for the options. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
grid_select_question
grid_select_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
options: list[str | MediaItem],
style: Literal['button', 'radio'] = 'radio',
randomize: bool = False,
randomize_options: bool = False,
other_options: list[str] | None = None,
fixed_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = 'Please specify',
image: MediaItem | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[dict[str, str] | str | int], str | None] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
skip_empty: bool = False,
default: dict[str, str | list[str]] | list[dict[str, str | list[str]]] | None = None,
sorted: Literal['ascending', 'descending'] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a grid select question to the survey.
This presents the user with a grid of options where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_select_question(
"Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
options=["Poor", "Fair", "Good", "Very Good", "Excellent"],
)
Randomization
Set randomize=True to randomize rows and randomize_options=True to randomize options.
fixed_options remain fixed when option randomization is used.
Default values
Defaults are provided as a dictionary keyed by row, or as a list of candidate dictionaries. Per-row list values are still treated as sorted select candidates and are sampled once when the question is created.
Other, please specify
Use specify_option to add an option where the respondent can provide their own value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows or no options.
Style
style controls the grid control style, either "button" or "radio".
Recodes: Recodes map responses to different values. This is useful when you want to classify responses into categories.
s.grid_select_question(
"Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
options=["Poor", "Fair", "Good", "Very Good", "Excellent"],
recodes={
"Poor": "1",
"Fair": "2",
"Good": "3",
"Very Good": "4",
"Excellent": "5",
},
)
Custom Errors:
By default, custom_validator is set to a straight-line check that raises an error if
the respondent enters the same value for each cell. You can override this by passing a
custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | The question to ask the respondent. |
rows | list[str | MediaItem] | Strings or media items containing the rows. |
row_name | str | Label to use for each row in topics. |
options | list[str | MediaItem] | Strings or media items containing the options. |
style | Literal['button', 'radio'] | The style of the question ("button" or "radio"). Defaults to "radio". |
randomize | bool | Whether to randomize the order of the rows. |
randomize_options | bool | Whether to randomize the order of the options. |
other_options | list[str] | None | Additional options to present to the user. |
fixed_options | list[str] | None | Fixed options that should not be randomized. |
specify_option | str | None | An "other" option that the respondent can specify. |
specify_text | str | Text to show when the respondent selects specify_option. |
image | MediaItem | None | An image to show with the grid, from the s.media collection. |
recodes | dict[str, str] | None | A dictionary of recodes to apply to the question. |
custom_validator | Callable[[dict[str, str] | str | int], str | None] | None | function called with the parsed grid in landscape orientation, or each parsed row response in portrait orientation; sorted portrait rows use integers. Return an error message to reject the response, otherwise None. |
image_label_field | str | None | The field to use as the label for media items in the rows. |
show_image_label | bool | Whether to show the image label or just the image in the rows. |
image_size | tuple[int, int] | None | The bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
skip_empty | bool | If True, skip the question when there are no rows or options. |
default | dict[str, str | list[str]] | list[dict[str, str | list[str]]] | None | Per-row defaults or candidate default dictionaries. Per-row list values are sampled once at creation. |
sorted | Literal['ascending', 'descending'] | None | Optional reporting sort direction, either "ascending" or "descending". When set, reporting stores numeric scale values using option order while the respondent UI remains a grid select question. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | dict[str, Any] to attach to this question, from s.tag(...). |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
grid_multi_select_question
grid_multi_select_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
options: list[str | MediaItem],
style: Literal['button', 'checkbox'] = 'checkbox',
randomize: bool = False,
randomize_options: bool = False,
other_options: list[str] | None = None,
fixed_options: list[str] | None = None,
exclusive_options: list[str] | None = None,
specify_option: str | None = None,
specify_text: str = 'Please specify',
image: MediaItem | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[dict[str, list[str]] | list[str]], str | None] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
skip_empty: bool = False,
default: dict[str, list[str]] | list[dict[str, list[str]]] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a grid multi-select question to the survey.
This presents the user with a grid of options where they can select multiple options from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_multi_select_question("What do you associate with these brands? Select all that apply",
rows=["Coke", "Pepsi", "Dasani", "Fanta"],
row_name="Brand",
options=["Tasty", "Refreshing", "Cool", "Value", "Convenient"])
Randomization
Set randomize=True to randomize rows and randomize_options=True to randomize options.
fixed_options remain fixed when option randomization is used.
Default values
Defaults are provided as a dictionary keyed by row, or as a list of candidate dictionaries.
Exclusive options
Options listed in exclusive_options are mutually exclusive with other selected options
in the same row.
Other, please specify
Use specify_option to add an option where the respondent can provide their own value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows or no options.
Style
style controls the grid control style, either "button" or "checkbox".
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Example
from survey import Survey
s = Survey(**globals())
s.grid_multi_select_question("What do you associate with these brands? Select all that apply",
row_name="Brand",
rows=["Coke", "Pepsi", "Dasani", "Fanta"],
options=["Tasty", "Refreshing", "Cool", "Value", "Convenient"],
recodes={
"Tasty": "Product",
"Refreshing": "Product",
"Cool": "Brand",
"Value": "Pricing",
"Convenient": "Distribution"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | the rows to show in the grid |
row_name | str | the name of the row |
options | list[str | MediaItem] | the options available for each row |
style | Literal['button', 'checkbox'] | the question style, either "button" or "checkbox"; defaults to "checkbox" |
randomize | bool | whether to randomize the order of the rows |
randomize_options | bool | whether to randomize the order of the options |
other_options | list[str] | None | additional options to present to the user |
fixed_options | list[str] | None | options that should remain fixed in place |
exclusive_options | list[str] | None | options that should be mutually exclusive |
specify_option | str | None | an "other" option that the respondent can specify |
specify_text | str | text to show when the respondent selects specify_option |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, list[str]] | list[dict[str, list[str]]] | None | default selections or candidate default dictionaries to use when generating test data |
skip_empty | bool | if True, skip the question when there are no rows |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
custom_validator | Callable[[dict[str, list[str]] | list[str]], str | None] | None | function called with the parsed grid in landscape orientation or each parsed row list in portrait orientation; return an error message to reject the response, otherwise None |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
grid_rating_question
grid_rating_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
image: MediaItem | None = None,
number_of_points: int | None = None,
style: Literal['slider', 'button', 'star'] = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
randomize: bool = False,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
skip_empty: bool = False,
custom_validator: Callable[[dict[str, int] | int | str], str | None] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
default: dict[str, int] | list[dict[str, int]] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a grid rating question to the survey.
This presents the user with a grid of options where they can rate each row on user defined scale.
Example
from survey import Survey
s = Survey(**globals())
s.grid_rating_question("Please rate the following aspects of the product",
rows=["Quality", "Price", "Service", "Delivery"],
row_name="Product Aspect",
number_of_points=5)
Randomization
Set randomize=True to randomize the row order.
Default values
Defaults are provided as a dictionary keyed by row, or as a list of candidate dictionaries.
Style and labels
style, number_of_points, first_point, and labels control the rating scale shown
for each row. Button and slider styles support between 2 and 10 points, while the star style
supports between 2 and 5 points.
Using the don't know option
Use dont_know_option to add a "Don't know" option. It is reported using the provided
text and stored as a sentinel value.
Skip empty
If skip_empty=True, the question is skipped when there are no rows.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the response to the correct value.
Example
from survey import Survey
s = Survey(**globals())
s.grid_rating_question("Please rate the following aspects of the product",
row_name="Product Aspect",
rows=["Quality", "Price", "Service", "Delivery"],
number_of_points=5,
recodes={
"1": "Poor",
"2": "Poor",
"3": "Fair",
"4": "Fair",
"5": "Good"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | a list of strings or media items containing the rows |
row_name | str | the name of the row |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
number_of_points | int | None | the number of points to rate the row on. Button and slider styles support between 2 and 10 points, while the star style supports between 2 and 5 points. Defaults to 5 or the number of labels. |
style | Literal['slider', 'button', 'star'] | the style of the question, either "slider", "button" or "star" |
first_point | int | None | the value of the first point, defaults to 1 or the first label |
labels | dict[int, str] | None | a dictionary of labels to use on the question |
randomize | bool | whether to randomize the order of the rows |
default | dict[str, int] | list[dict[str, int]] | None | default ratings or candidate default dictionaries to use when generating test data |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
dont_know_option | str | label to use for the don't-know option, if any. |
skip_empty | bool | if set to True, the question will be skipped if there are no rows |
custom_validator | Callable[[dict[str, int] | int | str], str | None] | None | function called with the parsed grid in landscape orientation or each parsed rating in portrait orientation; don't-know rows use strings. Return an error message to reject the response, otherwise None. |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display. If omitted, the default rendered size is 600x600. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
grid_numeric_question
grid_numeric_question(
question: str,
rows: list[str | MediaItem],
row_name: str,
columns: list[str] | None = None,
column_name: str | None = None,
image: MediaItem | None = None,
min_max: tuple[int, int] = (1, 10),
randomize: bool = False,
randomize_columns: bool = False,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[dict[str, int] | int], str | None] | None = None,
autosum_columns: bool = False,
autosum_rows: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
default: dict[str, int | dict[str, int]] | list[dict[str, int | dict[str, int]]] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a grid numeric question to the survey.
This presents the user with a grid of options where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.grid_numeric_question("How many hours do you spend per week on the following activities?",
rows=["Work", "Sleep", "Exercise", "Socializing"],
row_name="Activity",
columns=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
column_name="Day",
randomize=True,
)
Randomization
Set randomize=True to randomize rows and randomize_columns=True to randomize columns.
Default values
Defaults are provided as a dictionary keyed by row, or by row and column when columns are used. You can also pass a list of candidate default dictionaries.
Range validation
Each numeric response must be between the inclusive minimum and maximum values in min_max.
Autosum
Set autosum_columns or autosum_rows to require responses to sum correctly by column or row.
Recodes
Recodes can be used to change the values of the responses.
Recodes for numeric question take the format of a dictionary where the key is the range of values to be recoded, and the value is the value to recode to. You can also use percentages to recode a percentage of the values.
Example
from survey import Survey
s = Survey(**globals())
s.grid_numeric_question("How many hours do you spend per week on the following activities?",
rows=["Work", "Sleep", "Exercise", "Socializing"],
row_name="Activity",
columns=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
column_name="Day",
randomize=True,
recodes={
"0-30%": "Low",
"31-70%": "Medium",
"71-100%": "High"
})
Custom Errors
By default, the custom_validator parameter is set to a straight-line check that raises an error if the respondent enters the same value for each cell but you can override this by passing a custom validator function.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
rows | list[str | MediaItem] | a list of strings containing the rows |
row_name | str | the name of the row |
columns | list[str] | None | a list of strings containing the columns |
column_name | str | None | the name of the column |
image | MediaItem | None | an image to show with the grid, from the s.media collection |
min_max | tuple[int, int] | the minimum and maximum values for the question |
randomize | bool | whether to randomize the order of the rows |
randomize_columns | bool | whether to randomize the order of the columns |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
default | dict[str, int | dict[str, int]] | list[dict[str, int | dict[str, int]]] | None | default values or candidate default dictionaries to use when generating test data |
autosum_columns | bool | whether to autosum the columns |
autosum_rows | bool | whether to autosum the rows |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display. If omitted, the default rendered size is 600x600. |
custom_validator | Callable[[dict[str, int] | int], str | None] | None | function called with the parsed grid in landscape orientation or each parsed number in portrait orientation; return an error message to reject the response, otherwise None |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
this_or_that_question
this_or_that_question(
question: str,
row_options: list[list[str | MediaItem]],
randomize: bool = False,
randomize_columns: bool = False,
default: dict[str, str | list[str]] | list[dict[str, str | list[str]]] | None = None,
recodes: dict[str, str] | None = None,
image: MediaItem | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
custom_validator: Callable[[dict[str, str] | str], str | None] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a this or that question to the survey.
This presents the user with a series of questions where they can select one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Randomization
Set randomize=True to randomize row order and randomize_columns=True to randomize the
order of options within each row.
Default values
Defaults are provided as a dictionary keyed by generated row ID (row1, row2, and so on)
in landscape orientation, or as a list of candidate dictionaries. Within each dictionary, a
list value supplies candidates from which one option is sampled once when the question is
created. The same one-based default keys are used in portrait orientation.
Return keys
The return value is a DictResponse of selected StringResponse values. Landscape keys are
one-based (row1, row2, ...); the portrait per-question flow currently returns zero-based
keys (row0, row1, ...).
Media options
row_options may contain media items. Use image_label_field, show_image_label, and
image_size to control media option labels and display size.
Recodes
Recodes map each exact selected option string to its reported value, for example
{"Apples": "fruit", "Oranges": "fruit"}. Numeric ranges and percentage keys are not
supported for this choice-based question.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Custom validation
custom_validator runs on the parsed response. Return a string to reject it, or None to
accept it.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
row_options | list[list[str | MediaItem]] | a list of lists containing the options |
randomize | bool | whether to randomize the order of the rows |
randomize_columns | bool | whether to randomize the order of the columns |
recodes | dict[str, str] | None | mappings from exact option strings to reported values |
default | dict[str, str | list[str]] | list[dict[str, str | list[str]]] | None | per-row defaults keyed by generated row ID, or candidate default dictionaries; per-row list values are sampled once at creation |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_label_field | str | None | the media field to use as the image label |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display. If omitted, the default rendered size is 600x600. |
custom_validator | Callable[[dict[str, str] | str], str | None] | None | function called with the parsed grid in landscape orientation or each parsed choice in portrait orientation; return an error message to reject the response, otherwise None |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
this_or_that_rating_question
this_or_that_rating_question(
question: str,
row_options: list[list[str]],
row_name: str = 'scale',
number_of_points: int | None = None,
style: Literal['slider', 'button'] = 'slider',
first_point: int | None = None,
dont_know_option: str = '',
randomize: bool = False,
default: dict[str, int] | list[dict[str, int]] | None = None,
recodes: dict[str, str] | None = None,
image: MediaItem | None = None,
custom_validator: Callable[[dict[str, int] | int | str], str | None] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> DictResponse
Add a this or that rating question to the survey.
This presents the user with a series of questions where they can rate one option from each row.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_rating_question("Which of the following do you prefer?",
number_of_points=5,
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Randomization
Set randomize=True to randomize row order.
Default values
Defaults are provided as a dictionary keyed by the generated endpoint name, such as
"Apples to Oranges", or as a list of candidate dictionaries.
Return keys
The return value is a DictResponse keyed by those endpoint names. Each value is the parsed
IntResponse, or the don't-know sentinel after post-processing.
Style and scale
style, number_of_points, first_point, and row_options define the rating scale shown
for each row. Button and slider styles support between 2 and 10 points.
Using the don't know option
Use dont_know_option to add a "Don't know" option. It is reported using the provided
text and stored as a sentinel value.
Recodes
Recodes map exact selected point strings such as "1" or "5" to reported values. They do
not use range or percentage syntax.
Example
from survey import Survey
s = Survey(**globals())
s.this_or_that_rating_question("Which of the following do you prefer?",
row_options=[
["Apples", "Oranges"],
["Coke", "Pepsi"],
["Dogs", "Cats"],
["Winter", "Summer"]
])
Custom validation
custom_validator runs on the parsed response. Return a string to reject it, or None to
accept it.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
row_options | list[list[str]] | a list of lists containing the options |
row_name | str | the name of the row (default "scale") |
number_of_points | int | None | the number of points to rate the row on. Button and slider styles support between 2 and 10 points. Defaults to 5. |
style | Literal['slider', 'button'] | the style of the question, either "slider" or "button" |
first_point | int | None | the value of the first point, defaults to 1 |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
randomize | bool | whether to randomize the order of the rows |
recodes | dict[str, str] | None | mappings from exact point strings to reported values |
default | dict[str, int] | list[dict[str, int]] | None | ratings keyed by endpoint name, such as "Apples to Oranges", or candidate default dictionaries |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
custom_validator | Callable[[dict[str, int] | int | str], str | None] | None | function called with the parsed grid in landscape orientation or each parsed rating in portrait orientation; don't-know rows use strings. Return an error message to reject the response, otherwise None. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
show_image
show_image(
image: MediaItem,
number_seconds: int = 5,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Show an image to the respondent.
This will show an image to the respondent for a set number of seconds.
Timed display
The image is displayed for number_seconds seconds before the respondent can continue.
Response requirement
The respondent does not provide an answer; this method is for controlled image exposure.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.show_image(image=media, number_seconds=5)
Parameters
| Name | Type | Description |
|---|---|---|
image | MediaItem | the image to show |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
number_seconds | int | the number of seconds to show the image |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
play_video
play_video(
video: MediaItem,
end_message: str = 'Thank you for watching the video!',
start_message: str = "Hit play when you're ready to watch the video.",
dial: str = 'none',
dial_labels: dict[int, str] | None = None,
dial_caption: str = 'Please adjust the slider to reflect your feelings as you watch the video.',
start_position: int | None = None,
number_seconds: int = 0,
id: str | None = None,
) -> StringResponse | BoolResponse | IntResponse | ListResponse | None
Play a video to the respondent.
This will play a video to the respondent and then present them with a message at the end, which defaults to "Thank you for watching the video!".
Video security
Videos are DRM protected to prevent copying or screen recording. Secure videos are watermarked with the respondent's IP address, and video links expire after 60 seconds.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.play_video(video=media)
# Now ask questions about the video.
Dial testing
You can also add a dial to the video to allow the respondent to indicate their feelings as they watch the video. The dial can be set to one of the following values:
- "none" - no dial is shown
- "right" - a dial is shown on the right of the video
- "bottom" - a dial is shown at the bottom of the video
The dial defaults to a range of 0-100, with captions "Dislike", "Neutral", and "Like" but you can set the labels and caption for the dial using the dial_labels and dial_caption parameters.
Parameters
| Name | Type | Description |
|---|---|---|
video | MediaItem | the video to play, from the s.media collection |
end_message | str | the message to show the respondent at the end of the video |
start_message | str | the message to show the respondent at the start of the video |
dial | str | the position of the dial, either "none", "right", or "bottom", defaults to "none" |
dial_labels | dict[int, str] | None | the labels to show on the dial |
dial_caption | str | the caption to show above the dial |
start_position | int | None | starting value for the dial; defaults to the midpoint of the dial labels |
number_seconds | int | Seconds to wait before allowing the respondent to continue after the video. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
show_embedded_media
show_embedded_media(question: str, url: str, duration: int) -> StringResponse
Embed external content from a URL in the survey.
Supported providers
Embedded media currently supports YouTube URLs only.
Example
from survey import Survey
s = Survey(**globals())
s.show_embedded_media("Watch this video", "https://www.youtube.com/watch?v=2gcsgfzqN8k", duration=231)
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to show above the embedded media |
url | str | the URL to embed in the survey |
duration | int | the number of seconds the respondent must wait before continuing |
location_question
location_question(
field: str = 'postal_code',
question: str = 'What is your zipcode?',
validation_field: str | None = 'city',
validation_question: str = 'Which city best describes where you live?',
report: Sequence[str] = ('region',),
strict: bool = False,
style: Literal['text', 'autocomplete'] = 'text',
country: str | None = None,
tags: dict[str, Any] | None = None,
default: str | list[str] | None = None,
number_options: int = 5,
number_seconds: int = 0,
id: str | None = None,
) -> StringResponse
Ask the respondent for their postal code and derive reportable location fields.
Location capture uses a text postal-code question. Set validation_field=None to skip the
optional location-confirmation question.
Report fields and IDs
report must be a nonempty, non-string sequence of fields supported for the selected
country. Unsupported report fields add survey validation errors. The id applies to the
derived report fields, not the postal-code capture: a single report field uses id, while
multiple fields use {id}-{field}.
Strict validation
With strict=True, a respondent is terminated when the postal-code capture or optional
validation question leaves an active validation warning. This is separate from survey
configuration validation.
Return value
Returns the entered postal code as a StringResponse; derived report fields are added for
reporting but are not returned.
Example
location = s.location_question(report=["region", "state", "market"])
Recodes
The location question does not support recodes.
Returns
StringResponse: the captured postal code.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the primary location question copy. |
validation_field | str | None | the location field to validate against. Use None to skip validation. |
validation_question | str | the validation follow-up question copy. |
report | Sequence[str] | nonempty sequence of country-supported fields to derive for reporting. |
strict | bool | whether to terminate when respondent-level location validation leaves a warning. |
country | str | None | optional country override. Defaults to respondent country or US. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
default | str | list[str] | None | the postal code or candidate postal codes to use in test data. If omitted, test data randomly selects a valid postal code from the selected country's location data. |
number_options | int | the number of matching locations to show the respondent for validation. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
id | str | None | Optional stable identifier for one derived report field, or prefix when reporting multiple fields. If omitted, each derived field uses its generated question identity. |
quota
quota(
name: str,
criteria: bool,
quota: float = 0.0,
min_respondents: int = 0,
max_respondents: int | None = None,
) -> Quota
Create a quota line.
Use this to define an individual quota line that can be passed to s.set_quota().
Quota line only
This method only defines a quota line. Pass the returned quota to s.set_quota() to apply
quota logic to the survey.
Minimum and maximum respondents
min_respondents enforces a minimum fill for the line. max_respondents caps the number
of respondents assigned to the line.
Example
from survey import Survey
s = Survey(**globals())
age = s.numeric_question(question="How old are you?",
min_max=(18, 100),
recodes={
"0-17": "Under 18",
"18-34": "18-34",
"35-54": "35-54",
"55-120": "55+"
})
gender = s.select_question(question="What is your gender?",
options=["Male", "Female"])
s.set_quota(
name="Quads",
quotas=[
s.quota("Younger Men", criteria=(age < 40) & (gender == "Male"), quota=0.25),
s.quota("Older Men", criteria=(age >= 40) & (gender == "Male"), quota=0.25),
s.quota("Younger Women", criteria=(age < 40) & (gender == "Female"), quota=0.25),
s.quota("Older Women", criteria=(age >= 40) & (gender == "Female"), quota=0.25)
]
)
You can also specify a minimum number of respondents that must be included in this quota line by passing the min_respondents parameter.
Example
from survey import Survey
s = Survey(**globals())
s.set_quota(
name="Age Quota",
quotas=[
s.quota("Under 18", criteria=(age < 18), quota=0.1, min_respondents=50),
s.quota("18-34", criteria=(age >= 18) & (age < 35), quota=0.3, min_respondents=100),
s.quota("35-54", criteria=(age >= 35) & (age < 55), quota=0.3, min_respondents=100),
s.quota("55+", criteria=(age >= 55), quota=0.3, min_respondents=100)
]
)
If you only pass in values with min_respondents, then the quota will not be used for weighting, but it will still be used to terminate respondents that do not meet the criteria.
You can optionally set max_respondents to cap the number of respondents assigned to a quota line. If omitted, that line has no explicit cap.
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name to give this quota line when it is reported |
criteria | bool | the boolean condition based on responses to other questions that the respondent must meet to be included in this quota line |
quota | float | the proportion of respondents that should fall into this quota |
min_respondents | int | the minimum number of respondents that must be included in this quota line |
max_respondents | int | None | optional maximum number of respondents for this quota line |
set_quota
set_quota(name: str, quotas: list[Quota], exclusive: bool = True) -> QuotaResponse
Set a quota group based on a number of quota lines.
Takes a group of individual quotas from s.quota() and creates a quota group.
Termination behavior
Respondents are terminated when they do not match any quota line or when their matching quota line is over quota.
Exclusive quotas
By default quotas are exclusive and each respondent fills one matching quota line. If
exclusive=False, respondents may be included in multiple matching quota lines.
Example
from survey import Survey
s = Survey(**globals())
age = s.numeric_question(question="How old are you?",
min_max=(18, 100),
recodes={
"0-17": "Under 18",
"18-34": "18-34",
"35-54": "35-54",
"55-120": "55+"
})
gender = s.select_question(question="What is your gender?",
options=["Male", "Female"])
# Check age and gender quotas
s.set_quota(
name="Quads",
quotas=[
s.quota("Younger Men", criteria=(age < 40) & (gender == "Male"), quota=0.25),
s.quota("Older Men", criteria=(age >= 40) & (gender == "Male"), quota=0.25),
s.quota("Younger Women", criteria=(age < 40) & (gender == "Female"), quota=0.25),
s.quota("Older Women", criteria=(age >= 40) & (gender == "Female"), quota=0.25)
]
)
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of this group of quotas, for example "demos" |
quotas | list[Quota] | a list of quotas to be added, each created using s.quota() |
exclusive | bool | whether the quotas are exclusive, defaults to True. If set to False, then respondents can be included in multiple quota lines, but they will still be terminated if they do not meet any of the quota criteria. |
get_least_filled
get_least_filled(number: int, from_list: list[Any], quota: str) -> list[Any]
Get the least filled quotas from a list of elements in a quota.
Selects the least-filled items from a list for balanced follow-up questions.
Selection only
This function returns the least-filled items from the list, but does not terminate the respondent based on quota state.
Terminating by quota
To terminate respondents based on quota state, use s.set_quota() to establish quotas.
Example
from survey import Survey
s = Survey(**globals())
brand_list = ['Acura', 'Alfa Romeo', 'Aston Martin', 'Audi', 'Bentley',
'BMW', 'Bugatti', 'Buick', 'Cadillac', 'Chevrolet', 'Chrysler',
'Citroen', 'Dodge', 'Ferrari', 'Fiat', 'Ford', 'Geely', 'Genesis',
'GMC', 'Honda', 'Hyundai', 'Infiniti', 'Jaguar', 'Jeep', 'Kia',
'Koenigsegg', 'Lamborghini', 'Land Rover', 'Lexus', 'Lincoln',
'Lotus', 'Maserati', 'Mazda', 'McLaren', 'Mercedes-Benz', 'Mini',
'Mitsubishi', 'Nissan', 'Pagani', 'Peugeot', 'Polestar', 'Porsche',
'Ram', 'Renault', 'Rolls-Royce', 'Subaru', 'Suzuki', 'Tesla', 'Toyota',
'Volkswagen', 'Volvo']
brands = s.get_least_filled(number=10,
from_list=brand_list,
quota="Brand familiarity")
familiar_brands = s.multi_select_question(
question="Which of the following car brands have you heard of?",
options=brands)
Parameters
| Name | Type | Description |
|---|---|---|
number | int | the number of items to return |
from_list | list[Any] | the list of items to choose from |
quota | str | the name of the quota to check against |
terminate
terminate(reason: str, number_seconds: int = 0) -> NoReturn
Terminate the survey.
Use this when the respondent does not qualify for a specific reason.
Immediate termination
This immediately adds a terminal survey element and stops execution.
Quota termination
Use s.set_quota() for quota-based termination; it also terminates respondents who do not
qualify for the quotas.
Example
if not has_xbox:
s.terminate(
"Sorry you don't qualify for this survey "
"because you don't have an Xbox"
)
Parameters
| Name | Type | Description |
|---|---|---|
reason | str | The reason to give to the user to explain why they don't qualify. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
terminate_if
terminate_if(condition: bool, reason: str, number_seconds: int = 0) -> None
Terminate the survey if a condition is met.
Conditionally stops the survey for a respondent.
Condition evaluation
If condition is true, this calls s.terminate(reason) immediately. If it is false,
survey execution continues.
Example
s.terminate_if(
not has_xbox,
"Sorry you don't qualify for this survey because you don't have an Xbox",
)
Parameters
| Name | Type | Description |
|---|---|---|
condition | bool | The condition to check and terminate the user if it's True |
reason | str | The reason to give to the user to explain why they don't qualify. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
complete
complete(
message: str = 'Thank you for completing our survey! See you again.',
number_seconds: int = 0,
) -> None
Complete the survey.
Call this function at the end of the survey to complete the survey and thank the user.
Completion behavior
This adds a non-terminated completion element. Use s.terminate() instead when the
respondent should be screened out.
Example
from survey import Survey
s = Survey(**globals())
# Add questions here
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
message | str | The message to display to the user upon completing the survey. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
rating_question
rating_question(
question: str,
image: MediaItem | None = None,
number_of_points: int | None = None,
style: Literal['slider', 'button', 'star'] = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
default: int | list[int] | None = None,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
custom_validator: Callable[[int | str], str | None] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> IntResponse
Add a rating-scale question to the survey.
This presents the respondent with a numeric scale and records the selected point as an integer response.
There are three available styles:
- style="slider" - For the slider style, the user can drag the slider.
- style="button" - For the button style, the user can click on the button they want.
In both the slider and button styles, the rating scale is presented as a number with optional text labels for appropriate points on the scale.
Style and scale
style, number_of_points, first_point, and labels control how the rating scale is
displayed and what values it records.
Example
from survey import Survey
s = Survey(**globals())
s.rating_question("How much do you like this product?",
number_of_points=5,
style="slider",
labels={
1: "Dislike",
3: "Neutral",
5: "Like"
})
Default values
If no default value is specified, then the test data will consist of random values chosen from the options.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and classify it into different categories, you can use recodes to map the top two box response to a different value.
from survey import Survey
s = Survey(**globals())
s.rating_question("On a scale of 1 to 5, how much do you like this product?",
number_of_points=5,
style="slider",
labels={
1: "Dislike",
3: "Neutral",
5: "Like"
},
recodes={
"1": "dislike",
"2": "dislike",
"3": "neutral",
"4": "like",
"5": "like"})
Using the don't know option
If you want to add a "Don't know" option to the question, then you can specify the text to use for the "Don't know" option using the dont_know_option parameter. If the user selects this option then the response will be recorded as a value of -999 in the survey data and reported as the text specified in the dont_know_option parameter.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.rating_question(
"How do you rate {brand} cars?",
number_of_points=5,
style="slider",
labels={1: "Dislike", 3: "Neutral", 5: "Like"},
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
number_of_points | int | None | the number of points on the scale (default to 5 or the number of labels) |
first_point | int | None | the first point on the scale (default to 1 or the first label) |
style | Literal['slider', 'button', 'star'] | the style of the scale, either "slider", "button", or "star" |
labels | dict[int, str] | None | a dictionary of text labels for each point on the scale |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
default | int | list[int] | None | the default value or candidate values to use when generating test data |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
custom_validator | Callable[[int | str], str | None] | None | function called with the parsed rating; the don't-know option is a string. Return an error message to reject the response, otherwise None. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
net_promoter_score_question
net_promoter_score_question(
question: str,
image: MediaItem | None = None,
number_of_points: int | None = 11,
style: Literal['slider', 'button', 'star'] = 'slider',
first_point: int | None = None,
labels: dict[int, str] | None = None,
default: int | list[int] | None = None,
recodes: dict[str, str] | None = None,
dont_know_option: str = '',
custom_validator: Callable[[int | str], str | None] | None = None,
min_promoter_score: int | None = None,
max_detractor_score: int | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> IntResponse
Add a Net Promoter Score question to the survey.
This asks the respondent how likely they are to recommend a product, brand, service, or experience. By default, it uses an 11-point scale from 0 to 10 with endpoint labels for "Not at all likely" and "Extremely likely".
The available styles are "slider", "button", and "star". The scale is numeric and
may include text labels at any point. If labels are omitted, the first and last actual
scale points receive the standard NPS endpoint labels.
Style and scale
first_point defaults to 0 and number_of_points defaults to 11, producing the standard
0-to-10 scale. At least two points are required. A shifted scale runs from first_point
through first_point + number_of_points - 1.
Classified response
The selected raw scale point is classified before it is returned, stored, or reported:
- detractor (
score <= max_detractor_score):IntResponse(-100) - passive (between the thresholds):
IntResponse(0) - promoter (
score >= min_promoter_score):IntResponse(100)
The exact selected point is therefore not retained.
Promoter and detractor scoring
Thresholds are inclusive. Supply min_promoter_score and max_detractor_score together;
each must be a non-boolean integer on the actual scale and the promoter threshold must be
strictly greater than the detractor threshold. Defaults retain the standard relative
positions and shift with first_point: a 0-to-10 scale uses detractor 0-6, passive 7-8,
and promoter 9-10.
Example
from survey import Survey
s = Survey(**globals())
s.net_promoter_score_question(
"How likely are you to recommend this product to a friend or colleague?",
number_of_points=11,
style="slider",
labels={
0: "Not at all likely",
10: "Extremely likely",
},
)
Default, simulated, and transcript responses
default is a raw point on the displayed scale, but is immediately classified to
-100, 0, or 100. Without a default, simulation selects and classifies a random raw
point. When a classified value must be replayed in a simulated transcript, it is converted
to a valid representative raw point; on the standard scale the transcript values for
detractor, passive, and promoter are 6, 7, and 9 respectively. This conversion is
deliberately lossy and cannot recover an exact original point.
Recodes
Recodes operate on classified string keys "-100", "0", and "100", not raw score
keys. The configured don't-know label may also be recoded.
from survey import Survey
s = Survey(**globals())
s.net_promoter_score_question(
"How likely are you to recommend this product to a friend or colleague?",
number_of_points=11,
style="slider",
recodes={
"-100": "detractor",
"0": "passive",
"100": "promoter",
},
)
Imported responses
Native imported values -100, 0, and 100 are treated as already-classified buckets so
exports round-trip. Other valid scale points are accepted and classified as raw input. An
imported 0 is therefore the passive bucket, not raw score zero.
Distinct don't-know response
When dont_know_option is configured, selecting it stores that label as the parsed response,
passes the string value to custom_validator, and returns -999 to survey code. Reporting
and recoding use the configured label, and transcript reconstruction converts -999 back to
that label. Empty input remains invalid when no don't-know option is configured.
Historical stored zero values remain passive because an older don't-know selection cannot be
recovered from them.
Returns
IntResponse: -100 for a detractor, 0 for a passive, 100 for a promoter, or -999
when the configured don't-know option is selected.
Using the don't know option
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
for brand in car_brands:
s.net_promoter_score_question(
"How likely are you to recommend " + brand + " cars to a friend or colleague?",
number_of_points=11,
style="slider",
dont_know_option="Don't know",
tags=s.tag(brand=brand),
)
Custom validation
custom_validator runs after parsing and classification but before post-processing. Return
a string to reject the response, or None to accept it.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
number_of_points | int | None | number of scale points; defaults to 11 and must be at least 2 |
first_point | int | None | first point on the scale; defaults to 0 or the first supplied label |
style | Literal['slider', 'button', 'star'] | the style of the scale, either "slider", "button", or "star" |
labels | dict[int, str] | None | point labels. By default the actual endpoints receive "Not at all likely" and "Extremely likely". |
image | MediaItem | None | an image to show with the question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
default | int | list[int] | None | raw scale point or candidate raw scale points to classify and use when generating test data |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
recodes | dict[str, str] | None | mappings for classified keys "-100", "0", and "100", plus the optional don't-know label |
custom_validator | Callable[[int | str], str | None] | None | function called with the classified NPS response; numeric responses are -100, 0, or 100, while don't know is a string. Return an error message to reject it, otherwise None. |
min_promoter_score | int | None | inclusive minimum raw score classified as promoter; supply with max_detractor_score |
max_detractor_score | int | None | inclusive maximum raw score classified as detractor; supply with min_promoter_score |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
ranking_question
ranking_question(
question: str,
options: list[str | MediaItem],
min_options: int | None = None,
max_options: int | None = None,
style: Literal['default', 'button', 'list'] = 'button',
labels: tuple[str, str] = ('Least', 'Most'),
randomize: bool = False,
fixed_options: list[str] | None = None,
other_options: list[str] | None = None,
skip_empty: bool = False,
default: list[str] | list[list[str]] | None = None,
recodes: dict[str, str] | None = None,
custom_validator: Callable[[list[str]], str | None] | None = None,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
dont_know_option: str = '',
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
image: MediaItem | None = None,
) -> ListResponse
Add a ranking question to the survey.
This presents the user with a number of choices to rank in order of preference.
Ranking limits
min_options and max_options control how many options the respondent must rank.
Randomization
Set randomize=True to randomize option order. fixed_options remain fixed when
randomization is used.
Other options
Use other_options to add additional options to the ranking list.
Using the don't know option
Use dont_know_option to add a "Don't know" option to the question.
Example
from survey import Survey
s = Survey(**globals())
s.ranking_question("Rank the following car brands in order of preference",
options=["Ford", "Toyota", "Honda", "Tesla"],
labels=("Least", "Most"),
)
Default values
If no default value is specified, then the test data will consist of the options returned in a random order.
Recodes
Recodes are used to map the response to a different value. This is useful for when you want to map a response to a different value. For example, if you want to ask a question about what they enjoyed about the experience, and ranks the different activities, you can use recodes to map the top two box response to a different value.
from survey import Survey
s = Survey(**globals())
s.ranking_question("Rank the following activities in order of preference",
options=["Eating", "Drinking", "Sleeping", "Working"],
labels=("Least", "Most"),
recodes={
"Eating": "eating",
"Drinking": "drinking",
"Sleeping": "sleeping",
"Working": "working"
})
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
brand_cars = {
"Ford": ["Fiesta", "Focus", "Mustang", "F150"],
"Toyota": ["Corolla", "Camry", "Prius", "RAV4"],
"Honda": ["Civic", "Accord", "CR-V", "Pilot"],
"Tesla": ["Model S", "Model 3", "Model X", "Model Y"],
}
for brand in car_brands:
s.ranking_question(
"Rank the following {brand} cars in order of preference?",
options=brand_cars[brand],
labels=("Least", "Most"),
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
options | list[str | MediaItem] | a list of strings or media items containing the options |
min_options | int | None | the minimum number of options to be ranked, defaults to all options |
max_options | int | None | the maximum number of options to be ranked, defaults to all options |
style | Literal['default', 'button', 'list'] | presentation style for the options. |
labels | tuple[str, str] | a tuple of text labels for the least and most options |
randomize | bool | whether to randomize the order of the options |
fixed_options | list[str] | None | a list of options that will not be randomized |
other_options | list[str] | None | a list of additional options to present to the user |
default | list[str] | list[list[str]] | None | the default ranking or candidate rankings to use when generating test data |
skip_empty | bool | if set to True, the question will be skipped if there are no options |
recodes | dict[str, str] | None | response mappings to use when reporting this question |
dont_know_option | str | Optionally add a "Don't know" option to the question using this text. |
custom_validator | Callable[[list[str]], str | None] | None | function called with the parsed ranking; return an error message to reject it, otherwise None |
image_label_field | str | None | the field to use as the label for the image if using media items in the rows |
show_image_label | bool | whether to show the image label or just to show the image in the rows |
image_size | tuple[int, int] | None | the bounding box size of the images to display. If omitted, the default rendered size is 600x600. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
image | MediaItem | None | an image to show with the question, from the s.media collection |
media
@property
media: MediaCollection
Access the media collection.
The media collection contains images and videos that can be used in questions. The media are added to the survey automatically and can be uploaded from the MX8 dashboard. Additional columns added in the dashboard will be added to the media collection.
Each item in the media collection is a MediaItem object. You can access the media collection by iterating over the media object, and pass it to the image parameter of questions.
Columns can be accessed using dot notation. For example, if you have a column called "brand" in your media collection, then you can access it using media.brand. You can also get all the values of a column using the get_media_values function.
If you have not uploaded any media to the survey, then a dummy media collection will be used that contains dummy images with the following columns:
- name
- description
- product
- brand
- campaign
- agency
- id
You can create your own metadata columns in the MX8 dashboard and these will be added to the media collection after you have created the survey and uploaded the media.
The columns are automatically used when reporting the results. For example, if you have a column called "brand" and you ask a question about the brand, then the results will be automatically grouped by brand.
Example
from survey import Survey
s = Survey(**globals())
for media in s.media:
s.select_question("What do you think of this image?",
options=["Like", "Dislike"],
image=media)
get_media_values
get_media_values(column: str) -> list[str]
Get all values of the passed metadata column from the media collection.
This is useful if you want to get all the values of a column in the media collection and use them to create questions.
Example
from survey import Survey
s = Survey(**globals())
car_brands = s.get_media_values("brand")
for car in s.media:
s.select_question("What brand is this car?",
options=car_brands,
image=car)
Parameters
| Name | Type | Description |
|---|---|---|
column | str | the name of the media metadata column to read from each media item. The column must exist on each media item. |
get_image
get_image(**kwargs: Any) -> MediaItem
Get the image with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Single match
Exactly one image must match. The lookup raises ValueError when no image or more than one
image matches.
Example
from survey import Survey
s = Survey(**globals())
ford_image = s.get_image(brand="Ford")
s.show_image(image=ford_image)
s.complete()
get_video
get_video(**kwargs: Any) -> MediaItem
Get the video with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Single match
Exactly one video must match. The lookup raises ValueError when no video or more than one
video matches.
Example
from survey import Survey
s = Survey(**globals())
ford_video = s.get_video(brand="Ford")
s.play_video(video=ford_video)
s.complete()
get_images
get_images(**kwargs: Any) -> list[MediaItem]
Get the images with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Multiple matches
This returns all matching images. The lookup raises ValueError when no image matches.
Example
from survey import Survey
s = Survey(**globals())
for image in s.get_images(brand="Ford"):
s.show_image(image=image)
s.complete()
get_videos
get_videos(**kwargs: Any) -> list[MediaItem]
Get the videos with the specified values from the media collection.
Media lookup
Keyword arguments filter the media collection by metadata fields.
Multiple matches
This returns all matching videos. The lookup raises ValueError when no video matches.
Example
from survey import Survey
s = Survey(**globals())
for video in s.get_videos(brand="Ford"):
s.play_video(video=video)
s.complete()
store_value
store_value(name: str, value: str | int | bool, tags: dict[str, Any] | None = None) -> StringResponse | BoolResponse | IntResponse
Store a calculated value for reporting.
Use this to add a value calculated from previous answers to the survey data. For survey logic, keep using normal Python variables instead of retrieving stored reporting values.
Reporting
Stored values are added to the survey as calculated variables and can be tagged.
Example
from datetime import datetime
from survey import Survey
s = Survey(**globals())
birth_year = s.numeric_question("What year were you born in?",
min_max=(1920, 2020),
)
age = datetime.now().year - birth_year
s.store_value("age", age)
s.terminate_if(age < 18, "Sorry this survey is for 18+ only.")
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of the value to store |
value | str | int | bool | the value to store for reporting |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
store_values
store_values(
name: str,
values: list[str | int | bool],
tags: dict[str, Any] | None = None,
) -> ListResponse
Store multiple calculated values for reporting.
Use this to add a multiselect-style calculated value from previous answers to the survey data. For survey logic, keep using normal Python variables instead of retrieving stored reporting values.
Reporting
Stored values are added to the survey as calculated multi-select variables and can be tagged.
Example
from survey import Survey
s = Survey(**globals())
brands = s.multi_select_question("Which brands do you know?", ["A", "B", "C"])
s.store_values("Known brands bucket", list(brands), tags=s.tag(source="derived"))
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of the value to store |
values | list[str | int | bool] | the values to store for reporting |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
time_between
time_between(first_response: BaseResponse, second_response: BaseResponse) -> float | None
Return the elapsed seconds between two responses.
Returns None when either response has not been persisted or its timestamp is unavailable.
Example
from survey import Survey
s = Survey(**globals())
age = s.numeric_question("How old are you?", min_max=(18, 99))
gender = s.select_question("What is your gender?", options=["Female", "Male", "Non-binary"])
elapsed_seconds = s.time_between(age, gender)
if elapsed_seconds is not None and elapsed_seconds < 1:
s.terminate("You are speeding through the survey.")
random
random() -> float
Return a random float between 0 and 1.
Respondent-stable randomness
The value is generated from the respondent's random seed or respondent id, so it is stable for the same respondent context.
randomize
randomize(items: Sequence[Any], flatten: bool = False) -> list[Any]
Randomizes a list of items.
Typically used when iterating over a list of items that you want to ask questions about.
Grouped items
Nested lists stay together during top-level randomization, so you can randomize a set of options while keeping grouped options adjacent.
Example
from survey import Survey
s = Survey(**globals())
car_brands = s.randomize(["Ford", "Toyota", "Honda", "Tesla"])
for brand in s.randomize(car_brands):
s.select_question(
"What do you think of {brand} cars?",
options=["Like", "Dislike"],
tags=s.tag(brand=brand),
)
Example with flattening
from survey import Survey
s = Survey(**globals())
genres = [
"Jazz",
["Punk Rock", "Classic Rock", "Indie Rock"], # keep this group together
"Hip-Hop",
["House", "Techno"], # keep this group together
]
all_genres = s.randomize(genres, flatten=True)
for genre in all_genres:
s.select_question(
"What do you think of {genre} music?",
options=["Like", "Dislike", "Neutral"],
tags=s.tag(genre=genre),
)
Parameters
| Name | Type | Description |
|---|---|---|
items | Sequence[Any] | The sequence of items to randomize. |
flatten | bool | If True, flatten any nested lists after randomizing the top-level items. |
conjoint_question
conjoint_question(
question: str,
concepts: list[dict[str, Any]] | list[MediaItem],
task_sets: dict[str, list[list[str]]],
concept_id_tag: str = 'id',
required_tags: list[str] | None = None,
dont_know_option: str = '',
randomize: bool = False,
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
) -> DictResponse
Add a choice-based conjoint question to the survey.
Presents one task set as a series of select-style questions.
Task assignment
Each respondent is assigned one task set using least-fill quota behavior.
Dict-backed concepts
Dict-backed concepts render non-id fields as a markdown list option label. The selected
response includes concept_id_tag and required_tags through option tags.
Media-backed concepts
Media-backed concepts use existing media option rendering, labels, and media tags.
Fixed don't know option
dont_know_option is added as a fixed non-concept option when provided.
Randomization
Set randomize=True to randomize option order within each conjoint task.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | The question to ask for each conjoint task. |
concepts | list[dict[str, Any]] | list[MediaItem] | Concept dictionaries or media items to use in tasks. |
task_sets | dict[str, list[list[str]]] | Mapping of task set id to tasks, where each task contains concept ids. |
concept_id_tag | str | Tag containing the concept id used by task sets. |
required_tags | list[str] | None | Concept tags to require and report with selected dict-backed concepts. |
dont_know_option | str | Optional fixed non-concept option label. |
randomize | bool | Whether to randomize option order within each task. |
image_label_field | str | None | Media field to use as the display label for media concepts. |
show_image_label | bool | Whether to show media labels. |
image_size | tuple[int, int] | None | Bounding box size for media options. If omitted, the default rendered size is 600x600. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | dict[str, Any] to attach to each conjoint task question. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
max_diff_question
max_diff_question(
question: str,
items: list[str | MediaItem] | list[list[str | MediaItem]],
labels: list[str],
image: MediaItem | None = None,
randomize: bool = False,
custom_validator: Callable[[dict[str, str] | str], str | None] | None = None,
dont_know_option: str = '',
image_label_field: str | None = None,
show_image_label: bool = True,
image_size: tuple[int, int] | None = None,
number_seconds: int = 0,
tags: dict[str, Any] | None = None,
id: str | None = None,
randomize_labels: bool = False,
) -> DictResponse
Add a MaxDiff question to the survey.
This presents the user with a series of sets of items, and asks the user to select the most and least preferred items in each set.
labels must contain exactly two values, normally the least- and most-preferred labels.
Randomization
Set randomize=True to randomize item order within each set. Set randomize_labels=True
to randomize the displayed order of the two labels. Within a set, portrait
orientation keeps the same item order for both label questions.
Using the don't know option
Use dont_know_option to add a fixed "Don't know" option to each MaxDiff task.
Example
from survey import Survey
s = Survey(**globals())
car_brands = ["Ford", "Toyota", "Honda", "Tesla", "Chevrolet", "BMW", "Audi", "Mercedes"]
s.max_diff_question("Which of the following car brands do you prefer?",
items=car_brands,
labels=["Least", "Most"])
Portrait orientation
Portrait orientation first shows question as an introductory message, then asks one
choice question for each label in every set. The item selected for the first label is
disabled for the second. The method supplies the label-specific follow-up copy, so no
{label} placeholder is required in question.
from survey import Survey
s = Survey(**globals())
car_brands = ["Ford", "Toyota", "Honda", "Tesla", "Chevrolet", "BMW", "Audi", "Mercedes"]
s.max_diff_question("Which of the following cars do you prefer?",
items=car_brands,
labels=["Least", "Most"])
Return value for multiple sets
Every generated set is asked and reported. The returned DictResponse contains only the
two selections from the final set; use reporting output rather than the return value when
all set-level selections are needed.
from survey import Survey
s = Survey(**globals())
car_brands = s.multi_select_question(
"When you think about cars, which brands immediately come to mind?",
options=["Ford", "Toyota", "Honda", "Tesla"],
)
brand_cars = {
"Ford": ["Fiesta", "Focus", "Mustang", "F150"],
"Toyota": ["Corolla", "Camry", "Prius", "RAV4"],
"Honda": ["Civic", "Accord", "CR-V", "Pilot"],
"Tesla": ["Model S", "Model 3", "Model X", "Model Y"],
}
for brand in car_brands:
s.max_diff_question(
"Which of the following {brand} cars do you prefer?",
items=brand_cars[brand],
labels=["Least", "Most"],
tags=s.tag(brand=brand),
)
Custom Errors
If you want to return a specific response to the user if they enter a specific value, you can use the custom_validator parameter. This is typically a lambda function that takes the response as a parameter and returns a string if the response is invalid.
It is typically used for "gotcha" questions, where the user is asked to enter a specific value to ensure they are paying attention.
e.g. custom_validator=lambda x: "Are you sure you meant Sleepy Cows?" if "Sleepy Cows" in x else None
Parameters
| Name | Type | Description |
|---|---|---|
question | str | the question to ask the respondent |
items | list[str | MediaItem] | list[list[str | MediaItem]] | items to compare, or predefined sets of items to compare |
labels | list[str] | the two labels to use for the most and least preferred items |
image | MediaItem | None | an image to show with the question, from the s.media collection |
randomize | bool | whether to randomize item order within each set |
randomize_labels | bool | whether to randomize the displayed order of the two labels |
custom_validator | Callable[[dict[str, str] | str], str | None] | None | function called with the parsed MaxDiff set in landscape orientation or each parsed label choice in portrait orientation; return an error message to reject the response, otherwise None |
dont_know_option | str | label to use for the don't-know option, if any. |
image_label_field | str | None | label field to use for media items in the options. |
show_image_label | bool | whether to show image labels for media options. |
image_size | tuple[int, int] | None | bounding box size for media options. If omitted, the default rendered size is 600x600. |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
tags | dict[str, Any] | None | Tags to attach to this question, from s.tag(...). You can also attach tags using the with s.tag(...): context manager. |
id | str | None | Optional stable identifier for this question. If not set the question text is used. |
count_validation_errors
count_validation_errors(question: str | None = None) -> int
Return the number of validation errors in the survey, or for a specific question.
Scope
If question is omitted or empty, this counts all current respondent validation warning
entries. If question is provided, warnings for every question with that exact text are
counted; a known question with no active warning returns zero.
Raises
KeyError if a nonempty question text does not match any survey element.
Parameters
| Name | Type | Description |
|---|---|---|
question | str | None | If nonempty, only count validation errors for questions with this exact text. |
authenticate_by_phone_number
authenticate_by_phone_number(
question_1: str,
question_2: str = 'Please enter the code we sent you.',
termination_message: str = 'Sorry, the code you entered was incorrect',
store_phone_number: bool = False,
image: MediaItem | None = None,
default_country: str | None = None,
number_seconds: int = 0,
id: str | None = None,
image_size: tuple[int, int] | None = None,
) -> StringResponse
Authenticate the respondent by phone number.
Asks the respondent for their phone number and sends a verification code.
OTP authentication
The respondent must enter the one-time password sent to their phone number before the survey continues.
Phone number storage
If store_phone_number=True, the response contains the phone number. Otherwise, the
stored response text is "obfuscated".
Response fields
The returned response always exposes the phone number as response.phone_number and the
one-time password as response.otp.
Example
from survey import Survey
s = Survey(**globals())
s.authenticate_by_phone_number("This is a high security survey, please enter your phone number" +
"to continue, and we will send you a verification code.")
Parameters
| Name | Type | Description |
|---|---|---|
question_1 | str | the question that asks the respondent to enter their phone number |
question_2 | str | the question that asks the respondent to enter the verification code |
termination_message | str | the message to show the respondent if the code is incorrect |
store_phone_number | bool | if set to True, the phone number will be stored in the survey data |
image | MediaItem | None | an image to show with the phone number question, from the s.media collection |
image_size | tuple[int, int] | None | the bounding box size of the image to display. If omitted, the default rendered size is 600x600. |
default_country | str | None | the default country code to use for the phone number |
number_seconds | int | Seconds to wait before allowing the respondent to continue. |
id | str | None | Optional stable identifier prefix for this question. If not set the question text is used. |
call_api
call_api(
url: str,
method: Literal['get', 'post', 'GET', 'POST'],
data: dict | None = None,
params: dict | None = None,
headers: dict | None = None,
timeout: int = 10,
) -> StringResponse
Call an external API on an allowlisted domain.
Allowed domains
External API calls are only available for pre-approved domains.
Request payloads
method accepts GET or POST, case-insensitively. Use params for GET query parameters and
data for POST request bodies. Any {respondent_id} placeholder in url is replaced with
the current respondent ID before the request is created.
Return value and simulation
A live call returns the response body as a StringResponse. Simulation and other
machine-generated runs skip the network request and return an empty StringResponse.
Timeout and errors
Only HTTP status 200 is accepted. A disallowed domain or any other response status raises
SurveyValidationError.
Returns
StringResponse: response body text, or an empty response during simulation.
Raises
SurveyValidationError if the domain is not allowlisted or the response status is not 200.
Parameters
| Name | Type | Description |
|---|---|---|
url | str | URL on an allowlisted domain. May contain {respondent_id}. |
method | Literal['get', 'post', 'GET', 'POST'] | request method, either GET or POST. |
data | dict | None | form data to send in the POST request body. |
params | dict | None | query parameters to include in the request. |
headers | dict | None | HTTP headers to include in the request. |
timeout | int | request timeout in seconds. |
get_all_children_age_gender
get_all_children_age_gender(
min_age: int,
max_age: int,
age_recodes: dict[int, str] | None = None,
gender_map: dict[str, str] | None = None,
question_1_text: str | None = None,
question_2_text: str | None = None,
question_3_text: str | None = None,
id: str | None = None,
) -> list[StringResponse]
Ask the user a series of questions to get the ages and genders of their children.
Returns a list of StringResponse objects, one for each child, with the following tags attached:
- age: the recoded age of the child
- gender: the gender of the child
- name: the name of the child to present to the respondent, e.g. 16 year old son.
Return shape and tags
The returned list contains one response per child in grid order, tagged with age, gender, and display name. Children with the same age and gender remain separate entries.
No children
If the user has no children, an empty list is returned.
Default question sequence
Unless custom question text is provided, this asks for child count, each child's age, and each child's gender.
Age recodes
age_recodes maps raw ages to reported age groups.
Gender mapping
gender_map maps gender responses to child terminology such as son or daughter.
The default questions are:
- "How many children do you have between the ages of {min_age} and {max_age}?"
- "For each child, please let us know their age"
- "Now, please let us know their gender"
By default, genders are Male and Female and are mapped to son and daughter using the gender_map parameter.
Age recodes may be specified as a dictionary mapping the age to the recode value, e.g.
{
5: "Pre-school",
6: "Primary",
7: "Primary",
8: "Primary",
9: "Primary",
10: "Primary",
11: "Secondary",
12: "Secondary",
13: "Secondary",
14: "Secondary",
15: "Secondary",
16: "Secondary",
17: "Secondary",
}
Example
from survey import Survey
s = Survey(**globals())
children = s.get_all_children_age_gender(
min_age=5,
max_age=18,
)
for child in children:
s.text_question(
"Thinking about {child}, what is their favorite subject in school?",
tags=s.tag(child=child.name),
)
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
min_age | int | the minimum age of the children |
max_age | int | the maximum age of the children |
age_recodes | dict[int, str] | None | a dictionary mapping raw ages to reported age labels |
gender_map | dict[str, str] | None | a dictionary to map genders to child terminology |
question_1_text | str | None | the text to use for the first question |
question_2_text | str | None | the text to use for the second question |
question_3_text | str | None | the text to use for the third question |
id | str | None | Optional stable identifier prefix for generated child questions. If not set the question text is used. |
kid_picker_question
kid_picker_question(
min_age: int = 13,
max_age: int = 17,
age_recodes: dict[int, str] | None = None,
gender_map: dict[str, str] | None = None,
question_1_text: str | None = None,
question_2_text: str | None = None,
question_3_text: str | None = None,
question_4_text: str | None = None,
id: str | None = None,
) -> StringResponse | None
Ask for child age and gender, then select the child available to take the survey.
This asks a series of questions to collect eligible children and then asks which child is available.
Returns the selected child as a StringResponse with age, gender, and name attributes.
Return shape and tags
The returned child can be used directly in survey logic and tags. The response value is the raw age
and gender, child.age is the recoded age if age_recodes is provided, child.gender is the raw
gender response, and child.name is the respondent-facing child label. These age, gender, and
name values are response tags. Children with the same name are numbered only in the availability
options so each child remains separately selectable.
When a child is selected, the helper also stores reportable calculated fields named
Selected child age and Selected child gender.
No children
None is returned when the respondent has no eligible children or selects the fixed None
option at the availability question.
Default question sequence
Unless custom question text is provided, this asks for child count, age, gender, and which child is available to take the survey now.
Age recodes
age_recodes maps raw ages to reported age groups.
Gender mapping
gender_map maps gender responses to child terminology such as son or daughter.
The default questions are:
- "How many children do you have between the ages of {min_age} and {max_age}?"
- "For each child, please let us know their age"
- "Now, please let us know their gender"
- "Which, if any, of your children are available to take the survey now?"
By default, genders are Male and Female and the children are referred to as son and daughter using the default gender map:
{
"Male": "son",
"Female": "daughter"
}
Age recodes can be specified as a dictionary mapping the age to the recode value as in the get_all_children question.
Example
from survey import Survey
s = Survey(**globals())
child = s.kid_picker_question(
min_age=5,
max_age=18,
)
if child:
s.show_message(
"Please ask your {child} to take the survey now.",
tags=s.tag(
child=child.name,
selected_child_age=child.age,
selected_child_gender=child.gender,
),
)
else:
s.terminate("Sorry, this survey is for children only.")
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
min_age | int | the minimum age of the children |
max_age | int | the maximum age of the children |
age_recodes | dict[int, str] | None | a dictionary mapping raw ages to reported age labels |
gender_map | dict[str, str] | None | a dictionary to map the genders to child terminology |
question_1_text | str | None | the text to use for the first question |
question_2_text | str | None | the text to use for the second question |
question_3_text | str | None | the text to use for the third question |
question_4_text | str | None | the text to use for the fourth question |
id | str | None | Optional stable identifier prefix for generated child picker questions. If not set the question text is used. |
get_exposed_values
get_exposed_values(
source: str,
exposed_dimension: str,
allowed_values: list[str] | None = None,
number_allowed_unknown_values: int = 0,
) -> ListResponse
Return a list of exposure values for a configured dimension.
Looks up exposure values from a configured exposure source.
Pre-loaded exposures
Exposure data must be pre-loaded into MX8 for the configured source and
exposed_dimension.
Exposure tags
Each returned value has a frequency tag containing the exposure count.
When recency data is available, it also has a days_since_exposure tag.
Example
from survey import Survey
s = Survey(**globals())
products = ["SUV", "Sedan", "Coupe"]
exposed_products = s.get_exposed_values(
source="campaign-123",
exposed_dimension="product",
allowed_values=products,
number_allowed_unknown_values=1,
)
for product in products:
with s.tag(product=product,
exposed=product in exposed_products):
s.select_question(
"What do you think of {product} cars?",
options=["Like", "Dislike"],
)
# Remaining question about the product...
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
source | str | the source of the exposures, set with the server side integration, e.g. 'campaign-123' |
exposed_dimension | str | the exposure dimension to return, e.g. 'brand' or 'product' |
allowed_values | list[str] | None | an optional list of known values to filter the returned exposures by |
number_allowed_unknown_values | int | the number of values that are not in the allow-list that are allowed before the question is failed. |
tag_exposed_media
tag_exposed_media(
source: str,
field_name: str,
exposed_dimension: str = 'brand',
number_allowed_unknown_values: int | None = None,
number_allowed_unknown_brands: int | None = None,
) -> None
Tags media items in the survey as exposed based on IP address matching.
Tags media items by matching a media metadata field against the respondent's exposure values.
Pre-loaded exposures
Exposure data must be pre-loaded into MX8 for the configured source and
exposed_dimension.
IP matching
Exposure lookup is based on the current respondent's IP address.
Exposure tags
Exposed media items receive a frequency tag containing the exposure count.
When recency data is available, they also receive a days_since_exposure tag.
Deprecated alias
number_allowed_unknown_brands is a deprecated alias for number_allowed_unknown_values.
Provide only one of these parameters.
Example
from survey import Survey
s = Survey(**globals())
s.tag_exposed_media(
source="campaign-123",
field_name="brand",
exposed_dimension="brand",
number_allowed_unknown_values=1,
)
for media in s.media:
s.select_question(
"What do you think of this car?",
options=["Like", "Dislike"],
image=media,
tags=s.tag(exposed=media.exposed),
)
s.complete()
Parameters
| Name | Type | Description |
|---|---|---|
source | str | the source of the exposures, set with the server side integration, e.g. 'campaign-123' |
field_name | str | the name of the field in the media collection to match against the exposures |
exposed_dimension | str | the exposure dimension to match against, defaults to 'brand' |
number_allowed_unknown_values | int | None | the number of values that are not in the allow-list that are allowed before the lookup is failed |
number_allowed_unknown_brands | int | None | deprecated alias for number_allowed_unknown_values |
insert_block
insert_block(name: str) -> None
Insert a block into the survey at the current position.
Blocks are defined in the MX8 dashboard and can be used to add custom questions to surveys without modifying the survey code.
Dashboard-defined blocks
The block name must refer to a block configured in the MX8 dashboard.
Missing blocks
If the named block is unavailable, block insertion fails during survey processing.
Parameters
| Name | Type | Description |
|---|---|---|
name | str | the name of the block |

