Signals

Get a signal's rejection funnel

See why a signal delivers fewer leads than you expect: how many people it found, how many each of your filters removed, and the job titles, company sizes, locations and industries it rejected most often. Built for tuning a signal (by hand or with an agent): pick the filter that removes the most candidates you would actually want, relax it with PATCH /api/v1/signals/{signal_id}, and read the funnel again a few days later.

Input: the signal ID in the path; optional days (1-90, default 30), the window ending now.

Output: candidatesFound (candidates found before any filter; candidateUnit says whether a candidate is a person or a job posting), rejectedBy (persona, companySize, location, industry, excluded, duplicate, other; job signals add jobTitle, jobDescription and publishedDate), otherBreakdown (splits other), passedFilters, awaitingReview (found, not yet checked), unattributed (checked, but the record does not say which of your settings removed them; left out of rejectedBy rather than guessed; unattributedReason explains it when the cause is known), delivered (leads delivered in the window) and topRejected (the five most frequent rejected values per filter; unknown means the value was missing, which usually means accepting unknown values is the lever).

How candidates are counted: each candidate is counted once, under the FIRST filter it failed; filterOrder gives the order for this signal type (for competitor connections: company size, location, persona, industry, excluded companies, duplicates). A later filter never saw what an earlier one removed, so relaxing company size alone can move candidates on to the persona filter instead of into your leads. For social engagement and technology signals (countingMethod: per_run_counters) the counts are summed per collection run, so a person seen by several runs is counted each time, and no values are sampled.

Job signals (job offers, republished job offers, recruitment campaigns): the filters judge job postings (candidateUnit: job_posting), then the people you target are searched at each company that passed, so delivered counts people and can exceed passedFilters.

Company followers: the people each follower extraction returned, the role check and the company-size check they failed, and what was delivered. People already delivered on the signal are skipped without a per-person record, so they appear in unattributed.

Account watchlists: sources holds one funnel per source (countingMethod: per_source); the top-level numbers add up the measured sources.

Coverage: competitor connections, competitor customer care, job changes, new decision makers, employee departures, dormant LinkedIn posters, social engagement and technology signals, job offers, republished job offers, recruitment campaigns, company followers and account watchlists. Other signal types answer supported: false with a reason; that means 'not measured', never 'nothing was rejected'. Numbers are refreshed at most every 10 minutes.

get/api/v1/signals/{signal_id}/funnel

Request

  • Base URL: https://api.rodz.io (the document declares no server; this is the origin it was published from)
  • URL: https://api.rodz.io/api/v1/signals/{signal_id}/funnel
  • Auth: HTTP bearer

Path parameters

signal_idstring required

Query parameters

daysinteger

Size of the window, in days, ending now. 1 to 90; default 30.

Example:30

Response

Successful Response

signalTypeIdstring required

The signal's type.

supportedboolean required

False when this signal type does not record per-filter rejections yet. Every count below is then absent: 'not measured', not zero.

unsupportedReasonstring nullable

Why the funnel is not available.

countingMethod'per_candidate' | 'per_run_counters' | 'per_source' nullable

per_candidate: each candidate found is counted once, under the first filter it failed. per_run_counters: the counts are summed over every collection run in the window, so a person seen by several runs is counted each time, the stages can overlap slightly (they need not add up to candidatesFound), and no rejected values are sampled (topRejected is empty). per_source (account watchlists): the sum of sources, one entry per source of the watchlist.

candidateUnit'person' | 'job_posting' | 'mixed' nullable

What one candidate is. person for most signals. job_posting for job signals: the filters judge postings, then people are searched at each company that passed, so delivered (people) can exceed passedFilters (postings). mixed for an account watchlist whose sources count different things: read sources for comparable numbers.

filterOrderstring[] nullable

The order this signal type applies its filters in, using the rejectedBy keys (jobChangeAge is the job-age part of other). Null for per_run_counters, whose stages are not strictly ordered.

dataAvailableSincestring date-time nullable

For per_run_counters: the date counting started. A window that begins earlier only covers the period since then.

candidatesFoundinteger nullable

People the signal found in the window, before any of your filters. A candidate that aged out before it was reviewed is not counted here, nor anywhere else in the funnel.

otherBreakdownobject

Splits rejectedBy.other. Keys: companyNotIdentified (no company name, page or website to check your company filters against), jobChangeTooOld / jobChangeDateUnknown (job changes older than your maximum job age, or undated), noWatchedCompany, noFormerCompany, notReactivated, sourceRemovedFromSettings, noLeadCreated, competitorEmployee (the person works at the competitor you watch), ownCompany (the person works at your own company), profileUnavailable (the profile's details could not be read when it was reviewed), noJobTitle (the profile shows no job title or headline to check your personas against), notMutual (connected to only one of the two profiles of a mutual-connections signal), existingNetwork (already connected to the watched person when the signal started watching, and not part of your one-time extract: not delivered, not billed), commentKeyword (LinkedIn engagement signals: the person's comment contains one of your excludedCommentKeywords; not delivered, not billed), unclassified.

passedFiltersinteger nullable

Candidates that passed every filter. More than delivered when your daily lead limit or credit balance stopped a run.

awaitingReviewinteger nullable

Candidates found but not yet checked against your filters.

unattributedinteger nullable

Candidates that were checked, but whose record does not say which of YOUR settings removed them. They are left out of rejectedBy rather than guessed.

unattributedReasonstring nullable

Why unattributed is not zero, or why candidatesFound is empty, when this signal type has a known explanation.

deliveredinteger nullable

Leads delivered by this signal in the window: the leads you can see. A lead removed by a final automatic check (e.g. a company-size recheck) is counted under that filter, not here.

signalIdstring required

The signal's id.

countedAtstring date-time required

When these numbers were computed. They can be up to an hour old.

Example response

{
  "awaitingReview": 0,
  "candidateUnit": "person",
  "candidatesFound": 2399,
  "countedAt": "2026-09-25T09:00:00+00:00",
  "countingMethod": "per_candidate",
  "delivered": 0,
  "filterOrder": [
    "companySize",
    "location",
    "persona",
    "industry",
    "excluded",
    "duplicate"
  ],
  "otherBreakdown": {
    "companyNotIdentified": 8
  },
  "passedFilters": 0,
  "rejectedBy": {
    "companySize": 1157,
    "duplicate": 1,
    "excluded": 0,
    "industry": 113,
    "location": 226,
    "other": 8,
    "persona": 894
  },
  "signalId": "b92cde5c-7381-4d85-9c9b-3287301d053d",
  "signalTypeId": "competitor_relationship_monitoring",
  "supported": true,
  "topRejected": {
    "companySize": [
      {
        "count": 412,
        "value": "11-50"
      },
      {
        "count": 208,
        "value": "1001-5000"
      },
      {
        "count": 95,
        "value": "unknown"
      }
    ],
    "industry": [
      {
        "count": 22,
        "value": "Software Development"
      }
    ],
    "location": [
      {
        "count": 31,
        "value": "London, England, United Kingdom"
      }
    ],
    "persona": [
      {
        "count": 10,
        "value": "Enterprise Account Executive"
      },
      {
        "count": 7,
        "value": "Chief Executive Officer"
      },
      {
        "count": 6,
        "value": "Board Member"
      }
    ]
  },
  "unattributed": 0,
  "window": {
    "days": 30,
    "from": "2026-08-26T09:00:00+00:00",
    "to": "2026-09-25T09:00:00+00:00"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.