humanize() turns an EDSL Survey into a web questionnaire that people can complete. It preserves the questions and survey logic while adding a respondent interface, an administration page, and response collection. Start with Human studies for a short end-to-end example; use this page to configure scenarios, respondent lists, and data retrieval.
This page covers code methods for launching surveys, gathering human responses, and analyzing results. For interactive survey building, see Survey Builder. For Prolific recruitment, see Prolific studies. For email notifications and scheduled reminders, see Survey notifications. To let AI agents take a survey, see AI agent access.
How it works
2
Humanize the survey to generate a web-hosted version on Expected Parrot with a respondent link and an admin dashboard.
3
Share the respondent link with human participants (or recruit via Prolific).
4
Collect responses back as a standard EDSL
Results object.5
Analyze your human results alongside any LLM results.
Basic usage
The example collects three measures of workplace experience: work-from-home frequency, satisfaction, and a suggested improvement. After defining the questions,.humanize() publishes them using your Expected Parrot account.
.humanize() parameters
Return value
survey.humanize() returns a Scenario (dict-like) with these keys:
Save the
uuid — you need it to retrieve responses.
Previewing before deployment
Use.preview() to get a preview URL without creating a live survey:
Present questions in groups
To request one group per page, define contiguous question groups on the survey and passhumanize_schema={"survey": {"presentation": "group"}} to .humanize(). See Grouped surveys for a complete example, branching behavior, and availability requirements.
Add your logo
To show your own logo above the questions, upload the image once and reference it from the humanize schema:Coop().list_human_survey_assets(page=1, page_size=20) lists a page of image-library metadata. Coop().get_human_survey_asset(asset_uuid) returns a HumanSurveyAsset; its download("logo.png") method saves the image locally. Coop().delete_human_survey_asset(asset_uuid) removes an asset when you intend to delete it.
From the CLI, upload once and apply it to a survey that already exists:
branding fields, and Custom CSS for resizing the logo or putting it on a colored banner.
With scenarios
When using scenarios, call.humanize() on a Jobs object (not directly on the survey). You must specify a scenario_list_method.
Use scenarios for stimulus material or treatment conditions. This example assigns each respondent one of three policy descriptions and asks for a support rating and its reason. Random assignment can select the same policy for several respondents:
Set question options from an earlier answer
Use a Jinja template forquestion_options when a later question’s choices depend on an earlier answer. Put the lookup data in a scenario; the survey server resolves the template after the respondent answers the first question and sends only the matching options to that page. A QuestionDropdown answer is a list, so [0] selects its single choice.
This example downloads O*NET’s Task Statements CSV, builds a task list for each occupation, then shows the selected occupation’s tasks in a checkbox question:
QuestionCompute variant below embeds the lookup in the survey when you need a self-contained definition.
Embed the lookup in a QuestionCompute
For a self-contained survey, you can put the lookup dictionary directly in a QuestionCompute template instead of attaching it as a scenario. The compute question reads the prior dropdown answer, and the checkbox receives its answer as its options:
QuestionCompute is an intermediate question; respondents see the occupation dropdown and task checkbox. This avoids a scenario attachment, but serializes the full lookup into the question text. For O*NET 31.0 that is about 1.9 MB, which worked in a local live survey. The scenario-backed version above keeps the question definition much smaller and is easier to maintain; embedding can be reasonable when a self-contained survey is more useful.
Scenario list methods
Rules:
- If you attach scenarios, you must specify
scenario_list_method. - If you specify a method, you must attach scenarios.
- Do not attach language models to a human-survey job. An AgentList can supply participant metadata and delivery addresses, as described below; it does not generate simulated responses.
With agent lists (for email notifications)
Attach anAgentList via .by() to associate respondents with email addresses. Pass a DeliveryMap to tell Coop which agent trait holds the email address.
col_name must match the trait key that holds each respondent’s email address. Once deployed, use Survey notifications to send invitations, schedule reminders, and track delivery status.
Retrieving responses
What the results contain
Each completed response is one result, oldest first. Retrieving data reads the recorded answers; it does not rerun the questionnaire. Skipped questions stay skipped. Column names match the responses table on the survey’s page. Selectscenario.* alongside answers when you need to compare the material or condition each respondent received.
Fields that show how a response was collected, such as
agent.collection_interface, are listed under AI agent access. The model.* columns describe a placeholder model: a human response has no model, but every result records one.
To get only some responses, pass started_after and/or started_before (inclusive; a response is “started” when the respondent opened the survey).
In-progress responses
By default you get completed responses only. Passinclude_partial=True to also get responses that are still being taken, for example to look at dropout or check on a survey that is still running:
agent.response_status == 'complete' before computing anything that assumes a finished survey. A response that is in progress now can have more answers, or be complete, the next time you fetch, so combine downloads by agent.response_uuid. include_partial works together with started_after and started_before.
Checking response count
Find surveys and inspect respondent progress
Use Python pagination when retrieving account records:coop.get_human_survey_agent_list(uuid) retrieves the attached respondent list configuration. To change its delivery mapping or participation settings, use coop.patch_human_survey_agent_list(...); see notifications and AI agent access.
For events logged by a survey’s custom JavaScript, coop.get_human_survey_events(uuid, limit=200) retrieves a batch. While has_more is true, pass the returned next_cursor as after to continue, or use coop.get_all_human_survey_events(uuid) to collect all batches. coop.count_human_survey_events(uuid) counts those events. Preview events are included and marked is_preview; exclude them from participant analyses. Use get_human_survey_responses() for answer data.
Working with file upload responses
If your survey includes aQuestionFileUpload question, each response’s answer for that question is a list of file-info dicts. Use FileStore.from_file_upload_answer (single file) or FileStoreList.from_file_upload_answers (all files at once) to download and wrap them:
FileStore and FileStoreList objects behave like any other — you can inspect, filter, or pass them into further EDSL workflows.
QR code
Generate a QR code for the respondent link usingget_human_survey_qr_code(). Requires the qrcode library (pip install "edsl[full]" or pip install "qrcode[pil]").
--qr-dir to generate one personal QR code per respondent and record each
PNG path in the CSV:
