Skip to content

alchemer_tools

Alchemer API wrapper module for managing survey data and interactions with the Alchemer platform.

Requires: - databricks-sdk - dbutils (Databricks utilities) - pandas (for some operations)

Author: Gino F. Fazzi, gino.franco.fazzi@audienceproject.com

alchemer_tools._read_alchemer_resource(filename)

Read a text resource bundled with the alchemer_utils package.

Package resources work both from the source tree and from an installed wheel, including on Databricks clusters where library files live under an ephemeral site-packages directory.

Parameters:

Name Type Description Default
filename str

Name of the resource file inside alchemer_utils, for example mappings.json.

required

Returns:

Type Description
str

File contents decoded as UTF-8.

alchemer_tools.AlchemerAPIError

Bases: RuntimeError

Raised when Alchemer accepts a request but reports it as unsuccessful.

Alchemer signals application-level failures by returning HTTP 200 with result_ok set to false in the response body, so these never surface as requests.HTTPError. Catch this to handle a rejected operation (an unknown survey ID, an invalid option payload); catch requests.HTTPError to handle a transport or status-code failure.

alchemer_tools._question_title(question, language='English', clean=False)

Return a question's or option's title in language.

Alchemer returns title either as a language-keyed mapping or, for some endpoints, as a plain string. Both forms are accepted. When the requested language is absent, the first available translation is used so that single-language surveys still resolve.

Parameters:

Name Type Description Default
question dict[str, Any]

Alchemer question or survey-option record.

required
language str

Preferred title language.

'English'
clean bool

If True, strip HTML markup from the result. Title lookups enable this so that a caller-supplied plain-text title can match Alchemer's marked-up one. It stays off by default because callers that turn titles into DataFrame column names must preserve Alchemer's exact text.

False

Returns:

Type Description
str or None

The resolved title, or None when the record carries no title at all.

alchemer_tools.AlchemerAPI

Client for retrieving and managing surveys through the Alchemer API.

Parameters:

Name Type Description Default
api_token

Alchemer API token used to authenticate requests.

required
api_token_secret

Secret paired with api_token.

required
region

Alchemer service region. Supported values are "US", "EU", and "CA". The default is "US".

'US'
Notes

The client maintains a reusable HTTP session with retries for transient failures. Credentials are included in the request parameters sent to Alchemer; they are not logged by this class.

Methods follow a three-tier naming convention:

__name Raw transport. Builds the URL and parameters, calls Alchemer, and returns the decoded JSON envelope unchanged. No pandas, no business rules. _name Internal parsing or business helper. Pure — never calls the API. name Public, documented API. Composes the two tiers above and returns a business object such as a DataFrame, a list, or an ID.

The name therefore tells you what you get back: a __ method always returns Alchemer's raw envelope, a public method always returns parsed data.

alchemer_tools.AlchemerAPI.__init__(api_token, api_token_secret, region='US')

Initialize an Alchemer client with API credentials and a region.

Parameters:

Name Type Description Default
api_token

Alchemer API token.

required
api_token_secret

Alchemer API token secret.

required
region

Alchemer data-center region, which selects the API base URL.

'US'
Notes

Credentials are stored privately and sent as query parameters on every request. Keep them in Databricks secrets rather than notebook literals.

alchemer_tools.AlchemerAPI.__build_session() staticmethod

Create a reusable HTTP session for paginated Alchemer API calls.

Returns:

Type Description
Session

Session whose adapter retries GET and POST up to three times with exponential backoff on 429, 500, 502, 503, and 504 responses. Reusing one session keeps connections pooled across paginated requests.

alchemer_tools.AlchemerAPI.__set_params()

Configure the API session parameters from the current client state.

alchemer_tools.AlchemerAPI.set_api_token(api_token)

Update the Alchemer API token used for subsequent requests.

Parameters:

Name Type Description Default
api_token

Alchemer API token to store on this client.

required

Returns:

Type Description
None

This method updates the client in place.

Notes

Updating the token also refreshes the internal request parameters used by the client's API calls.

alchemer_tools.AlchemerAPI.set_api_token_secret(api_token_secret)

Update the API token secret used for subsequent requests.

Parameters:

Name Type Description Default
api_token_secret

Alchemer API token secret to store on this client.

required

Returns:

Type Description
None

This method updates the client in place and refreshes its internal request parameters.

alchemer_tools.AlchemerAPI.set_region(region)

Select the Alchemer regional API endpoint.

Parameters:

Name Type Description Default
region

Region code, case-insensitively matching "US", "EU", or "CA".

required

Returns:

Type Description
None

This method updates region and the base API URL in place.

Raises:

Type Description
KeyError

If region is not one of the supported region codes.

alchemer_tools.AlchemerAPI.__request(method, path, *, extra_params=None, timeout=DEFAULT_TIMEOUT, check_result_ok=True)

Send one authenticated request to Alchemer and decode the JSON envelope.

Parameters:

Name Type Description Default
method str

HTTP verb to use, for example "GET" or "POST". Callers pass the verb explicitly because Alchemer accepts several spellings of the same operation and the existing endpoints are not consistent about it.

required
path str

API path appended to the region base URL, for example "/v5/survey/12345".

required
extra_params dict[str, Any] | None

Endpoint-specific query parameters merged over the client's authentication parameters. The client's parameters are copied, never mutated.

None
timeout int

Per-request timeout in seconds.

DEFAULT_TIMEOUT
check_result_ok bool

If True, treat a false result_ok field as a failure.

True

Returns:

Type Description
dict

Raw JSON-decoded response envelope.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

JSONDecodeError

If the response body is not valid JSON.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__iter_pages(path, *, extra_params=None, timeout=DEFAULT_TIMEOUT)

Yield each page envelope of a paginated endpoint, starting at page 1.

Alchemer reports total_pages on every page, so the page count is only known after the first request. Callers that need just part of the result may stop iterating early and no further requests are made — this is what lets title lookups return as soon as they find a match.

Parameters:

Name Type Description Default
path str

API path appended to the region base URL.

required
extra_params dict[str, Any] | None

Endpoint-specific query parameters. A page entry is added per iteration and overrides any supplied value.

None
timeout int

Per-request timeout in seconds.

DEFAULT_TIMEOUT

Yields:

Type Description
dict

Raw JSON-decoded envelope for one page, in page order.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false for any page.

alchemer_tools.AlchemerAPI.__collect_pages(path, *, extra_params=None, timeout=DEFAULT_TIMEOUT)

Return one envelope whose data concatenates every page's records.

Parameters:

Name Type Description Default
path str

API path appended to the region base URL.

required
extra_params dict[str, Any] | None

Endpoint-specific query parameters.

None
timeout int

Per-request timeout in seconds.

DEFAULT_TIMEOUT

Returns:

Type Description
dict

Raw envelope carrying the last page's metadata and the combined data records from all pages. An endpoint that returns nothing yields {"data": [], "total_pages": 0}.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false for any page.

alchemer_tools.AlchemerAPI.__survey_path(survey_id=None) staticmethod

Return the API path for the account's survey list, or one survey.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID. Omit it for the account-wide survey list.

None

Returns:

Type Description
str

/v5/survey/ for the list, or /v5/survey/{survey_id}. Alchemer serves the list only from the trailing-slash form, so the two are not interchangeable.

alchemer_tools.AlchemerAPI.__question_path(survey_id, question_id=None) staticmethod

Return the API path for a survey's questions, or one question.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
question_id

Alchemer question ID. Omit it for every question in the survey.

None

Returns:

Type Description
str

The survey-question collection path, with the question ID appended when one is given.

alchemer_tools.AlchemerAPI.__response_path(survey_id, response_id=None) staticmethod

Return the API path for a survey's responses, or one response.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
response_id

Alchemer response ID. Omit it for every response in the survey.

None

Returns:

Type Description
str

The survey-response collection path, with the response ID appended when one is given.

alchemer_tools.AlchemerAPI.__option_path(survey_id, question_id, option_id=None) classmethod

Return the API path for a question's survey options, or one option.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID that owns the options.

required
option_id

Alchemer survey-option ID. Omit it for every option on the question.

None

Returns:

Type Description
str

The survey-option collection path, with the option ID appended when one is given.

alchemer_tools.AlchemerAPI.__get_survey_responses(survey_id, order_by='date_submitted', start_date=None)

Return every page of a survey's responses merged into one raw envelope.

Wraps GET /v5/survey/{survey_id}/surveyresponse.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
order_by str

Alchemer sort field. Prefix with - for newest first.

'date_submitted'
start_date str | None

Optional inclusive lower bound on date_submitted, formatted as YYYY-MM-DD HH:MM:SS.

None

Returns:

Type Description
dict

Raw response envelope; data holds the response records from all pages, each with its nested survey_data.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false for any page.

alchemer_tools.AlchemerAPI.__get_survey_options(survey_id, question_id)

Return every page of a question's survey options as one raw envelope.

Wraps GET /v5/survey/{survey_id}/surveyquestion/{question_id}/surveyoption.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID whose options are requested.

required

Returns:

Type Description
dict

Raw response envelope; data holds the option records from all pages.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false for any page.

alchemer_tools.AlchemerAPI.__get_survey_option(survey_id, question_id, option_id)

Return the raw envelope for one survey option.

Wraps GET /v5/survey/{survey_id}/surveyquestion/{question_id}/surveyoption/{option_id}.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to retrieve.

required

Returns:

Type Description
dict

Raw response envelope; data holds the option record.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__create_survey_option(survey_id, question_id, option_data)

Create a survey option and return the raw envelope.

Wraps PUT /v5/survey/{survey_id}/surveyquestion/{question_id}/surveyoption.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID that will contain the new option.

required
option_data dict[str, Any]

Option parameters sent in the request query string.

required

Returns:

Type Description
dict

Raw response envelope describing the created option.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__update_survey_option(survey_id, question_id, option_id, option_data)

Update a survey option and return the raw envelope.

Wraps POST /v5/survey/{survey_id}/surveyquestion/{question_id}/surveyoption/{option_id}.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to update.

required
option_data dict[str, Any]

Replacement option parameters sent in the request query string.

required

Returns:

Type Description
dict

Raw response envelope describing the updated option.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__delete_survey_option(survey_id, question_id, option_id)

Delete a survey option and return the raw envelope.

Wraps DELETE /v5/survey/{survey_id}/surveyquestion/{question_id}/surveyoption/{option_id}.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to delete.

required

Returns:

Type Description
dict

Raw response envelope confirming the deletion.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__delete_survey(survey_id)

Delete a survey and return the raw envelope.

Wraps GET /v5/survey/{survey_id} with Alchemer's _method=DELETE override rather than a real DELETE verb, matching the request this client has always sent for this endpoint.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID to delete.

required

Returns:

Type Description
dict

Raw response envelope confirming the deletion.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.__move_survey_to_folder(survey_id, folder_id)

Move a survey into a folder and return the raw envelope.

Wraps POST /v5/survey/{survey_id}. The redundant _method=POST override is preserved because it is what this client has always sent.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID to update.

required
folder_id

Destination folder ID.

required

Returns:

Type Description
dict

Raw response envelope describing the updated survey.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI._titles_by_question_id(questions, language='English') staticmethod

Map each question ID to its title, preserving Alchemer's order.

Does not call the API. Titles keep their original markup because callers turn them into DataFrame column names.

Parameters:

Name Type Description Default
questions list[dict[str, Any]]

Question records, each carrying id and title.

required
language str

Preferred title language.

'English'

Returns:

Type Description
dict

Question ID to title. Questions with no title at all are omitted.

alchemer_tools.AlchemerAPI._append_answer_column(row_values, column, value, answer_id) staticmethod

Store one answer in row_values under a column name unique to the row.

Does not call the API. Mutates row_values in place. Two questions can share a label within a single response, so a colliding name is suffixed with the answer ID to keep every answer addressable.

Parameters:

Name Type Description Default
row_values dict[str, Any]

Accumulator for the response currently being flattened.

required
column

Preferred column name.

required
value

Answer value to store.

required
answer_id

Alchemer answer ID used to disambiguate a colliding column name.

required

alchemer_tools.AlchemerAPI._append_subquestion_columns(row_values, parent_question, subquestions)

Store a parent question's shown subquestion answers in row_values.

Does not call the API. Mutates row_values in place. Alchemer returns subquestions in two shapes: a flat record carrying its own type, or a mapping of option ID to option record. Both are handled here.

Parameters:

Name Type Description Default
row_values

Accumulator for the response currently being flattened.

required
parent_question

Title of the parent question, used to build the column name.

required
subquestions

The parent question's subquestions mapping.

required

alchemer_tools.AlchemerAPI._responses_to_dataframe(response_data, question_ids_by_title, status_flag='all', debug=False) staticmethod

Reduce raw response records to one column per requested question title.

Does not call the API.

Parameters:

Name Type Description Default
response_data list[dict[str, Any]]

Raw response records, as found under a response envelope's data.

required
question_ids_by_title dict[str, Any]

Question title to Alchemer question ID, for example {"How old are you?": "123"}. Determines the output columns and their order.

required
status_flag str

Response status to keep: "Complete", "Partial", or "all".

'all'
debug bool

If True, print filtering details.

False

Returns:

Type Description
DataFrame

One row per kept response, with response_id and one column per requested title.

alchemer_tools.AlchemerAPI.get_surveys_list(timeout=60, newer_than=None)

Return the account's surveys as a DataFrame, newest pages first.

The statistics and links objects Alchemer nests in each record are flattened into their own columns.

Parameters:

Name Type Description Default
timeout int

Request timeout in seconds for each survey-list page.

60
newer_than datetime | None

If given, stop paging once a page's oldest survey predates this date.

None

Returns:

Type Description
DataFrame

One row per survey, with the nested statistics and links expanded.

Raises:

Type Description
HTTPError

If a survey-list request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

Notes

The newer_than cutoff assumes Alchemer returns surveys newest-first. This client does not request an explicit order_by, so it relies on the API's default ordering.

alchemer_tools.AlchemerAPI.get_survey_id_by_title(title, timeout=60, debug=False)

Return the survey ID for an exact Alchemer survey title.

The lookup scans survey-list pages until it finds a match, so a survey near the start of the list costs a single request. Matching is exact and case-sensitive.

Parameters:

Name Type Description Default
title str

Exact survey title to find.

required
timeout int

Request timeout in seconds for each survey-list page.

60
debug bool

If True, print page-level lookup details.

False

Returns:

Type Description
str

Alchemer survey ID.

Raises:

Type Description
ValueError

If no survey with the exact title is found.

HTTPError

If a survey-list request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.delete_survey(survey_id, request_verification=True)

Delete a survey, optionally requesting interactive confirmation.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID to delete.

required
request_verification bool

If True, prompt for confirmation and proceed only when the user enters "y". Disable this only in a controlled non-interactive workflow.

True

Returns:

Type Description
dict or None

Alchemer's JSON response, or None when the user cancels deletion.

Raises:

Type Description
HTTPError

If the deletion request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.move_survey_to_folder(survey_id, folder_id)

Move a survey into an Alchemer folder.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID to update.

required
folder_id

Destination folder ID.

required

Returns:

Type Description
dict

JSON response returned by Alchemer.

Raises:

Type Description
HTTPError

If the update request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.test()

Return the first raw survey-list page for a connectivity check.

Returns:

Type Description
dict

Raw JSON payload from the Alchemer survey-list endpoint.

Raises:

Type Description
HTTPError

If the request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

Notes

This method is retained as a lightweight compatibility and debugging helper. Use get_surveys_list for normal survey discovery. The 60 second timeout is longer than the client default because listing surveys is slower than the per-survey endpoints.

alchemer_tools.AlchemerAPI.get_survey_questions(survey_id, attributes=['id', 'base_type', 'type', 'title', 'options', 'show_rules_ids', 'sub_questions'])

Return selected fields for every question in a survey.

The method follows all pages of the Alchemer survey-question endpoint and preserves the API's question order.

Parameters

survey_id: Alchemer survey ID. attributes: Question fields to include in each returned dictionary. Missing fields are returned with a value of None.

Returns

list of dict One dictionary per survey question with the requested fields.

Raises

requests.HTTPError If a survey-question request returns an unsuccessful HTTP status. AlchemerAPIError If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.get_question_ids_by_titles(survey_id, question_titles, language='English', debug=False)

Return question IDs for the requested question titles.

Looks up survey questions by their localized title and returns a dictionary mapping each requested title to its Alchemer question ID. Missing titles are skipped. If a question does not have a title for the requested language, the first available title value is used as a fallback.

Titles are compared verbatim, including any HTML markup Alchemer returns. Use get_question_id_by_title to match against plain text instead.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
question_titles list[str]

Question titles to search for.

required
language str

Title language to match.

'English'
debug bool

If True, print lookup details for troubleshooting.

False

Returns:

Type Description
dict

Title to question ID, for example {"How old are you?": "123"}, ordered by question_titles.

Raises:

Type Description
HTTPError

If a survey-question request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

Examples:

>>> client.get_question_ids_by_titles(12345, ["How old are you?"])
{'How old are you?': '123'}

alchemer_tools.AlchemerAPI.get_question_id_by_title(survey_id, question_title, language='English', debug=False)

Return the ID of the question whose plain-text title matches.

HTML markup is stripped from Alchemer's titles before comparing, so question_title should be given as plain text. Pages are fetched only until a match is found.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
question_title str

Plain-text question title to find.

required
language str

Title language to match.

'English'
debug bool

If True, print each title evaluated during the lookup.

False

Returns:

Type Description
str or None

Alchemer question ID, or None if no title matches on any page.

Raises:

Type Description
HTTPError

If a survey-question request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.get_survey_options(survey_id, question_id)

Return every survey option belonging to a question.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID whose options are requested.

required

Returns:

Type Description
list of dict

Survey-option records from all pages, in Alchemer's order.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.get_survey_option(survey_id, question_id, option_id)

Return a specific survey option.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to retrieve.

required

Returns:

Type Description
dict

Survey-option record.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.get_survey_option_id_by_title(survey_id, question_id, option_title, language='English', debug=False)

Return the ID of the survey option whose plain-text title matches.

HTML markup is stripped from Alchemer's titles before comparing, so option_title should be given as plain text.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_title str

Plain-text title of the survey option to find.

required
language str

Language of the option title to match.

'English'
debug bool

If True, print each option title evaluated during the lookup.

False

Returns:

Type Description
int or None

Survey-option ID, or None if no option title matches.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

Examples:

>>> client.get_survey_option_id_by_title(12345, 67, "Prefer not to say")
10045

alchemer_tools.AlchemerAPI.create_survey_option(survey_id, question_id, option_data)

Create a new survey option for a specific question.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID that will contain the new option.

required
option_data dict[str, Any]

Option parameters sent in the request query string. title and value are required fields.

required

Returns:

Type Description
dict

Full API response describing the created survey option.

Raises:

Type Description
TypeError

If option_data is not a dictionary.

ValueError

If option_data does not include both required fields.

HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.update_survey_option(survey_id, question_id, option_id, option_data)

Update an existing survey option for a specific question.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to update.

required
option_data dict[str, Any]

Replacement option parameters sent in the request query string. Only the fields present are changed.

required

Returns:

Type Description
dict

Full API response describing the updated survey option.

Raises:

Type Description
TypeError

If option_data is not a dictionary.

HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.delete_survey_option(survey_id, question_id, option_id)

Delete a survey option from a specific question.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID containing the question.

required
question_id

Alchemer question ID containing the option.

required
option_id

Alchemer survey-option ID to delete.

required

Returns:

Type Description
dict

Full API response confirming the deletion.

Raises:

Type Description
HTTPError

If Alchemer returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

alchemer_tools.AlchemerAPI.get_filtered_survey_responses(survey_id, question_titles, language='English', status_flag='all', debug=False)

Return survey responses reduced to the requested question titles.

The returned DataFrame includes one row per response, a response_id column, and one column for each requested question title that was found in the survey. Missing question titles are skipped.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
question_titles list[str]

Question titles to include as DataFrame columns.

required
language str

Title language to match when resolving titles to IDs.

'English'
status_flag str

Response status filter: "Complete", "Partial", or "all".

'all'
debug bool

If True, print lookup and response filtering details.

False

Returns:

Type Description
DataFrame

response_id plus the requested question-title columns.

Raises:

Type Description
HTTPError

If a survey-question or survey-response request fails.

AlchemerAPIError

If Alchemer reports result_ok false.

Examples:

>>> client.get_filtered_survey_responses(
...   12345, ["How old are you?"], status_flag="Complete"
... )

alchemer_tools.AlchemerAPI.get_survey_responses_by_title(survey_name, start_date=None, debug=False, as_spark=False)

Return all responses for an exact survey title as a DataFrame.

This is a convenience wrapper around get_survey_responses with formatted="default". The returned DataFrame includes response metadata and flattened answers for questions that were shown in each response.

Parameters:

Name Type Description Default
survey_name str

Exact Alchemer survey title to retrieve.

required
start_date str | None

Optional inclusive lower bound for date_submitted in YYYY-MM-DD HH:MM:SS format.

None
debug bool

If True, print lookup details while resolving the survey title.

False
as_spark bool

If True, return a Spark DataFrame instead of a pandas DataFrame.

False

Returns:

Type Description
DataFrame or DataFrame

Response metadata plus flattened survey answer columns.

Raises:

Type Description
ValueError

If no survey with the exact title is found.

alchemer_tools.AlchemerAPI.get_survey_responses(survey_id, start_date=None, formatted='default')

Return all responses for a survey, formatted or raw.

The raw payload is also cached on self.raw_survey_responses under survey_id so it can be inspected after a formatting failure.

Parameters:

Name Type Description Default
survey_id

Alchemer survey ID.

required
start_date str | None

If given, only responses submitted at or after this moment are retrieved, formatted as YYYY-MM-DD HH:MM:SS. If omitted, all responses are retrieved.

None
formatted bool | Literal['default', 'synpop']

"default" flattens the raw response into metadata plus one column per shown question. "synpop" maps responses onto the synthetic-population schema, keeping only complete responses. False returns the raw payload unchanged, for debugging and for developing new formats.

'default'

Returns:

Type Description
DataFrame or dict

A DataFrame for either formatted mode; the raw envelope when formatted is False, or when the survey has no responses at all.

Raises:

Type Description
ValueError

If formatted is not False, "default", or "synpop".

FileNotFoundError

If formatted="synpop" and the bundled mappings.json is missing.

RuntimeError

If a response cannot be mapped onto the synthetic-population schema.

HTTPError

If a survey-response request returns an unsuccessful HTTP status.

AlchemerAPIError

If Alchemer reports result_ok false.

Examples:

>>> client.get_survey_responses(12345)                     # flattened
>>> client.get_survey_responses(12345, formatted="synpop")  # SynPop schema
>>> client.get_survey_responses(12345, formatted=False)     # raw envelope

alchemer_tools.AlchemerAPI._synpop_responses_to_dataframe(raw_survey_responses)

Map complete responses onto the synthetic-population schema.

Does not call the API. Partial responses are skipped, because the SynPop mappings assume every question was reached. Column order follows the bundled col_order.txt when present, with any unlisted columns appended.

Parameters:

Name Type Description Default
raw_survey_responses dict[str, Any]

Raw response envelope as returned by the survey-response endpoint.

required

Returns:

Type Description
DataFrame

One row per complete response, in synthetic-population schema.

Raises:

Type Description
FileNotFoundError

If the bundled mappings.json resource is missing.

RuntimeError

If a response cannot be resolved through the mappings.

alchemer_tools.AlchemerAPI.format_default_response(response, survey_id)

Flatten a raw survey-response payload into a pandas DataFrame.

Response metadata is retained, while shown survey answers become columns. Parent-question options, including nested subquestion options, use "option:question" column names. Flat subquestions use "subquestion:parent question" names. When answer labels collide, the answer ID is appended to keep names unique.

Parameters:

Name Type Description Default
response

Raw Alchemer response payload containing a data list and nested survey_data dictionaries.

required
survey_id

Survey ID used to retrieve question titles for parent questions.

required

Returns:

Type Description
DataFrame

Response metadata and flattened answer columns, with one row per response.

Raises:

Type Description
HTTPError

If retrieving the survey's question metadata fails.

KeyError

If required response or question fields are missing.

alchemer_tools.AlchemerAPI.format_synpop_response(response, main_mappings)

Map one raw survey response to the synthetic-population schema.

The method copies response metadata, ignores questions that were not shown, and applies the configured mappings for hidden values, text boxes, checkboxes, subquestions, and radio questions.

Parameters:

Name Type Description Default
response

One Alchemer response dictionary containing metadata and survey_data.

required
main_mappings

Mapping configuration keyed by Alchemer question and answer IDs. The input is deep-copied before dynamic piping information is added.

required

Returns:

Type Description
dict

Flattened response ready to become a synthetic-population DataFrame record.

Raises:

Type Description
RuntimeError

If one or more shown answers cannot be resolved through the supplied mappings. All answers are inspected before the collected errors are raised.

alchemer_tools.AlchemerAPI.execute_finecast_extract(s3_base_path=f's3://ap-synthetic-population/dev/raw_data/surveys/finecast_surveys', finecast_survey_ids={'GB': 8344353, 'DE': 7636573}, logger=None, file_extension='parquet')

Extract the given Finecast surveys and save each to S3.

Parameters:

Name Type Description Default
s3_base_path str

Base S3 prefix. Each country is written to {s3_base_path}/{country}/latest for Parquet or latest.csv for CSV.

f's3://ap-synthetic-population/dev/raw_data/surveys/finecast_surveys'
finecast_survey_ids dict

Mapping of country code to Finecast survey ID.

{'GB': 8344353, 'DE': 7636573}
logger

Optional logger. Progress is logged only when one is supplied.

None
file_extension

Output format, either csv or parquet.

'parquet'

Returns:

Type Description
None

Extracts are written to S3 rather than returned.

Raises:

Type Description
AssertionError

If file_extension is neither csv nor parquet.

Notes

Every column is cast to string before writing, so downstream readers must handle their own type conversion. Parquet output overwrites the target path.

alchemer_tools.AlchemerAPI.execute_synpop_extract(countries=None, s3_base_path='s3://ap-synthetic-population/raw_data/surveys/synpop_surveys', synpop_identifier='Synthetic', logger=None, list_of_surveys=None, legacy_version_files=None, file_extension='parquet')

Extract SynPop surveys per country and save each to S3.

Parameters:

Name Type Description Default
countries list | None

Optional country codes to keep. Surveys are matched by title substring. When omitted, every discovered SynPop survey is extracted.

None
s3_base_path str

Base S3 prefix for the written extracts.

's3://ap-synthetic-population/raw_data/surveys/synpop_surveys'
synpop_identifier str

Substring identifying a SynPop survey by title.

'Synthetic'
logger

Optional logger. Progress is logged only when one is supplied.

None
list_of_surveys

Optional pre-fetched surveys list. When omitted, surveys created after 2025-12-01 are listed from the API.

None
legacy_version_files

Optional mapping used to also write legacy-format copies.

None
file_extension

Output format, either csv or parquet.

'parquet'

Returns:

Type Description
None

Extracts are written to S3 rather than returned.

Raises:

Type Description
AssertionError

If file_extension is neither csv nor parquet.