CSV to JSON for API Integration
Why APIs Expect JSON, Not CSV
REST APIs almost universally accept JSON request bodies. CSV is a spreadsheet format — flat, row-based, and typeless. JSON supports nested objects, arrays, and explicit types. When you need to feed spreadsheet data into an API, you must convert CSV to JSON first.
Common scenarios:
- Importing a product catalog from a spreadsheet into an e-commerce API
- Bulk-uploading user records from a CRM export
- Seeding a database through a REST endpoint
Basic CSV to JSON Conversion
Given this CSV:
name,email,role
Alice,alice@example.com,admin
Bob,bob@example.com,user
Carol,carol@example.com,user
The JSON output for API consumption:
[
{
"name": "Alice",
"email": "alice@example.com",
"role": "admin"
},
{
"name": "Bob",
"email": "bob@example.com",
"role": "user"
},
{
"name": "Carol",
"email": "carol@example.com",
"role": "user"
}
]
Most APIs accept this array-of-objects format directly or wrapped in an envelope:
{
"users": [...]
}
Type Coercion: The Silent Bug Source
CSV has no types — everything is a string. APIs often require specific types:
| CSV Value | Needed Type | Conversion |
|---|---|---|
25 | number | 25 (not "25") |
true | boolean | true (not "true") |
2024-01-15 | date string | "2024-01-15" |
(empty) | null | null (not "") |
Without type coercion, you send "age": "25" instead of "age": 25. Some APIs reject the wrong type silently — the request succeeds but the field is ignored.
Strategy: Define a schema for your conversion:
const schema = {
age: 'number',
active: 'boolean',
created_at: 'date',
notes: 'string'
};
function coerce(value, type) {
if (value === '') return null;
switch (type) {
case 'number': return Number(value);
case 'boolean': return value === 'true';
case 'date': return value; // ISO format assumed
default: return value;
}
}
Nested Object Construction
APIs often expect nested structures. A flat CSV row:
name,street,city,zip,role_name,role_level
Alice,123 Main St,Portland,97201,admin,5
Needs to become:
{
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "Portland",
"zip": "97201"
},
"role": {
"name": "admin",
"level": 5
}
}
Two approaches:
| Approach | CSV Header | Conversion |
|---|---|---|
| Dot notation | address.street | Parse dots into nested objects |
| Prefix convention | addr_street | Map prefixes to parent objects |
Dot notation is cleaner for tooling:
function nest(headers, values) {
const obj = {};
headers.forEach((header, i) => {
const parts = header.split('.');
let target = obj;
parts.forEach((part, j) => {
if (j === parts.length - 1) {
target[part] = values[i];
} else {
target[part] ??= {};
target = target[part];
}
});
});
return obj;
}
Batch Size and Rate Limits
APIs impose rate limits. Sending 10,000 records in one request usually fails.
| Strategy | How |
|---|---|
| Batch splitting | Send 100-500 records per request |
| Retry with backoff | Wait and retry on 429 responses |
| Parallel with limit | Use 3-5 concurrent connections |
async function batchUpload(records, batchSize = 200) {
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
const response = await fetch('/api/users/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ users: batch })
});
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 5;
await new Promise(r => setTimeout(r, retryAfter * 1000));
i -= batchSize; // retry this batch
}
}
}
Validation Before Sending
Never send converted data straight to the API. Validate first:
- Required fields: Ensure columns the API demands are not empty
- Format checks: Email addresses, phone numbers, date formats
- Enum values: Confirm role names match allowed values
- Uniqueness: Check for duplicate emails or IDs
A single invalid record can reject an entire batch on strict APIs.
Common Pitfalls
| Pitfall | Symptom | Fix |
|---|---|---|
| String numbers | API ignores "age": "25" | Coerce to Number |
| Empty strings for null | API stores empty string instead of null | Map "" to null |
| Encoding mismatch | Special characters arrive garbled | Convert CSV to UTF-8 first |
| Duplicate headers | Later column overwrites earlier | Prefix duplicate column names |
| Missing wrapping envelope | API returns 400 Bad Request | Wrap array in { "data": [...] } |
Key Takeaways
- REST APIs expect JSON — CSV must be converted before integration
- Type coercion prevents silent failures where string values replace expected numbers or booleans
- Dot-notation headers simplify nested object construction from flat CSV rows
- Batch uploads with retry logic handle rate limits and large datasets
- Validate every record before sending — one bad row can reject the entire batch
Try It Yourself
Convert your CSV files to JSON instantly with our CSV to JSON Converter. Paste CSV, choose options, and get API-ready JSON in seconds.