Comparing traffic sources

Shows which channel brought the right people and which only brought numbers

Free agent skill for Claude Code, Codex, Cursor and Gemini CLI. It works through the SurveyNinja MCP server, with your permissions.

Category
Understand results
Tags
channels and links, comparison
Works with
Claude Code, Codex, Cursor, Gemini CLI
MCP tools
5, 2 of them change data
Version
1.1, October 20, 2026
Made by
SurveyNinja
Price
Free
Skill file
SKILL.md

What it does

  • Channel quality, not only quantity. A thousand people at five percent completion is worse than a hundred with half of them finishing.
  • No label, no guessing. The assistant does not infer a channel from arrival time: if there is no label, there is no answer.
  • Unlabelled responses in plain sight. Direct visits and labels lost in forwarding get their own row instead of disappearing into “other”.

Example

You type

Where did respondents come from and which channel worked best?

What the assistant brings back

  • Email to customers, 168 people 71% finished
  • Instagram, 94 64%
  • No label, 82 59%
  • Website banner, 68 22%
  1. Email to customers 168 arrived, 119 finished, 62 contacts
  2. Instagram 94 arrived, 60 finished, 38 contacts
  3. No label 82 arrived, 48 finished, 27 contacts
  4. Website banner 68 arrived, 15 finished, 9 contacts
The banner brings people, just not the right ones

It delivers decent traffic, but only about one in five finishes. Usually the promise on the banner is the cause rather than the channel itself. One run is not enough to switch it off.

The channels are an example. The split is built from your own source labels.

How it works

  1. Checks the labels exist firstWithout source labels on the links a channel breakdown is impossible, and labels cannot be added afterwards. The assistant says so instead of inventing a split.
  2. Counts three things per channelHow many arrived, how many finished, and the metric that matters to you, such as contacts or average rating.
  3. Separates volume from qualityA thousand people at five percent completion is worse than a hundred with half finishing. The table shows it; a single total does not.
  4. Keeps unlabelled responses visibleThey get their own row instead of an “other” bucket: usually direct visits, or a label lost when the link was forwarded.

Try also

  • Compare the email campaign and Instagram by response quality
  • Split the responses by source label

What it will not do

  • Do not infer the channel from arrival time
  • Do not advise switching a channel off
  • Do not suggest a plan upgrade or lead to payment

The skill works with your permissions. Tools that change data are marked, so you check what the assistant is about to do before you confirm. To cut access, remove the connector in the assistant or delete the API key.

In short

Can I compare channels without labels?

No. Labels cannot be added afterwards, and the assistant will not guess the channel from arrival time. It suggests giving each channel its own labeled link, so the next run splits by channel.

What does the skill count for each channel?

Three things: how many arrived, how many finished, and your target metric, such as contacts or average rating. Both unique links and source labels are counted from the responses themselves.

Is the best channel the one with the most responses?

Not necessarily. A channel that brought a thousand people with five percent finishing is worse than one that brought a hundred with half finishing.

Does it use Google Analytics or Meta Pixel data?

No. Visits counted by Google Analytics or Meta Pixel stay in those services. The breakdown comes only from the source labels saved with the responses.

Will it advise switching off a weak channel?

Not after a single run. It shows how each channel performed, notes when a channel is too small to compare, and leaves the decision to you.

What do I need to use it?

A SurveyNinja account and an assistant that supports MCP and loads skills from a folder, such as Claude Code, Codex, Cursor or Gemini CLI. The assistant connects with your own API key or through “Allow access”. The skill only reads responses, reports and label values; it is a plain text file and runs nothing by itself.

The full skill

The SKILL.md file the assistant reads, exactly as it is in the archive.

Show the full text

Where they came from and what it gave

A survey is usually shared in several places at once. Responses can be split by channel only if each channel got its own set of unique links or its own link with a source label.

Reply to the person in the language they write in.

Check there is something to compare

  • manage_quiz_unique_links with list_groups: whether the channels got their own sets of unique links.
  • get_quiz_variables: the link variables set up in the survey. A source label is usually one of them, such as source or utm_source. A label is saved with the response even when no variable was set up for it, so also look at extra_fields in a few responses from get_quiz_answers, or ask which parameter the links carried.
  • get_answer_extra_field_values with that name as key lists the values that arrived. It is not on every plan; the extra_fields of responses show the same on any plan.

If there are neither unique links nor labels, there is nothing to compare: they cannot be added retroactively. Say so plainly and suggest giving each channel its own link next time.

Visits that Google Analytics or Meta Pixel count for the survey stay in those services and are not visible through these tools. This breakdown comes only from unique links and labels on responses; for traffic numbers, the person reads their analytics.

Collect

Per channel you need: how many arrived, how many finished, and the target metric (contacts, average rating, whatever matters here).

Count them from get_quiz_answers, paged with offset (up to 100 per page): each response carries its extra_fields (the labels) and its unique_link, and unique_link_group_id narrows the list to one set of unique links. Read is_complete true, then false, and group the responses by label or link yourself. answer_total_count is the total for the whole survey, not for the filtered list: count the responses you received.

Unfinished responses are kept only if the plan keeps them. Without that, is_complete false quietly returns completed ones: check is_complete on each response, and if there are no unfinished ones, say that completion per channel cannot be measured.

The target metric comes from the answers inside those responses. For unique links there is also generate_filtered_report with unique_link_group_id (and unique_link_ids for single links): the same report per channel, completed responses only. No tool builds the report for one label value; in the app that is the Hidden Variables filter in Results.

How to present

A table by channel, sorted by what matters to the person: channel, responses, completion share, target metric.

Below it, three conclusions:

  • Where the most responses came from. Obvious, and the least useful.
  • Where completion is best. A channel can bring few people but good ones.
  • Where responses are worse. Sometimes the cause is the promise in the ad rather than the channel: the wrong people arrived.

Worth stating

  • Volume and quality are different things. A thousand people at five percent completion is worse than a hundred at fifty.
  • Do not compare small channels head-on with large ones: at twenty responses the share jumps around.
  • Responses with no label deserve their own row instead of "other": usually direct visits or a label lost when the link was forwarded.

What not to do

  • Do not infer the channel from arrival time. No label means no answer.
  • Do not advise switching a channel off after a single run.
  • Do not suggest a plan upgrade or lead to payment. If a limit is hit, state the fact and stop.
The SurveyNinja ninja on a rocket

Connect SurveyNinja to your assistant

The MCP server works on every plan, Free included. Sign up, allow access and ask your assistant to build the first survey.