Batches

A batch is what happens when you hand us a phone list file. We accept the file, parse it, dedupe it, validate the unique numbers, and produce downloadable CSV exports you can use immediately.

What it is

A batch represents one upload-and-clean job. Every batch has:

  • A unique id (UUID).
  • An owner (you) and a status timeline.
  • An input file in object storage, two primary output files (full results + SMS-ready shortlist), and filtered exports generated from the completed results when you need them.
  • A ledger of credits debited and refunded, all linked to the batch id.

Who uses it

  • Web users, upload a file from /app/upload, watch the progress, then download full results, SMS-ready rows, valid rows, call-ready rows, or a deduped-best export when it's done.
  • API integrators, list recent dashboard-created batches from /v1/batches, poll /v1/batches/:id, and fetch primary result URLs from /v1/batches/:id/results.
  • Webhook subscribers, receive batch.completed or batch.failed events with batch counters and status details. Download URLs are minted on demand from the dashboard or API.

How it works (under the hood)

  1. Upload. The CSV is stored encrypted in a private bucket. We compute a checksum to short-circuit duplicate uploads.
  2. Parse + preview. Headers are extracted; you pick the phone column (or we auto-detect phone, mobile, cell, tel).
  3. Local pass. Every selected phone value is run through our parser for free, duplicates collapse to one, obviously malformed values are marked and never sent to the paid engine.
  4. Cache check. Each unique normalised number is looked up in our 24-hour result cache. Cache hits cost zero credits.
  5. Paid validation. Only the unique, parseable, uncached numbers reach our paid validation engine. Each one debits one credit.
  6. Join + write. Results are joined back to your original file order. Primary output CSVs are written to object storage; filtered exports can be generated from the full results without running the provider again.
  7. Notify. You get an in-app status update, an email (if you have batch-completed mail enabled), and any subscribed webhooks fire.

What you give it (inputs)

FieldRequiredNotes
Input fileyesCSV, TSV, TXT, XLSX, or JSON. Up to 100,000 rows and 50 MB in v1. UTF-8 preferred (we auto-detect UTF-16 BOM). Header row required for spreadsheet-style files.
Phone columnyesAuto-detected from common names; select the single column you want cleaned. The picker can show every column header in your file if the first guess is wrong.
Default countryconditionalRequired when more than 70% of numbers are in local format (no leading +). Used as the dial-plan when parsing. Suggested from your billing country or your file's own country column.
Simplified export togglenoOff by default. Off ⇒ output preserves your original columns with cleaning columns appended. On ⇒ output is just the cleaning columns.

What you get back (outputs)

Two primary CSV files with pre-signed download URLs valid for about 10 minutes. Re-issue a fresh URL any time from the batch detail page or the API.

full_results.csv

One result row per uploaded row, in the original file order. Original columns are preserved verbatim; our cleaning columns are appended on the right. See CSV format for the per-column contract.

sms_ready_only.csv

A shortlist of just the rows where sms_ready=true. Smaller column set so you can hand it straight to your sender: original_row_number, source_phone_column, input_phone, normalized_e164, country, line_type, carrier, reason.

Filtered exports

The batch detail page can also generate filtered CSVs from the full results without spending credits again:

  • valid_rows_only.csv, rows with a normalized number and no invalid, malformed, unresolved, or provider-failed result.
  • call_ready_only.csv, valid non-duplicate mobile, landline, and VoIP rows for calling workflows.
  • deduped_best_rows.csv, one row per number, preferring the row with more original contact data.

Campaign tool presets

Completed batches can also generate derived CSVs for Twilio SendGrid, HubSpot, Klaviyo, Pipedrive, and generic dialers. These are download presets, not native integrations, so they spend no credits and make no third-party API calls.

  • twilio_sendgrid_contacts.csv, SMS-ready rows with phone_number_id in the first column.
  • hubspot_contacts_sms_ready.csv, SMS-ready contacts with Mobile phone number.
  • klaviyo_sms_contacts.csv, SMS-ready rows with documented Klaviyo country values where available. Consent is not encoded.
  • pipedrive_people_call_ready.csv, call-ready people for sales workflows.
  • generic_dialer_call_ready.csv, deduped call-ready E.164 phone rows.

Status timeline

StatusWhat it means
uploadedFile is in storage; waiting for the worker.
processingWorker is parsing, deduplicating, and looking up numbers.
completedBoth result CSVs are ready; webhooks have fired.
failedUnrecoverable error during processing. Credits debited for unprocessed rows are automatically refunded.
cancelledStopped by you (only allowed in uploaded) or by an admin. Same refund treatment as failed.

Pricing

  • 1 credit per unique, parseable, uncached number sent to the paid validation engine.
  • 0 credits for duplicates within the batch.
  • 0 credits for malformed rows.
  • 0 credits for cache hits (within the 24-hour cache window).
  • Refund on every row that hard-fails the paid lookup.

Concretely: a 10,000-row CSV with 20% duplicates, 5% malformed, and 30% cache hits debits 4,500 credits, not 10,000.

Limits

  • 100,000 rows per batch (v1).
  • 1 active batch per account at a time (v1).
  • File size: 50 MB. We stream parse the file memory is not the bottleneck, but very large files take longer.
  • API rate limits still apply: 6000 req/min, 1000000 req/day per API key. The batch endpoints count as one request each.

Errors

CodeWhat it meansWhat to do
bad_csvWe couldn't parse the file (encoding, BOM, line-ending).Re-export from your tool as UTF-8 CSV with header row.
missing_phone_columnThe column you selected doesn't exist in this file.Pick a different column in the upload preview.
insufficient_creditsYour balance won't cover the worst-case credit cost for this file (rows minus dups minus locally-malformed).Buy more credits or split the file.
row_limit_exceededFile has more than 100,000 rows.Split the file. We'll lift this limit in v2.
provider_unavailableThe paid validation engine couldn't complete the lookups. Per-row credits are refunded and the batch is marked failed.Retry the batch from the detail page. Already-debited credits stay refunded.

Example (API)

After a dashboard upload, track the batch and download the primary result files via the API:

# 1. List recent batches
curl https://api.phonelistcleaner.com/v1/batches \
  -H "Authorization: Bearer plc_xxx" \
# -> { "batches": [{ "id": "ba_01J...", "status": "processing", ... }] }

# 2. Poll
curl https://api.phonelistcleaner.com/v1/batches/ba_01J... \
  -H "Authorization: Bearer plc_xxx"
# → { "id": "ba_01J...", "status": "completed",
#      "counters": { "total_rows": 10000, "credits_used": 4500,
#                    "successful_rows": 6321 } }

# 3. Download primary result URLs
curl https://api.phonelistcleaner.com/v1/batches/ba_01J.../results \
  -H "Authorization: Bearer plc_xxx"
# → { "batch_id": "ba_01J...",
#      "urls": { "full": "https://...", "sms_ready": "https://..." },
#      "expires_in_seconds": 600 }

Related