API Reference
ChangeCheck API v1 — Deterministic table diff over HTTP.
Authentication
All requests use your license key as a Bearer token:
Authorization: Bearer <your-license-key>License keys are issued on purchase. Free-tier requests (no header) are limited to 5 per day per IP.
Endpoints
| Method | Path | Description | Credits |
|---|---|---|---|
POST | /v1/diff | Compare two files | 1 |
GET | /v1/diff/health | Check auth & credits | 0 |
POST /v1/diff
Upload two files as multipart form-data. Returns a structured diff with added, removed, and modified rows.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| fileA | file | Yes | Base file (CSV, XLSX, TSV) |
| fileB | file | Yes | Changed file (CSV, XLSX, TSV) |
| key | string | No | Column name for row matching. Auto-detected if omitted. |
| normalize | string | No | finance to enable finance number normalization. Default: off. |
Quick Start
Basic comparison
curl -X POST https://changecheck.se/v1/diff \
-H "Authorization: Bearer YOUR_KEY" \
-F fileA=@before.csv \
-F fileB=@after.csvWith key column
curl -X POST https://changecheck.se/v1/diff \
-H "Authorization: Bearer YOUR_KEY" \
-F fileA=@before.csv \
-F fileB=@after.csv \
-F key=employee_idHealth check
curl https://changecheck.se/v1/diff/health \
-H "Authorization: Bearer YOUR_KEY"Response Schema
Successful responses return a summary-first JSON structure. CI pipelines can read summary only; full row-level detail is in rows.
{
"summary": {
"rows_added": 12,
"rows_removed": 3,
"cells_modified": 47,
"total_rows_file_a": 1500,
"total_rows_file_b": 1509,
"matched_by": "employee_id",
"matched_by_confidence": "high",
"has_schema_changes": true,
"has_data_changes": true
},
"schema": {
"added_columns": ["price"],
"removed_columns": [],
"renamed_columns": []
},
"rows": {
"added": [
{
"key": "EMP-1042",
"values": { "name": "Jane", "dept": "Sales" }
}
],
"removed": [
{
"key": "EMP-0017",
"values": { "name": "Bob", "dept": "HR" }
}
],
"modified": [
{
"key": "EMP-0203",
"changes": [
{ "column": "salary", "from": "50000", "to": "52000" },
{ "column": "dept", "from": "Engineering", "to": "Product" }
]
}
]
},
"meta": {
"request_id": "req_a1b2c3d4",
"credits_remaining": 12,
"engine_version": "diff-core@0.1.0",
"processing_ms": 342,
"deterministic": true
}
}Error Codes
All errors return a consistent JSON format:
{
"error": {
"code": "OUT_OF_CREDITS",
"message": "No credits remaining.",
"request_id": "req_a1b2c3d4"
}
}| Code | HTTP Status | Description |
|---|---|---|
| MISSING_FILE | 400 | fileA or fileB not provided |
| INVALID_FORMAT | 400 | File is not CSV/XLSX/TSV |
| INVALID_KEY | 400 | Specified key column not found in both files |
| FILE_EMPTY | 400 | File contains no data rows |
| UNAUTHORIZED | 401 | Missing or invalid license key |
| OUT_OF_CREDITS | 402 | No credits remaining |
| FILE_TOO_LARGE | 413 | File exceeds 4 MB upload limit |
| TOO_MANY_ROWS | 422 | File exceeds 5,000 rows |
| TOO_MANY_COLUMNS | 422 | File exceeds 50 columns |
| RATE_LIMITED | 429 | Too many requests |
| INTERNAL_ERROR | 500 | System error (credit auto-reverted) |
Limits
| Limit | Value |
|---|---|
| Max rows per file | 5,000 |
| Max columns per file | 50 |
| Max file size | 4 MB per file |
| Rate limit (authenticated) | 60 requests/min |
| Rate limit (free tier) | 5 requests/day |
| Accepted formats | .csv, .xlsx, .xls, .tsv |