Direct structured query against YPulse's behavioral and omnibus survey database. Returns weighted response distributions for specific survey questions, with optional demographic filtering and breakdowns. Prefer this over behavior_tracker for exact percentages or a demographic cut on a known question. Provide either question_ids, or topics and/or search to resolve matching questions server-side (mutually exclusive). Discovery mode resolves and returns distributions for up to limit matches (default 10, max 100); page further matches with offset (advance by limit when match_total > offset + limit). Use behavior_question_list when you need the full inventory without distributions. For high-cardinality filter_by values (household income, DMA) whose full lists are not in the tool description, call behavior_distinct_data first. Question IDs are NOT globally unique — the same ID (e.g. S110, T220, A100) is reused across topics and survey waves. A question_id is unique only within a survey_source_id. This tool returns one separate results[] block per distinct question context (question_text + question_topic + survey_type + survey_source_id). Pin a wave with filter_by.survey_source_id (e.g. NA_BEHAVIORAL_202101B), or pass survey_type / question_topic / question_text on the question_ids path. When multiple texts still share the same question_id and survey_source_id after filters (rare data collision), context_collisions lists the colliding question texts. Cross-source reuse of the same literal ID is expected and is not a collision — use survey_source_id to disambiguate. Response shape: results[] blocks with question_id, question_text, question_topic, survey_type, survey_source_id, question_type, optional statement (row label for Matrix/MR sub-items), base_group, base, total_weighted_respondents, and responses[] entries of {response_option, weighted_pct, weighted_count}; plus match_total and context_collisions (discovery also echoes limit and offset). base_group and base describe who was asked the question (the survey's own base population), not any filter_by you applied; the top-level applied_filters object echoes the filter_by values in effect, so the data is scoped to that subset even though base is unchanged. Matrix sub-items: response_option is the Likert/scale label; statement is the row statement. Multiple Response check-all batteries: response_option is Selected, Not selected, or Not in base; statement is the reason label. Not in base is battery-level skip logic — the same share across all sub-items in a check-all bank is expected; Selected and Not selected vary per reason. questions_not_found lists IDs absent from the database; an empty results array with no questions_not_found entry means the ID exists but has no rows in the applied time window (try a wider months_lookback or calendar fielding range). Skip/non-response methodology codes (-77 skip-logic coded, -66 skip-logic open end, 0 saw but did not answer) and null/blank answers are excluded from responses[] and the weighted denominator; Multiple Response Not in base is retained (battery-level skip, different semantics). Open End, String, and Mean questions return at most the top 100 responses by weighted_count; when more exist the block sets responses_truncated, responses_total_count (full distinct-answer count), and responses_limit. weighted_pct for those types still uses the full-answer denominator.
behavior_data