Data Migration · API Reverse-Engineering · SMB

We Want to Leave Our Current SaaS — but the Export Is Broken. Now What?

Your vendor just hiked prices 4×. Their UI shows 12,420 records. Their CSV export gives you 11,903. Their API isn't documented. Their support ticket queue takes a week. You can't justify staying, but you can't justify leaving without your data either. Here's the 6-step framework we use to get every record out — cleanly, with proof.

Get a free migration feasibility audit
No commitment. We'll tell you whether your data is rescuable before you spend a dollar.

The vendor lock-in business model depends on one quiet assumption: that getting your data out costs more than staying. Every niche industry SaaS knows it. The customers who feel most trapped pay the most willingly — and the vendor knows you have years of customer records, jobs, invoices, notes, attachments, and workflow state stored on their servers. They also know that "export to CSV" is the bare minimum the contract required, and that the export was built to look complete without actually being complete. So when the renewal letter announces a 4× price hike, switching becomes a project that scares everyone in the room. (If your business handles EU customer data, the right to receive your data in a structured, machine-readable format is actually written into law under GDPR Article 20 on data portability.)

It doesn't have to be. The data is recoverable — almost always — through a combination of API reverse-engineering against the vendor's RFC 7231 HTTP endpoints, careful reconciliation, and a parallel-run migration. Below is the exact 6-step framework, with a real Python snippet of the kind of reconciliation pass we run before recommending any cutover. Once the data lands in your new system, the same dedup discipline from our CRM cleanup framework stops the migration from importing four years of legacy junk along with the actual records.

Worked example Logistics company, 40 employees: wants to leave a niche dispatch platform after a 4× price hike. The CSV export is missing roughly 4% of jobs. The API is undocumented. The vendor's support team takes a week to respond. Each card below shows what that gate of the framework would do for that single situation.
1
Inventory the Data

List every entity worth migrating: customers, jobs, invoices, files, statuses, notes, attachments.

The export's silent omissions are usually here.

Example
"We map every screen in the UI to its underlying entity. The dispatcher screen alone reveals 4 entities the CSV export doesn't include."
2
Test Every Export

Compare UI counts to CSV/API counts.

If they don't match, the export is hiding rows on purpose.

Example
"Dashboard says 12,420 jobs. CSV export gives 11,903 rows. That's 517 silently missing. Find them before any cutover plan."
3
Reverse-Map Fields and Enums

Decode hidden IDs, enum codes, and status flows.

The CSV's status_code = 7 has to become 'delivered, awaiting invoice' or your new system won't know what it means.

Example
"We map every internal enum: status_code = 7 → 'delivered_awaiting_invoice'. role_id = 4 → 'driver_lead'. payment_state = 9 → 'paid_late'. Without this map, the data is unusable."
4
Recover Missing Records

Use APIs, hidden reports, screen scraping, and backups to pull what the CSV export silently dropped.

Example
"Pull missing job IDs directly from the detail-page API. If the API is rate-limited, throttle and resume. If it's not exposed, render the screen as PDF and OCR the values."
5
Design the Target Schema

Fit the recovered data into the new system cleanly.

Don't replicate the old vendor's bad shape — fix it on the way out.

Example
"Split address_text into street/city/state/zip. Split notes into note_text plus note_visibility. Replace the old composite ID with a clean UUID."
6
Prove the Move

Reconcile counts and samples between source and target before cutover.

The migration is only done when the totals tie.

Example
"For every entity: source count must equal target count. For 1,000 random samples per entity: every field must match. Anything that doesn't is fixed before traffic moves."

What this looks like in practice

A simplified version of the API reverse-engineering and reconciliation pass we run before any cutover.

Python · API reverse-engineering + 3-way reconciliation# 1. Reverse-engineer the undocumented "list jobs" API
def fetch_all_jobs(session, base_url, page_size=100):
    page = 1
    while True:
        r = session.get(
            f"{base_url}/jobs",
            params={"page": page, "size": page_size, "include": "all"},
        )
        rows = r.json().get("data", [])
        if not rows:
            return
        yield from rows
        page += 1

# 2. Three-way reconcile: dashboard count vs API count vs CSV export
ui_count   = scrape_dashboard_total(session)            # 12,420
api_count  = sum(1 for _ in fetch_all_jobs(session, API))  # 12,418
csv_count  = len(pd.read_csv("vendor_export.csv"))      # 11,903

assert abs(ui_count - api_count) < 5,  "API drift vs UI"
assert csv_count >= ui_count - 50,    "CSV missing rows — do not cut over"

# 3. Re-fetch missing rows directly from the detail-page API
missing_ids = (set(api_ids) - set(csv_ids))
recovered   = [fetch_job_detail(jid) for jid in missing_ids]
print(f"Recovered {len(recovered)} jobs the CSV export silently dropped.")
Why this is the path we recommend   Every gate produces evidence. Every count that doesn't match is surfaced. Every field that doesn't map is decoded. By the time we recommend cutover, the migration has been proven on paper — not promised in a slide deck.
Every RecordRecovered
CountsReconciled
CutoverWith Proof

When this stops being optional

This becomes urgent — not optional — if any of these is true today:

The framework above isn't theoretical — it's a checklist that runs before any commitment to a new vendor. Each gate produces an artifact: an entity inventory, a reconciliation report, an enum map, a recovered-records log, a target schema validated against a JSON Schema specification, and a final tie-out. The actual extraction usually runs through the Python requests library for the API reverse-engineering pass, with bulk loads handled by AWS Database Migration Service when the volumes get large. By the time you sign anything new, you already know what's coming with you.

Most of our migrations start the same way: a single phone call where the founder describes the renewal letter and the broken export. Within 48 hours we send back a feasibility audit — what's recoverable, what's risky, and a realistic week-by-week plan. No access to your vendor required, no commitment, no pressure. The same parallel-run, never-cut-too-fast approach we use here is the spine of our framework for migrating from DigitalOcean to AWS or Azure.

Get a free migration feasibility audit

Tell us which vendor you want to leave and which entities you'd need to bring out. Within 48 hours we'll send back a clear report: what's recoverable, what's risky, what reverse-engineering the API will involve — and a realistic week-by-week plan if you decide to move.

Show me what's possible (free)
No vendor access required. No commitment. No pressure.