Bulk SMS CSV Format: The Complete Guide to Preparing Contact Lists That Send Cleanly

A bulk SMS CSV is only as good as its formatting. The right format, E.164 phone numbers, UTF-8 encoding, deduplicated rows, and the columns your platform actually needs, is the difference between clean delivery and a send where a fifth of your messages bounce, duplicate, or garble. This guide gives you the exact column structure, formatting rules, and a pre-send checklist that works with virtually every bulk SMS platform.

Why CSV Format Directly Affects Delivery and Cost

Every malformed number in your file is money spent on a message that cannot arrive, and on some platforms, a failed API call that still counts against rate limits. The most common damage comes from five formatting sins: local-format numbers without country codes, Excel silently stripping the leading +, duplicate rows from merged lists, non-UTF-8 characters corrupting names, and header names your platform does not recognize. All five are preventable in about ten minutes of prep.

The Required Columns

Most platforms need surprisingly few columns. Build your file around this core set, then add custom fields only if your messages use personalization variables.

Column Required? Format Example
phone Yes E.164: + + country code + national number, digits only after the + +14155552671
first_name Recommended Plain text, UTF-8 Maria
last_name Recommended Plain text, UTF-8 Santos
country Recommended ISO 3166-1 alpha-2 US
opt_in_date Strongly recommended ISO 8601 YYYY-MM-DD 2026-03-14
opt_in_source Strongly recommended Short label website-form
timezone Optional but valuable IANA name America/Chicago
custom_1, custom_2 Optional Any short text for personalization Dallas, VIP

Keep a header row. Every major platform expects one. Use lowercase snake_case headers with no spaces. Name the phone column phone (aliases like mobile, msisdn, or number work on some platforms but phone is the most universally accepted).

For list-quality fundamentals before you format anything, see our bulk phone number validation workflow.

E.164 Phone Number Formatting, Explained

E.164 is the international standard: a leading +, then the country code, then the national number with no spaces, dashes, parentheses, or leading zeros. It is the only format you should store.

Raw input E.164 output What was wrong
(415) 555-2671 +14155552671 Missing country code, punctuation
0044 7700 900123 +447700900123 International prefix 00 instead of +
07700 900123 +447700900123 Local format with trunk 0
+1 415 555 2671 +14155552671 Spaces (harmless to humans, fatal to some parsers)
14155552671 +14155552671 Missing +

Conversion rules: strip every non-digit character except a leading +; replace a leading 00 with +; drop the national trunk 0 after adding the country code; then validate length (7-15 digits after the + is the E.164 range, though most mobile numbers cluster at 10-12). Rows that cannot be converted to a plausible E.164 number should be quarantined, not guessed at. A wrong number texts a stranger.

Encoding, Dedupe, and the Excel Traps

Save as UTF-8. Names with accents (José, François, Müller) corrupt into mojibake when a file is saved in Excel's default encoding and uploaded as-is. In Excel use Save As → CSV UTF-8; in Google Sheets, downloads are UTF-8 by default.

Deduplicate on the normalized number. The same contact as +14155552671, 415-555-2671, and (415) 555-2671 is three rows until you normalize first, then dedupe. Sending triplicates to one person is a fast track to STOP replies and complaints.

Beware Excel's "helpfulness": it strips leading zeros (0044... becomes 44... as a number), converts long digit strings to scientific notation, and mangles the + prefix. Defenses: format the phone column as Text before pasting data, or do the normalization in a script/Google Sheets and export from there. Never round-trip a finished CSV through Excel without re-checking the phone column.

One row per recipient. Do not put multiple numbers in one cell separated by commas or slashes, split them into separate rows. Keep the file under your platform's row limit per upload (commonly 50k to 500k rows); larger lists should be chunked.

Sample CSV Template

Copy this structure as your starting point:

`csv phone,first_name,last_name,country,opt_in_date,opt_in_source,timezone +14155552671,Maria,Santos,US,2026-03-14,website-form,America/Los_Angeles +447700900123,James,Okafor,GB,2026-02-28,keyword-JOIN,Europe/London +971501234567,Aisha,Khan,AE,2026-04-02,point-of-sale,Asia/Dubai `

Note the opt_in_source values: specific labels like website-form or keyword-JOIN are exactly what 10DLC reviewers want to see documented. If your opt-in flow ever gets questioned, this column is your evidence, which is also why describing your opt-in for 10DLC matters before you send.

Pre-Send Checklist

Run these checks on every file, every time:

  1. Header row present, lowercase snake_case, phone column named phone.
  2. Every number in E.164: leading +, correct country code, no trunk zeros, no spaces or punctuation.
  3. File saved as UTF-8; spot-check accented names after upload.
  4. Duplicates removed on the normalized number.
  5. opt_in_date and opt_in_source populated, no blank consent fields.
  6. Timezone column present if scheduling across zones (see SMS quiet hours by state).
  7. Message templates fit segment limits, check with free SMS character counter.
  8. Test send to 5 to 10 internal numbers across carriers before the full blast.
  9. Suppression list applied (opt-outs, DNC, hard bounces from prior sends).
  10. Row count matches expectations after dedupe, investigate any big drop.

For deeper sending strategy, compare providers in our bulk SMS API comparison, and if you need data to start from, our phone number databases ship in clean, send-ready CSV format.

Frequently Asked Questions

Can I use .xlsx instead of .csv?

Most platforms accept CSV universally; many also accept XLSX. CSV is safer, no hidden sheets, no formatting surprises, and it forces the UTF-8 question into the open.

What delimiter should I use?

Comma. Semicolons appear in European exports (where commas are decimal separators) and break naive parsers. If your source file uses semicolons, convert before uploading.

How do I handle contacts with multiple numbers?

One row per number. If you need to keep them linked, add a contact_id column shared across the rows.

Should I include landlines?

Only if your use case supports voice fallback. For SMS-only campaigns, filter to mobile/voip lines during validation, texting landlines wastes sends.

What is the maximum safe file size?

There is no universal limit, but uploads above ~50,000 rows deserve chunking: smaller batches isolate formatting errors, make delivery reports readable, and let you pause if something looks wrong.

Do I need consent columns for 10DLC?

You need consent evidence, and these columns are the practical form of it. Reviewers ask how consent was obtained; opt_in_date + opt_in_source per row is the cleanest possible answer.