Lead lists · 2026
How to dial a list straight from Google Sheets
Last updated September 3, 2026
Most articles on this query spend eight hundred words deciding which dialer to buy. That decision is already covered in calling a list of leads from a spreadsheet. This page is the part nobody writes down: what to do to the sheet itself, the one formula that fixes the phone column, the three routes out of Sheets with an honest effort estimate on each, and the import errors you will hit at 4pm on the day you wanted to start dialing.
The short answer
Google Sheets has no dialer, so you either bolt one on or take the list somewhere that has one. Do the same three things first regardless of route: one row per phone number, a plain-text helper column holding every number in E.164 (+14155551234), and duplicates removed by number rather than by name. Then: Apps Script plus Twilio if you write code and want the sheet to stay the system of record; a click-to-call extension if you have a dozen calls; export to CSV and import into a power dialer if you actually intend to call the whole list.
Step 1: get the shape of the sheet right
A dialer reads a row as one dial. That single sentence dictates the layout, and it is where most sheets are wrong. If a company has three contacts, that is three rows, not one row with three numbers in a cell. If a contact has a direct dial and a mobile, that is either two rows or two columns you will import as two separate lists — never 555-1234 / 555-9876 in one cell, which no importer can split reliably.
Required: exactly one column of phone numbers. That really is the only field a dialer cannot work without. Worth having: first name (for the opener), company, and something that tells you the local time — state, area code or an explicit timezone column — so that calling hours are a lookup rather than a guess. Optional but useful later: a source column, a date-added column, and any field you want visible on the call screen.
Split full names before you import. A single Name column produces openers like "Hi Jennifer Alvarez-Ruiz". In Sheets: =SPLIT(A2, " ") for clean two-word names, or first name only with =REGEXEXTRACT(TRIM(A2), "^\\S+"), which survives middle names and suffixes. Then paste-special as values, because formulas that reference deleted columns export as errors.
Delete everything above the header row. A merged title cell saying "Q3 Prospect List — FINAL v4" is the single most common reason a CSV import shows blank column names.
Step 2: normalise the phone column to E.164
E.164 is the international standard format: a plus sign, the country code, then the national number, with nothing else. +14155551234. No spaces, no brackets, no dashes, no leading zero from the national trunk code. Every telephony API on earth, Twilio included, wants numbers in this form, and a list that arrives already in it never gets misdialed because a tool guessed the wrong default country.
Add a helper column, format it as plain text first (Format → Number → Plain text — otherwise Sheets sees a leading plus and tries to do arithmetic), and paste this into row 2:
=LET(
d, REGEXREPLACE(TO_TEXT(A2), "[^0-9]", ""),
IF(d = "", "",
IF(LEFT(TRIM(TO_TEXT(A2)), 1) = "+", "+" & d,
IF(AND(LEN(d) = 11, LEFT(d, 1) = "1"), "+" & d,
IF(LEN(d) = 10, "+1" & d,
"CHECK: " & d)))))It strips every non-digit, then decides by length. Ten digits gets +1. Eleven digits starting with 1 gets a plus. Anything already starting with a plus is kept as-is with its punctuation removed. And crucially, anything that does not fit is labelled CHECK: rather than silently mangled — a seven-digit number with no area code, a nine-digit typo, an extension glued onto the end. Filter on CHECK:, fix or delete those rows, and the rest of your list is safe to dial.
For a UK list the same idea with a different trunk rule — UK numbers are written with a leading 0 that the country code replaces:
=LET(
d, REGEXREPLACE(TO_TEXT(A2), "[^0-9]", ""),
IF(LEFT(d, 2) = "44", "+" & d,
IF(LEFT(d, 1) = "0", "+44" & MID(d, 2, 20),
IF(LEN(d) = 10, "+44" & d,
"CHECK: " & d))))Two artefacts survive both formulas because they are not in the phone column. Non-breaking spaces from a web page paste: =TRIM(SUBSTITUTE(A2, CHAR(160), " ")) on the name and company columns. And leading apostrophes, which Sheets uses as an invisible "treat this as text" marker — normally stripped on CSV export, but they survive a round trip through Excel or a copy-paste out of a rendered table, at which point they become a literal character inside your data. The REGEXREPLACE above removes them from numbers; for text columns, =IF(LEFT(A2,1)="'", MID(A2,2,999), A2).
Step 3: dedupe by number, not by name
Do this after normalising, never before. (415) 555-1234 and 415.555.1234 are not duplicates to a spreadsheet but are the same dial to a prospect, and the fastest way to sound like a nuisance is to call the same person twice in one session from two rows of the same list. Once the helper column is E.164, exact-match dedupe works.
Flag rather than delete, so you can see what you lost. With E.164 in column B:
=IF(COUNTIF($B$2:B2, B2) > 1, "dupe", "keep")The half-open range $B$2:B2 is the trick — it counts only rows above the current one, so the first occurrence reads keep and every repeat reads dupe. Filter to keep and export that. If you would rather just see the clean set, =UNIQUE(B2:B) in an empty column gives you the distinct numbers, and =COUNTA(B2:B) - COUNTA(UNIQUE(B2:B)) tells you how many rows the dedupe removes before you commit to it. Deduping properly, along with everything else worth doing to a raw list, is covered in how to clean a lead list before you dial it.
The three routes out of Google Sheets
| Route | Who it is for | Setup effort | How it dials | Call logging |
|---|---|---|---|---|
| Apps Script + Twilio | Someone who writes code and wants the sheet to stay the system | 1–2 days to something usable | One at a time, script-triggered | Whatever you write yourself |
| Click-to-call add-on / extension | Occasional calling, a dozen rows, no setup appetite | 5 minutes | One click and one wait per row | Usually none, or a note in the softphone |
| Export CSV → power dialer | Anyone calling the whole list | 10 minutes including the Twilio connection | Auto-advance, no clicking between calls | Disposition, notes, recording, timestamps |
Route 1: Google Apps Script and the Twilio API
The core is genuinely small. A UrlFetchApp.fetch POST to /2010-04-01/Accounts/{SID}/Calls.json with To, From and a Url pointing at some TwiML, authenticated with your Account SID and auth token, is about fifteen lines and works the first time you run it. Twilio itself publishes Apps Script walkthroughs for the messaging equivalent, and voice is the same shape.
The honest estimate is one to two days to something you would use, and here is where it goes. The API call rings the prospect, not you, so you need TwiML that dials your own phone and bridges the two legs — or a browser client, which Apps Script cannot host. Credentials must go in Script Properties, not the source. Apps Script kills a function after roughly six minutes, so any loop over a long list has to be a trigger that processes a batch and remembers its cursor. Then you want the outcome written back to the row, retry handling, and some way to stop halfway through, which in a script means a cell you check on each iteration. What you will not have at the end: recordings, dispositions, a call screen showing the lead, connect-rate reporting, or anyone to ask when it breaks. If you want to see the full build laid out, we costed it in how to build a power dialer with Twilio.
Route 2: a click-to-call add-on or extension
There are Google Workspace add-ons and browser extensions that turn the selected cell into a call — some through a softphone you install, some by opening a tel: handler. The zero-install version of the same idea is a formula: =HYPERLINK("tel:" & B2, B2) makes every row a clickable link that your desktop softphone or paired phone will pick up.
This is fine for twelve calls and miserable for a hundred and twenty. You click, you wait through four rings, you decide, you click back to the sheet, you find your place, you type a note, you click the next one. The clicking and the finding-your-place is roughly half of the working time, which is the entire reason power dialers exist. Also check the add-on's permission scope before installing: several ask for read access to all your spreadsheets.
Route 3: export to CSV and import into a dialer
File → Download → Comma-separated values (.csv, current sheet). Note current sheet: a workbook with four tabs exports only the one you are looking at, which catches people out. Then import the file, confirm the column mapping, and dial.
In DialSheet the mapper reads your header row and matches the obvious names itself — phone, mobile, first name, company, email — and phone is the only field it insists on. Anything it does not recognise becomes a custom field that shows on the call screen, so a "Renewal date" column from your sheet is still in front of you when the prospect picks up. Press start and it dials down the list, connects you when someone answers, and advances as soon as you pick a disposition. Every call is logged with time, duration and outcome, which is the thing the sheet can never give you.
What each route costs a month
Assume one rep and 700 billed minutes a month — roughly 100 dials a day with a normal share of voicemails. Telephony is telephony: whether the call is placed by your Apps Script or by a dialer, on your own Twilio account you pay Twilio ~$0.014/min for the minutes and ~$1.15/month for the number, and nobody marks it up.
| Route | Software / mo | Twilio / mo | Total / mo | One-off cost |
|---|---|---|---|---|
| Apps Script you write | $0 | $11 | $11 | 1–2 days of your time, then maintenance |
| Click-to-call extension | $0–20 | varies by softphone | $0–20+ | None, but a click per row forever |
| CSV import, free plan | $0 | $11 | $11 | Caps at 500 leads and 500 calls a month |
| CSV import, Pro | $29 | $11 | $40 | Covers 3 people, not one |
The comparison people expect to matter — free script versus paid tool — comes out at $29 a month against one to two days of engineering plus every future evening it breaks. If you truly never want a Twilio account there is a $49 managed line for US and Canada, covered in power dialer without Twilio.
Keeping the sheet and the dialer in sync
Be honest about this one: live two-way sync between a Google Sheet and a dialer does not exist unless you build it, and building it means resolving conflicts when both sides edited the same lead. Pick a direction.
- One-way, manual (what most people should do). The sheet is the loading dock; the dialer owns everything after the first dial. Export the dialer back to CSV whenever you want a snapshot in Sheets for a report. No moving parts, nothing to break.
- One-way, automated in. A scheduled Apps Script reads new rows and POSTs them to the dialer's API, keyed on the E.164 number so re-running it never creates duplicates. This is the right pattern when a form or a scraper feeds the sheet continuously.
- Webhook out. Subscribe to the call-completed event; the payload carries the number, disposition, duration and recording link. Point it at an Apps Script web app deployed with
doPost, match the row on the E.164 number, write the outcome. Match on the number, never on the name — names have spelling variants, E.164 strings do not.
DialSheet exposes a REST API, webhooks and an MCP server for all three patterns; the endpoints are in the API docs.
CSV import failures and what causes them
| Symptom | Cause | Fix |
|---|---|---|
| Nothing imports; the file is rejected | You downloaded XLSX, not CSV. Sheets defaults to XLSX in some menus. | File → Download → Comma-separated values (.csv, current sheet). |
| Column names are blank or "Column 1" | A merged title row or a blank row sits above the header. | Delete every row above the header so row 1 is the header. |
| Every phone row is invalid | The file passed through Excel and the numbers became floats (2069792347.0) or scientific notation (2.069792347E9). | Import the E.164 helper column instead, or re-export from Sheets without touching Excel. A good importer un-mangles both forms; not all do. |
| US numbers dial the wrong place | Ten-digit numbers imported without a country code, and the dialer guessed a different default country. | Ship E.164 with the +1 already on it and the guess never happens. |
| Leading zeros or the plus sign vanished | The column was number-formatted, so Sheets treated +44 20 7946 0000 as arithmetic. | Format → Number → Plain text on the column, then paste values. |
| Names show as ?? or é | The CSV was exported in UTF-16 or Latin-1. | Sheets exports UTF-8 by default; if the file came from elsewhere, re-save it as UTF-8. |
| Rows split in the middle | A notes field contains an unescaped comma, quote or line break. | Wrap free-text fields in quotes, or drop the notes column from the import file and keep it in the sheet. |
| The list is 30% shorter than the sheet | Blank phone cells. Phone is the one field that cannot be empty. | Filter the sheet on a non-empty phone column before exporting. |
The third row is not hypothetical. A 60,000-lead import in September 2026 arrived with 21,528 numbers in the 2069792347.0 form after a trip through Excel — every one of them a perfectly valid US number underneath, all of them initially rejected as unparseable. Our importer now un-mangles the float and scientific-notation forms before parsing, but plenty of tools do not, and the cheapest defence is to import the E.164 helper column and never let Excel see the raw one.
Take the sheet, get the calls
Import your CSV, confirm the mapping, press start. Free for one person up to 500 leads and 500 calls a month, no credit card, on your own Twilio account at wholesale. Teams start at $29 for 3 seats.
Import a list freeQuestions people actually ask
Can you call phone numbers directly from Google Sheets?
Not natively — Google Sheets has no dialer. There are three working routes. Write a Google Apps Script that posts each row to the Twilio Calls API, which works but takes a developer most of a weekend to make usable. Install a click-to-call add-on or browser extension that turns the selected cell into a call through a softphone, which is fine for a dozen calls but still costs you one click and one wait per row. Or export the sheet to CSV and import it into a power dialer, which auto-advances through the whole list and logs the outcome of each call. For calling an entire list, only the third route removes the clicking.
How do I format phone numbers in Google Sheets for a dialer?
Convert the column to E.164: a plus sign, the country code, then the national number with no spaces, dashes or brackets — +14155551234. In Google Sheets, strip everything that is not a digit with REGEXREPLACE, then add the country code based on length: ten digits gets +1, eleven digits starting with 1 gets a plus in front, anything else is flagged for a human to look at. Format the result column as plain text before you paste values into it, or Sheets will treat it as a number and drop the plus sign.
How do I auto-dial a Google Sheet without writing code?
File then Download then Comma-separated values, then import the CSV into a power dialer and map the phone column. In DialSheet the mapper reads your header row and matches common names automatically, and phone is the only required field — everything else becomes a lead field you can see on the call screen. Press start and it dials row by row, connects you when someone answers and moves on when you pick a disposition. That is the entire setup; there is no script, no add-on permission prompt and no softphone to install.
Is Google Apps Script plus Twilio a good way to build a dialer?
It is a good way to fire outbound calls from a sheet, and a poor way to make cold calls. A UrlFetchApp POST to the Twilio Calls API is about fifteen lines and works the first time. What takes the rest of the weekend is everything around it: TwiML to bridge the call to your own phone, storing credentials in Script Properties, the six-minute Apps Script execution limit that breaks any loop over a long list, writing the outcome back to the row, retries, and a way to stop mid-session. Budget one to two days for a working version and expect it to have no call recording, no dispositions and no reporting.
Why did my Google Sheets CSV fail to import into a dialer?
The five common causes are: the file was exported as XLSX rather than CSV; the sheet has a title row above the header so the importer reads merged or blank column names; the phone column passed through Excel and arrived as 2069792347.0 or 2.069792347E9 rather than a phone number; the export was UTF-16 or Latin-1 so accented names became question marks; or numbers lost their leading zero or plus sign because the column was formatted as a number. Fix the phone column at the source with a text-formatted E.164 helper column and most of these disappear at once.
How do I keep my Google Sheet in sync with the dialer after calling?
Pick one system as the source of truth, because two-way live sync between a sheet and a dialer does not exist without building it. The simple pattern is one-way: the sheet loads the list, the dialer owns outcomes, and you export the dialer back to CSV when you want a snapshot in Sheets. The automated pattern is a webhook on call completion posting to a Google Apps Script web app that writes the disposition into the matching row, keyed on the E.164 number rather than the name. DialSheet exposes a REST API and webhooks for exactly this.
What columns does a calling list actually need?
One required column: the phone number, in E.164. Everything else is optional but three are worth having — first name for the opener, company for context, and a timezone or state column so you can respect calling hours. Split full names into first and last before import; a dialer that greets someone as "Hi Jennifer Alvarez-Ruiz" on the first line sounds like a robot. Keep one row per phone number, not one row per company with three numbers in one cell, because a dialer treats a row as a dial.