Skip to content

CSV bulk loader

The Data card on a database’s Overview tab has a Load CSV button. Pick a .csv file, choose a target table, and PerSQL infers column types from the first 200 rows and inserts the whole file inside one transaction.

  • Create (default) — creates the table if it doesn’t exist; fails if it does.
  • Append — inserts into an existing table. Column count and order must match the CSV.
  • Replace — drops the table (if any) and re-creates it from the CSV’s inferred schema. Destructive.

Each column’s type is picked by walking the first 200 data rows:

All non-empty values are…Type
Integer literalsINTEGER
Numeric (any finite number)REAL
Anything elseTEXT

Empty cells become NULL. If a row outside the sample window has a value that doesn’t fit (e.g. "oops" in an INTEGER column), the INSERT for that row fails and the whole transaction rolls back.

By default, the first row is treated as a header and used as the column names. Names get sanitized — non-alphanumeric chars become underscores, duplicates get a suffix, empty cells fall back to col_<n>. Uncheck First row is a header in the dialog if your CSV starts with data.

  • 10 MB file size, 100 000 rows.
  • Multipart upload only (multipart/form-data).
  • Manage permission required.
Terminal window
curl --cookie cookies.txt \
-F "file=@orders.csv" \
-F "table=orders" \
-F "mode=create" \
-F "header=true" \
https://api.persql.com/api/namespaces/acme/databases/orders/load-csv

Response:

{
"success": true,
"data": {
"table": "orders",
"rowsInserted": 12500,
"columns": [
{ "name": "id", "type": "INTEGER" },
{ "name": "customer_id", "type": "INTEGER" },
{ "name": "total", "type": "REAL" },
{ "name": "status", "type": "TEXT" }
]
}
}
  • For very large datasets, split into multiple files of <100 k rows and use mode=append for the rest.
  • Quoting follows RFC-4180 — "a,b" is one field, "" inside a quoted field is an escaped ".
  • BOM at the start of the file is stripped automatically.