Skip to content

Python reference

Use the Python API to add parsing, reports, or Qualtrics API calls to your own scripts. Install the package first, following installation. Run scripts from this repository with uv run python your_script.py.

Public imports

The package root exports the offline workflow and the API client:

from qualtrics import (
    EntitySet,
    QualtricsClient,
    ReportAnalytics,
    SemanticModel,
    TranslationRequest,
    __version__,
    analyze_entities,
    build_semantic_model,
    load_entities,
    merge_entity_sets,
    parse_survey,
    parse_surveys,
    prepare_translations,
    render_report,
    write_entities,
    write_semantic_model,
)

The canonical report interface is from qualtrics.ui import render_report; you can also use the root import above. Install qualtrics[ui] before generating a report. Base SDK/data imports work without Jinja, Typer, or Rich; UI rendering does not require CLI dependencies.

Importing these names does not read credentials or make API calls. The client reads connection settings when you construct it; service methods make requests when you call them.

Migrate earlier deep imports

The package layout now has four implementation packages: api, cli, ui, and _common. The earlier qualtrics.models, qualtrics.parsers, qualtrics.analytics, qualtrics.serialization, qualtrics.reporting, and qualtrics.services paths have been removed. Update imports to the public interfaces:

Earlier import Replacement
from qualtrics.models import EntitySet, merge_entity_sets from qualtrics import EntitySet, merge_entity_sets
from qualtrics.models.semantic import SemanticModel, build_semantic_model from qualtrics import SemanticModel, build_semantic_model
from qualtrics.parsers import parse_survey, parse_surveys from qualtrics import parse_survey, parse_surveys
from qualtrics.analytics import ReportAnalytics, analyze_entities from qualtrics import ReportAnalytics, analyze_entities
from qualtrics.serialization import load_entities, write_entities from qualtrics import load_entities, write_entities
from qualtrics.serialization.semantic import write_semantic_model from qualtrics import write_semantic_model
from qualtrics.reporting import render_report from qualtrics.ui import render_report or from qualtrics import render_report
from qualtrics.services import merge_entity_sets from qualtrics import merge_entity_sets

Use the root imports for shared data operations. Treat _common and its submodules as private implementation details; they are not supported application imports. Existing root imports and the cli and ui extras retain their behavior. The base installation includes the API SDK and PyArrow dependencies.

Offline functions

The signatures below use Path from pathlib and Sequence and Callable from collections.abc.

parse_survey(
    source_path: str | Path,
    qsf_path: str | Path | None = None,
    survey_id: str | None = None,
    *,
    flow_path: str | Path | None = None,
) -> EntitySet

parse_surveys(
    source_paths: Sequence[str | Path],
    qsf_paths: Sequence[str | Path] | None = None,
) -> EntitySet

merge_entity_sets(entity_sets: list[EntitySet]) -> EntitySet

write_entities(entities: EntitySet, folder: str | Path, format: str = "json") -> None

load_entities(folder: str | Path | None = None, **paths: str | Path) -> EntitySet

prepare_translations(
    entities: EntitySet,
    *,
    language: str | None = None,
    translate: Callable[[TranslationRequest], str] | None = None,
    question: Callable[[TranslationRequest], str] | None = None,
    field: Callable[[TranslationRequest], str] | None = None,
    answer_option: Callable[[TranslationRequest], str] | None = None,
    comment: Callable[[TranslationRequest], str] | None = None,
) -> EntitySet

render_report(entities: EntitySet, output: str | Path) -> None

analyze_entities(entities: EntitySet) -> ReportAnalytics

build_semantic_model(entities: EntitySet) -> SemanticModel

write_semantic_model(
    model: SemanticModel,
    folder: str | Path,
    format: str = "parquet",
) -> None
Function Behavior and constraints
parse_survey Accepts a CSV, a ZIP containing one CSV, or a wildcard pattern matching several inputs. Discovers a same-stem .qsf then .json definition if you omit qsf_path. A survey_id override applies to one input only.
parse_surveys Expands wildcard patterns in each supplied path, sorting each pattern's matches. Explicit definitions pair with inputs by position; supply one per input or omit them for adjacent discovery.
merge_entity_sets Combines distinct survey IDs and deduplicates identical records in the two catalogs. Raises ValueError for repeated survey IDs or conflicting records with the same catalog ID.
write_entities Writes nine core entities and derived comments as json, csv, or parquet, plus manifest.json. Prepared targets are nullable comments columns, not a sidecar file. Creates the folder and overwrites matching files.
load_entities Loads entity files named for their tables. Explicit keyword paths use table names, such as responses="responses.json"; manifest="metadata.json" selects a separate manifest. When explicit table paths share a folder, its sibling manifest.json is loaded automatically. Validates the full contract when you supply a folder; without a folder, validates the supplied subset's keys and relationships. Rejects multiple formats for the same entity in a folder.
prepare_translations Calls your shared or kind-specific callback for missing definition labels and written answers. QSF labels win; callback-generated choice labels never resolve categorical facts. Returns a new collection; without a callback, it makes no service calls.
render_report Writes a self-contained HTML report with the built-in design. The output's parent directory must exist. Overwrites the target file.
analyze_entities Calculates response counts, question roles, answer groupings, and unused or unanswered content for reports. Returns a ReportAnalytics without requiring the ui extra.
build_semantic_model Requires a complete, valid entity collection. Returns nine tables; prepared text and freshness flags are columns of fact_comments.
write_semantic_model Writes all nine tables as json, csv, parquet, or one SQLite database. Creates the folder and overwrites matching files.

The Python writers do not apply the CLI combine and semantic commands' occupied-output checks. Choose a fresh output directory for each run or format. The base package supports reading and writing Parquet.

Use prepare_translations(entities, language="EN", comment=your_callback) to prepare written answers alone; pass translate=your_callback to use one structured callback for labels and comments. Each callback receives a TranslationRequest. To attach translations prepared outside Python, use the CLI import command with a hash-checked CSV or Parquet file. Raw answers remain unchanged and the toolkit never calls a translation service without your callback.

Parse, save, and report

from pathlib import Path

from qualtrics import (
    build_semantic_model,
    load_entities,
    parse_survey,
    render_report,
    write_entities,
    write_semantic_model,
)


def main() -> None:
    output = Path("output/customer")
    output.mkdir(parents=True, exist_ok=True)

    entities = parse_survey("data/customer.csv", "data/customer.qsf")
    write_entities(entities, output / "entities")  # JSON by default.
    saved = load_entities(output / "entities")
    render_report(saved, output / "report.html")

    semantic = build_semantic_model(saved)
    write_semantic_model(semantic, output / "semantic", format="csv")
    print(f"Wrote a report for {len(saved.responses)} responses")


if __name__ == "__main__":
    main()

Use the downloadable inputs in your first report, or follow parse exports with your own data.

render_report selects presentations from the question and field metadata: choice distributions, matrix tables, numeric summaries, or text summaries. It does not infer numeric measurements from text values. Preserve the metadata from parse_survey or load_entities when constructing your own workflows. See question-specific summaries for the percentage denominators and numeric statistics.

Collections and table names

EntitySet is a dataclass with lists of dictionaries named surveys, sections, question_catalog, question_field_catalog, questions, answer_options, question_fields, responses, response_answers, and comments. Its keyword-only survey_manifests map holds the per-survey metadata in memory. Prefer parse_survey or load_entities to construct a collection with the metadata needed for strict validation.

SemanticModel also includes display-language and localized-label tables. fact_comments remains one row per original written answer, with optional prepared target columns and translation_is_current__CODE freshness flags. dim_questions has one row per exported question field.

entities.comments and semantic.fact_comments are derived text-answer subsets. They reuse response_answer_id, preserve the answer text, and copy nullable user_language only from the linked response. The original nine entity lists remain authoritative; a legacy folder without comments reconstructs it from available types. New fields carry nullable boolean is_comment_field on question_fields and dim_questions so source classification survives serialization. See the comments contract for membership and validation rules. Do not count comments again when using the complete answer table.

Use analyze_entities(entities) to calculate report metrics in Python. Its ReportAnalytics result includes response_count, finished_count, question_roles, and the question, field, and answer lookups used by HTML reports.

Use the entity model and DBML schema for columns, keys, and relationships. The glossary explains the terminology.

API client

QualtricsClient(
    api_token: str | None = None,
    *,
    data_center: str | None = None,
    base_url: str | None = None,
    timeout: float = 30.0,
    max_retries: int = 3,
    retry_backoff: float = 1.0,
    transport: httpx.BaseTransport | None = None,
)

Omit connection arguments to read environment variables and .env. See configuration for precedence, URL resolution, and timeouts. Use with QualtricsClient() as client: to close the HTTP connection on exit; otherwise call client.close(). QualtricsClient.from_env(**kwargs) is a compatibility alias for construction with the same keyword arguments.

List surveys with error handling

import httpx

from qualtrics import QualtricsClient
from qualtrics.api import QualtricsAPIError, QualtricsExportError


def main() -> None:
    try:
        with QualtricsClient() as client:
            for survey in client.surveys.iter():
                print(survey.id, survey.name)
    except QualtricsAPIError as error:
        print(f"Qualtrics HTTP {error.status_code}: {error}")
        if error.request_id:
            print(f"Request ID: {error.request_id}")
    except QualtricsExportError as error:
        print(f"Import or export job failed: {error}")
    except httpx.HTTPError as error:
        print(f"Connection failed: {error}")
    except ValueError as error:
        print(f"Check connection settings or request data: {error}")


if __name__ == "__main__":
    main()

The main guard keeps importing this script from making API calls. For response downloads and definitions, follow API access.

API services

Service attributes group endpoints by purpose. The signatures below omit self. Any comes from typing; Iterator comes from collections.abc. Response models below are Pydantic models: use their snake_case attributes, .model_dump(), or .model_dump_json().

client.surveys

list(*, offset: int | None = None) -> SurveyPage
iter() -> Iterator[SurveySummary]
get(survey_id: str) -> dict[str, Any]
update(survey_id: str, changes: SurveyUpdateRequest | dict[str, Any]) -> dict[str, Any] | None

list returns one page; iter follows nextPage links. update sends changes to the live survey. SurveyUpdateRequest supports name, is_active, expiration, and owner_id; you can also pass API field names such as isActive.

client.survey_definitions

get(survey_id: str) -> SurveyDefinition
get_flow(survey_id: str) -> dict[str, Any]
create(definition: dict[str, Any]) -> dict[str, Any]
delete(survey_id: str) -> dict[str, Any] | None
get_metadata(survey_id: str) -> dict[str, Any]
update_metadata(survey_id: str, metadata: dict[str, Any]) -> None

get returns a wrapper with survey_id, survey_name, and the raw definition in payload. You can save .model_dump_json() as a .json definition and pass it to parse_survey. create, delete, and update_metadata change remote data.

get_flow reads the configured flow root with its ordered children through the existing retry transport. Save it as JSON and pass its path as parse_survey(..., flow_path=...) for one input. Flows already included in a QSF or API definition are preserved automatically. The parser stores report-safe flow metadata in the export folder's manifest.json, keyed by survey ID; surveys stays scalar. See survey flow for a complete example and supported walkthrough behavior.

client.survey_quotas

list(survey_id: str, *, offset: int | None = None) -> SurveyQuotaPage
iter(survey_id: str) -> Iterator[SurveyQuota]

client.quotas is an alias. list rejects negative offsets. A quota has id, name, count, quota, logic_type, and combinations; each combination has count, quota, and description.

client.responses: exports

client.response_exports is an alias for the same service.

start(survey_id: str, options: ResponseExportRequest | None = None) -> ExportProgress
progress(survey_id: str, progress_id: str) -> ExportProgress
wait(
    survey_id: str,
    progress_id: str,
    *,
    poll_interval: float = 1.0,
    timeout: float = 900.0,
    on_progress: ExportCallback | None = None,
) -> ExportProgress
download(survey_id: str, file_id: str) -> httpx.Response
export(
    survey_id: str,
    output: str | Path,
    *,
    options: ResponseExportRequest | None = None,
    naming: FilenameStrategy = FilenameStrategy.SURVEY_ID,
    filename: str | None = None,
    survey_name: str | None = None,
    poll_interval: float = 1.0,
    timeout: float = 900.0,
    on_progress: ExportCallback | None = None,
) -> ExportResult

export starts, waits for, downloads, and saves an export. It returns the saved path, survey_id, progress_id, file_id, format, and optional continuation_token. The default request uses labeled CSV with compression, resulting in a ZIP. Set ResponseExportRequest(compress=False) for an uncompressed export. See the CLI naming rules for output path behavior.

on_progress receives an ExportEvent with survey_id, stage, and optional percent_complete. Import ExportCallback and ExportEvent from qualtrics.api. The full export emits starting, exporting, downloading, and complete; wait emits only polling updates. Completion is emitted after the local file is saved. Callbacks run in the calling thread. See progress callbacks and safe retries for an example.

wait requires a completed status and file ID; failed or timed-out jobs raise QualtricsExportError. download returns the HTTP response without saving a file.

client.responses: imports and filters

import_file(
    survey_id: str,
    source: str | Path,
    *,
    format: ImportFormat = ImportFormat.CSV,
) -> ImportProgress
import_url(
    survey_id: str,
    file_url: str,
    *,
    format: ImportFormat = ImportFormat.CSV,
) -> ImportProgress
import_progress(survey_id: str, progress_id: str) -> ImportProgress
wait_for_import(
    survey_id: str,
    progress_id: str,
    *,
    poll_interval: float = 1.0,
    timeout: float = 900.0,
) -> ImportProgress
list_filters(survey_id: str, *, offset: int | None = None) -> SurveyFilterPage
iter_filters(survey_id: str) -> Iterator[SurveyFilter]

Imports add response data to a live survey. Pass ImportFormat.CSV or ImportFormat.JSON. import_file sends local file bytes; import_url sends a file URL for Qualtrics to fetch. Poll with the returned progress_id. Failed or timed-out imports also raise QualtricsExportError.

list_filters returns one page of saved filters; iter_filters follows pagination. Import SurveyFilter and SurveyFilterPage from qualtrics.api.models when you need their types; they are not re-exported from qualtrics.api.

Request models and enums

Import the main request models from qualtrics.api:

from qualtrics.api import (
    FilenameStrategy,
    ImportFormat,
    ResponseExportRequest,
    ResponseImportRequest,
    SurveyUpdateRequest,
)

ResponseExportRequest accepts the fields below. Both Python names and Qualtrics aliases work, for example use_labels=False and useLabels=False.

Python field API alias Default
format format "csv"; also tsv, json, ndjson, xml, spss
compress compress True
use_labels useLabels True
new_line_replacement newlineReplacement None
include_display_order includeDisplayOrder False
include_label_columns includeLabelColumns False
start_date, end_date startDate, endDate None
filter_id filterId None
question_ids questionIds None
survey_metadata_ids surveyMetadataIds None
embedded_data_ids embeddedDataIds None
limit limit None
continuation_token continuationToken None
allow_continuation allowContinuation False
sort_by_last_modified_date sortByLastModifiedDate False

ID collections take lists of strings; date values and tokens take strings; limit takes an integer. The SDK passes these filters to Qualtrics. It does not automatically continue an export from a returned continuation token.

ResponseImportRequest requires file_url (alias fileUrl) and defaults to format=ImportFormat.CSV. FilenameStrategy provides SURVEY_ID, SURVEY_NAME, QUALTRICS, and CUSTOM. ExportStatus provides IN_PROGRESS, COMPLETE, and FAILED.

The API package also re-exports ExportFormat, ExportProgress, ExportResult, ImportProgress, SurveyDefinition, SurveyPage, SurveySummary, SurveyQuota, SurveyQuotaCombination, SurveyQuotaPage, QualtricsSettings, and the service classes. ResponseExportsAPI is the compatibility name for ResponseImportsExportsAPI.

Low-level transport and compatibility methods

Use the services above for covered endpoints. For another JSON endpoint:

client.request(
    method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"],
    path: str,
    *,
    params: dict[str, Any] | None = None,
    json: dict[str, Any] | None = None,
    content: bytes | None = None,
    headers: dict[str, str] | None = None,
) -> Any

client.download(path: str) -> httpx.Response

request returns the JSON result member when present, the full payload otherwise, and None for HTTP 204. Both methods use the configured token and raise QualtricsAPIError for HTTP error responses.

The client keeps these flat methods as delegates to the service API. They accept the same arguments and defaults as the corresponding service methods:

Compatibility method Service equivalent
list_surveys surveys.list
iter_surveys surveys.iter
get_survey surveys.get
list_survey_quotas survey_quotas.list
iter_survey_quotas survey_quotas.iter
start_response_export response_exports.start
get_response_export_progress response_exports.progress
wait_for_response_export response_exports.wait
download_response_export response_exports.download
export_responses response_exports.export

Errors

Error Meaning
ValueError Invalid settings, a malformed input, or an invalid entity contract. Read the message for the missing field or conflict.
FileNotFoundError Missing file, an unmatched wildcard, or a report destination whose parent does not exist.
QualtricsAPIError Qualtrics returned an HTTP error. Inspect status_code and request_id.
QualtricsExportError An import or export failed, timed out, or omitted a required job/file ID.
httpx.HTTPError An HTTP transport failure, including timeouts.

QualtricsAPIError and QualtricsExportError inherit from QualtricsError, a RuntimeError subclass. Import all three from qualtrics.api. See troubleshooting for recovery steps.