How to filter a leads csv to a specific lead source
- Step 1Export the leads CSV from your CRM — HubSpot: Contacts → Export with the
Original Source/Lead Sourceproperty included. Salesforce: a report or data export includingLeadSource. Pipedrive / Zoho: the leads export with the source field. Note the exact column name and a couple of sample values. - Step 2Drop the file onto the filter above — PapaParse parses it in your browser and auto-detects the delimiter. Headers populate the Filter column dropdown — pick
Lead Source,Original Source,LeadSource, orutm_source. - Step 3Choose equals for a clean source or contains for UTM compounds — If the column holds tidy values (
Referral), useequals. If it holds compound UTM strings (paid_social_meta_q2), usecontainsand type the segment you care about (paid_social). - Step 4Type the source value exactly — Copy a value from a sample row to avoid spelling mismatches —
Organic Search,Paid Social. Casing is ignored by default. Special characters (_,/, parentheses in(direct)) are matched literally. - Step 5Filter and verify the count — Click Filter rows. The panel shows Rows matched, Rows filtered out, and a preview of the first 10 matching leads. Cross-check Rows matched against your CRM's reported count for that source before sharing.
- Step 6Download, or split by every source at once — Download gives
<name>.filtered.csv. To produce one file per source in a single step (one for the agency, one per channel owner), use CSV Column Value Splitter — it emits<name>.<source>.csvfor each distinct source.
Operators mapped to lead-source filtering
All 10 operators are available. Matching is case-insensitive by default and literal (never regex).
| Operator | Lead-source use | Needs a value? |
|---|---|---|
equals | Exact source — equals Referral keeps only referral leads | Yes |
contains | UTM-compound or fuzzy source — contains paid catches paid_social, paid_search (default operator) | Yes |
starts_with | Source family by prefix — starts_with Organic for Organic Search, Organic Social | Yes |
ends_with | Suffix grouping — ends_with _meta for any Meta channel | Yes |
not_equals | Everything except one source — not_equals Cold Outreach | Yes |
not_contains | Drop a source family — not_contains paid keeps only non-paid leads | Yes |
is_empty | Leads with no source attributed — a data-quality / unattributed bucket | No |
is_not_empty | Keep only leads that have a source set | No |
greater_than / less_than | Numeric only — use on a Lead Score or Deal Value column, not the source text | Yes |
Source-field shapes by CRM
CRMs name and shape the source field differently. Pick the column that drives your filter.
| CRM | Source column(s) | Typical values |
|---|---|---|
| HubSpot | Original Source, Original Source Drill-Down 1/2 | ORGANIC_SEARCH, PAID_SOCIAL, DIRECT_TRAFFIC, REFERRALS, OFFLINE |
| Salesforce | LeadSource | Web, Phone Inquiry, Partner Referral, Purchased List, Other |
| Pipedrive | Source / Source channel | Search engine, Social media, Direct, Referral, Email |
| Raw UTM capture | utm_source + utm_medium | google / cpc, facebook / paid, newsletter / email |
| Spreadsheet / manual | Lead Source | Free-text, often inconsistent (Organic Search vs organic vs SEO) |
Cookbook
Real before/after slices from lead exports. Contact PII anonymised. Matching is case-insensitive and literal unless noted.
Keep only Organic Search leads for the SEO analyst
ExampleHand the SEO channel owner only their leads. equals Organic Search on the source column gives the exact slice, with no other channel's contacts exposed.
Input (CRM leads export): Email,Name,Lead Source,Score a@x.com,Alex,Organic Search,72 b@x.com,Bea,Paid Social,55 c@x.com,Cy,Organic Search,40 d@x.com,Dee,Referral,88 Config: column = Lead Source, operator = equals, value = Organic Search Output (.filtered.csv): Email,Name,Lead Source,Score a@x.com,Alex,Organic Search,72 c@x.com,Cy,Organic Search,40 Rows matched: 2 · Rows filtered out: 2
All paid channels via a contains substring
ExampleYou want every paid lead regardless of network. With UTM-style values, contains paid catches paid_social, paid_search, and paid_display in one pass.
Input: email,utm_source p1@x.com,paid_social_meta p2@x.com,organic_search p3@x.com,paid_search_google p4@x.com,referral Config: column = utm_source, operator = contains, value = paid Output: email,utm_source p1@x.com,paid_social_meta p3@x.com,paid_search_google Rows matched: 2
Case-mixed free-text source still matches
ExampleManual entry produces Organic Search, organic search, and ORGANIC SEARCH. The default case-insensitive match catches all three without cleaning the column first.
Input: email,Lead Source l1@x.com,Organic Search l2@x.com,organic search l3@x.com,ORGANIC SEARCH l4@x.com,Referral Config: column = Lead Source, operator = equals, value = organic search (Case-sensitive checkbox OFF) Output: email,Lead Source l1@x.com,Organic Search l2@x.com,organic search l3@x.com,ORGANIC SEARCH Rows matched: 3
Unattributed leads for a data-quality cleanup
ExampleLeads with a blank source distort attribution reports. is_empty isolates them so you can backfill the source before reporting. It checks for a literally empty cell — a space won't count as empty.
Input: email,Lead Source q1@x.com,Referral q2@x.com, q3@x.com,Paid Social q4@x.com, Config: column = Lead Source, operator = is_empty (no value) Output: email,Lead Source q2@x.com, q4@x.com, Rows matched: 2
Two sources at once — chain or split, don't comma
ExampleThere's no multi-source box. contains Organic Search, Referral matches the literal substring, which no cell holds, so it returns zero. Use the splitter or chain negations.
Goal: keep {Organic Search, Referral}.
Wrong: contains 'Organic Search, Referral' → 0 rows.
Right (one file per source, then keep two):
/tool/csv-column-value-splitter on Lead Source →
leads.Organic Search.csv
leads.Referral.csv
leads.Paid Social.csv ...
Recombine the two you want with /tool/csv-merger.
Or, if those two are the only non-paid sources:
not_contains 'Paid' (one pass) keeps both.Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Comma-separated multi-source value box
Not supportedThere's no multi-value field. contains A, B matches the literal substring A, B, not source A OR B. To keep several sources, use CSV Column Value Splitter (one file per source) or use not_contains to drop the unwanted families in one pass.
Inconsistent capitalisation in the source column
Handled by defaultMatching is case-insensitive unless you tick Case-sensitive, so Organic Search, organic search, and ORGANIC SEARCH all match organic search. You don't have to run CSV Case Converter first — though doing so makes the column tidier for downstream reporting.
Spelling variants like SEO vs Organic Search
Filtered outequals Organic Search won't catch a row labelled SEO or organic — they're different strings. Normalise variants first with CSV Find & Replace, or use contains on a substring all variants share (rarely possible across SEO/Organic).
Source column is empty for some leads
Found via is_emptyUse is_empty to isolate unattributed leads. It checks for a literally empty cell; a cell containing only a space passes is_not_empty, not is_empty, so trim with CSV Whitespace Trimmer if your export pads blanks.
Filtering on a numeric lead score by mistake using equals
Works but exactequals on a Score column matches the exact text — equals 70 matches 70 but not 70.0 or 70 . For ranges use the numeric operators greater_than / less_than, which parse the cell as a float (non-numeric cells become NaN and never match).
HubSpot writes UPPER_SNAKE source codes
Match the exportHubSpot's Original Source exports as ORGANIC_SEARCH, PAID_SOCIAL, etc. Filter on those exact tokens (equals ORGANIC_SEARCH), or use the human-readable Drill-Down columns if your export includes them. Casing is ignored, but the underscores must match.
Free tier 500-row / 2 MB cap on a large lead list
Upgrade requiredFree accounts cap at 2 MB and 500 data rows. A full CRM export usually exceeds both. Pro raises the limit to 100 MB and 100,000 rows. Slice with CSV Row Limiter to work within free limits, or upgrade.
Need source AND another condition (e.g. score over 50)
Chain two passesOne pass = one condition. Filter Lead Source equals Organic Search first, download, re-drop, then filter Score greater_than 50. The header survives both passes. The orchestrator can run the two filter steps back to back.
Frequently asked questions
How do I extract just one lead source?
Pick the source column, choose equals (or contains for UTM compounds), type the source value, and click Filter rows. Download gives <name>.filtered.csv with the header plus only that source's leads. The result panel shows Rows matched so you can reconcile against your CRM's count for that source.
Can I keep multiple lead sources in one pass?
Not directly — there's no multi-value box, and a comma-separated value is matched as a literal substring (which returns nothing). To keep several sources, use CSV Column Value Splitter to emit one file per source and keep the ones you want, or use not_contains to drop an unwanted family in a single pass.
What if the source column has inconsistent capitalisation?
It's handled automatically — matching is case-insensitive by default, so Organic Search, organic search, and ORGANIC SEARCH all match organic search. For tidier downstream reporting you can still normalise with CSV Case Converter, but it's not required for the filter to work.
Is contact data uploaded?
No. Parsing and filtering run entirely in your browser via PapaParse. Names, emails, phone numbers, and deal values never reach a JAD Apps server — only an anonymous run counter is recorded for signed-in dashboard stats. This matters when handing slices to external agencies under GDPR/CCPA.
My CRM stores the source as a UTM compound — how do I filter it?
Use contains with the segment you care about. contains paid_social catches paid_social_meta_q2 and paid_social_li. contains paid catches every paid channel. The value is matched literally, so underscores and slashes are handled as-is, no escaping.
How do I find leads with no source attributed?
Use is_empty on the source column (it needs no value). It keeps rows where the cell is literally blank, so you can backfill attribution before reporting. Note it doesn't trim — a cell with only spaces won't match, so run CSV Whitespace Trimmer first if your export pads blanks.
Can I exclude one source instead of keeping it?
Yes — use not_equals (drop one exact source) or not_contains (drop a family). not_contains paid keeps every non-paid lead. There's no separate exclude toggle; the negative operators handle it.
Does the value act as a regex?
No. The value is matched literally, so (direct) with parentheses or paid_social with an underscore is matched character-for-character. Regex metacharacters have no special meaning here.
Can I filter by source and also by lead score?
Yes, by chaining. Filter the source first, download, re-drop the result, then filter the Score column with greater_than. The numeric operators use parseFloat, so non-numeric or blank scores become NaN and never match — they're filtered out, not treated as zero. The header is preserved through both passes.
How large a lead list can I filter?
Free tier caps at 2 MB and 500 data rows. Pro raises this to 100 MB and 100,000 rows. For very large exports, slice with CSV Row Limiter first or upgrade. Processing is in-browser, bounded by your machine's memory.
How is this different from splitting by source?
Column Filter keeps the rows matching one source. CSV Column Value Splitter produces one CSV per distinct source at once — ideal when every channel owner needs their own file. Use the filter for a single source; use the splitter to fan out all of them.
Can I automate per-source exports for monthly attribution?
Yes. GET /api/v1/tools/csv-column-filter returns the schema (column, operator, value, caseSensitive). Pair the @jadapps/runner once and POST your lead export to 127.0.0.1:9789/v1/tools/csv-column-filter/run. A monthly pipeline: CRM export → filter per source → deliver to each channel analyst. The runner is on-device, so contact PII never leaves your machine.
Privacy first
Processing runs locally in your browser with PapaParse. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.