# Changelog of Bulk Phone Number Validator & Cleaner — E.164, $0.50/1k (`yabanana99/phone-number-cleaner-validator`) Actor

- **URL**: https://apify.com/yabanana99/phone-number-cleaner-validator/changelog.md
- **Full Actor documentation**: https://apify.com/yabanana99/phone-number-cleaner-validator.md

## Changelog

### 0.1

- First release: bulk phone-number cleaning and validation with Google's
  libphonenumber metadata, **entirely offline** — parse → validate (valid /
  possible / invalid) → normalize to E.164 → line type → region and country
  code → carrier, area and time zones → deduplicate on E.164 → segment.
- Input from CSV, TSV, XLSX, JSON, JSON Lines and plain-text lists (URL, base64
  or key-value-store record) plus inline numbers; up to 20 files and 200000
  rows per run, merged and deduplicated together.
- **The phone column is chosen on the data, not on the header name.** A column
  matched by name must actually hold numbers (≥30% of sampled values valid or
  phone-shaped); if it does not, the column that does is used instead, and if
  no column qualifies nothing is read and nothing is charged. The column
  finally used is always reported: `Phone column: 'Mobile' (index 2)`.
- **Numbers buried in free text are extracted**, not rejected: `call me on
  333 1234567 after 6pm` becomes a clean contact. Extraction only accepts
  numbers that are *valid*, so an order id inside a sentence is never mistaken
  for a phone number, and a trailing word glued to a number
  (`+39 333 1234567 (home)`) no longer turns it into a "too long" verdict —
  libphonenumber's letter-to-digit rule is neutralised by comparing the digits
  the parser produced with the digits the cell actually contains.
- **Deduplication on E.164**, so `+39 333 1234567`, `0039 333 1234567`,
  `(333) 123-4567` and `333 1234567` are one contact; extensions are kept
  apart. Duplicates are never charged twice.
- **A missing `default_region` stops the run before it bills anything**: if the
  numbers are national and no country code is set, the Actor says which region
  would validate them (measured over all 245 regions libphonenumber knows) and
  exits after charging only the run start. The suggestion is never applied
  silently.
- **Circuit breaker on the failure signature, not the failure rate**: a list of
  old, disconnected or malformed numbers is the job and is processed to the
  last row; a column of names, order ids or dates stops the run after the first
  1000 values with a diagnosis. `ignore_low_validity` overrides it.
- Values that are not phone numbers at all are delivered with a reason and
  **never charged**.
- Segment exports (`valid.csv`, `possible.csv`, `invalid.csv`) plus a
  cross-cutting `mobile.csv` for SMS campaigns, a `PHONE_REPORT` in the
  key-value store, and a final `_summary` dataset record carrying the whole
  report as a JSON string (so a CSV export gains one column, not thirty).
- Robustness: content beats file name (a PNG renamed `.csv` is refused, UTF-16
  is decoded properly), an unbalanced quote is detected by comparing the
  numbers extracted with the numbers visible in the raw text and re-parsed only
  when that recovers more *numbers* (never more rows), spreadsheet formulas
  with no cached value are reported, wide exports are capped at 1024 columns
  and every cell at 2000 characters inside the shared row normalizer, dataset
  records are truncated below 8 MB, `push_data` failures fall back record by
  record, and an empty or unreadable input ends the run as `SUCCEEDED` with a
  status message instead of `FAILED`.
- SSRF-guarded downloads (private, loopback, link-local, multicast, reserved,
  CGNAT `100.64.0.0/10` and IPv4-mapped IPv6 addresses are refused, redirects
  included). Base image pinned by digest, dependencies capped.
