- Overview
- API Resources
Upload a CSV or gzipped CSV file to ingest large volumes of data asynchronously into the v2 Supply Chain & Retail Solutions Data Ingestion API.
Bulk CSV upload lets you ingest a large dataset by uploading a single CSV file, instead of sending rows as JSON to POST /api/v2/objects/{objectName}. You upload the file, the platform scans and validates it, and the rows are loaded into the warehouse asynchronously.
Because the file is processed in the background, the API responds immediately with a requestId that you use to track progress. Behavior is the same whether your warehouse is Snowflake or Redshift.
When to use it
- Bulk CSV upload (this page) — one CSV file per table, processed server-side. Best when you have a large file and would rather hand it to the platform than batch the rows yourself.
- Standard JSON ingestion —
POSTrows as JSON. Best for small or incremental data arriving continuously. - Historical data ingestion — a client-side script for very large, multi-file historical loads that need streaming, checkpointing, and resume.
Before you start
- The target table has been rolled out (see Schema lifecycle).
- You have a Personal Access Token (see Getting Started).
- Your file is a CSV with a header row whose column names match the rolled-out schema (case-insensitive).
- You know whether your tenant is on the hub (
https://ingestion.peak.ai) or a spoke cluster (https://ingestion.<cluster-identifier>.peak.ai). Spoke tenants must use their spoke host.
Upload a file
Send a multipart/form-data POST to the upload endpoint. Replace {objectName} with the fully qualified table name as rolled out (including any prefix or suffix), and provide three form fields:
| Field | Description |
|---|---|
file | The CSV file to ingest. Exactly one file per request. |
solutionName | The solution that owns the table. |
operationType | APPEND or UPSERT (see Operation types). |
curl -X POST "https://ingestion.peak.ai/api/v2/objects/{objectName}/upload" \
-H "Authorization: $PEAK_AUTH_TOKEN" \
-F "file=@sales.csv" \
-F "solutionName=your-solution-name" \
-F "operationType=APPEND"
curl -X POST "https://ingestion.peak.ai/api/v2/objects/{objectName}/upload" \
-H "Authorization: $PEAK_AUTH_TOKEN" \
-F "file=@sales.csv" \
-F "solutionName=your-solution-name" \
-F "operationType=APPEND"
A successful request returns 202 Accepted with a requestId:
{
"requestId": "3f9a5c8e-1d2b-4a6f-9c07-8e21b4d0a7f5",
"status": "UPLOADED",
"fileName": "sales.csv",
"sizeBytes": 4718592
}
{
"requestId": "3f9a5c8e-1d2b-4a6f-9c07-8e21b4d0a7f5",
"status": "UPLOADED",
"fileName": "sales.csv",
"sizeBytes": 4718592
}
The 202 confirms the file was uploaded and accepted for processing — not that its rows have been written to the warehouse. Use the requestId to track the outcome.
Track progress
Poll the status endpoint with the requestId from the upload response:
curl "https://ingestion.peak.ai/api/v2/ingestions/{requestId}/status" \
-H "Authorization: $PEAK_AUTH_TOKEN"
curl "https://ingestion.peak.ai/api/v2/ingestions/{requestId}/status" \
-H "Authorization: $PEAK_AUTH_TOKEN"
While the file is processed, status moves through intermediate states — UPLOADED, SCANNING, LOADING, QUEUED, PROCESSING — before reaching one of three terminal states:
| Terminal status | Meaning |
|---|---|
SUCCESS | Processing finished. Check rowsIngested and rowsFailed for the outcome. |
QUARANTINED | The file failed the malware scan and was not ingested. |
FAILED | The request could not be processed; see the error field. |
A SUCCESS response reports the row counts:
{
"requestId": "3f9a5c8e-1d2b-4a6f-9c07-8e21b4d0a7f5",
"status": "SUCCESS",
"ingestionType": "BULK_UPLOAD",
"tableName": "acme_sales_v1",
"fileName": "sales.csv",
"rowsIngested": 9847,
"rowsFailed": 153
}
{
"requestId": "3f9a5c8e-1d2b-4a6f-9c07-8e21b4d0a7f5",
"status": "SUCCESS",
"ingestionType": "BULK_UPLOAD",
"tableName": "acme_sales_v1",
"fileName": "sales.csv",
"rowsIngested": 9847,
"rowsFailed": 153
}
SUCCESS means the request finished, not that every row was ingested. A file whose rows all fail validation still returns SUCCESS with rowsIngested: 0 — always check rowsFailed.
Validation and failed rows
Each row is validated against the registered schema. Valid rows are ingested even when other rows in the same file fail — one bad row does not stop the load. The status response reports rowsIngested and rowsFailed; the per-row failure reasons appear in the Data Quality Dashboard and the matching <table_name>_failed_rows table.
For the validation rules and the meaning of each failure, see Validation behavior and Error codes.
APPEND and UPSERT behave as they do for JSON ingestion. With APPEND, a row whose primary key already exists is reported as a failed row; with UPSERT, it updates the existing row. See Operation types.
File requirements
- Format — a CSV file (
.csv) or a gzip-compressed CSV (.csv.gz). - Header — a header row is required, and column names must match the rolled-out schema (case-insensitive).
- One file per request — attach exactly one file in the
filefield. - Size — up to 500 MB per file.
Malware scanning
Every uploaded file is scanned for malware before any rows are ingested. If a file is flagged, its status becomes QUARANTINED and none of its rows are loaded.
Request errors
Invalid requests are rejected immediately, before processing begins:
| Situation | Response |
|---|---|
| No file, an empty file, or more than one file attached | 400 Bad Request |
The file is not .csv or .csv.gz | 400 Bad Request |
operationType is not APPEND or UPSERT | 400 Bad Request |
| The object is not rolled out in the solution | 404 Not Found |