Documentation

Number Question

When and Why to Use

Use this when you need a numeric input within a defined range. It's best for:

  • Age, year, count, or frequency questions
  • Questions that require range-based recoding (e.g. age bands)
  • Validating attention checks with specific values

Supports optional recoding and validation logic.

Chat Experience
  • Respondent is shown a numeric input with increment/decrement buttons.
  • Range boundaries are visible.
  • If input is outside the range or invalid, an error message appears inline.
With an imageWithout an image
Markdown ImagesPlain Text
Traditional Experience
  • Same as chat, but with more space for contextual text or images.
  • Navigation and focus-friendly for web-based interactions.
With an imageWith markdownMobile optimized
Text ImagesMarkdown Many OptionsNumber Question Figure 01
Configuration Options
OptionTypeRequiredDefaultDescription
questionstringyes-The prompt shown to the user
imageMediaItemno-Optional image from s.media
image_sizeTuple[int, int]no(600, 600)Bounding box size for a displayed image
min_maxTuple[int, int]no(1, 100)The valid numeric range for answers
style"input" | "button"no"input""button" shows recode ranges as buttons, storing the midpoint
defaultintnorandom between rangeDefault test data for QA
recodesdict[str, str]no-Map ranges or percentages to buckets
custom_validatorCallable[[str], str | None]no-Function that returns a custom error message
number_secondsintno0Seconds to wait before allowing the respondent to continue
tagss.tag()no-Used to fill tokens in text and for reporting groupings
idstringno-Optional stable identifier for this question
Example Code

Simple numeric:

s.numeric_question("What year were you born in?", min_max=(1900, 2024))

With recodes:

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+" } )

With percentage-based recodes:

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" } )

With custom validator:

s.numeric_question( "How old are you?", min_max=(0, 120), custom_validator=lambda x: "Are you sure that you're 150 years old?" if x > 150 else None )

As range buttons:

s.numeric_question( "How old are you?", min_max=(18, 100), style="button", recodes={ "18-34": "18-34", "35-54": "35-54", "55-100": "55+" } )

With style="button", the recode ranges are shown as buttons instead of a numeric input, and the selected range stores its integer midpoint as the underlying value. Button style requires explicit finite-range recode keys — percentage and open-ended keys (like "0-50%" or "65+") are not supported in button style.

Recoding Numeric Responses

Sometimes it's useful to group numeric values into labeled categories - for instance, turning income ranges into brackets like "Low", "Middle", and "High". You define a set of rules that map numeric ranges to labels. Supported formats include:

  1. Fixed Ranges (e.g. "0-49" → "Low")
  2. Percentage Ranges (e.g. "0-25%")→ Interpreted relative to a known min and max.
  3. Open-ended Ranges (e.g. "80+")→ Interpreted as "80 and above".
Example 1: Satisfaction Score (0-100)
_recodes = { "0-25%": "Very Dissatisfied", "25-50%": "Dissatisfied", "50-75%": "Satisfied", "75-100%": "Very Satisfied" }

This scales the numeric value based on known min/max and assigns the appropriate label.

Example 2: Age Brackets
_recodes = { "0-17": "Underage", "18-64": "Adult", "65+": "Senior" }

In this case, "65+" means any value 65 or greater gets labeled "Senior".

What If No Match?

If the value doesn't fall into any defined range, the original number is returned unchanged.