Bulk import users from CSV
Upload a CSV that creates many new traders in one background job, with their custom field values.
- Method
- POST
- Path
https://api.returning.ai / v1/ users/ bulk-import - Permission
- bulkImportUser
- Retries
- Not idempotent, but uploading the same file again rejects traders who already exist
When to use this
- You are moving your existing customers onto Returning.AI and need to create them all at once.
- You want each new trader's custom fields, such as funded dates or revenue, filled in on day one.
- A batch of new sign-ups arrives as a file rather than one by one.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- bulkImportUserShown in the dashboard as “Bulk Import Users”. Keys with “Bulk Update User” are also accepted.
Uploading needs bulkImportUser, shown in the dashboard as Bulk Import Users. Keys with bulkUpdateUser (Bulk Update User) are also accepted, but give new keys the import permission. Reading the custom field job back needs getBulkUpdate as well. The traders are always created in the API key's community. Keep the key on your server.
Behaviour#
The upload only queues the import. It then runs in the background, in two steps:
- Create the traders. Every row is checked first. Valid rows become traders; invalid rows are skipped with a reason, and the rest of the file still imports.
- Write the custom fields. If the file has custom field columns, one bulk update job is queued for the traders just created once step 1 finishes. It appears in List bulk update jobs as
Bulk import custom fields (...), and you can follow it like any other bulk update.
The import itself is shown in your community's API Logs, with a file of every row and its error. It does not appear in List bulk update jobs. To check it from the API, look the traders up with Get User Data, or read logType=api records in Retrieve and export community logs from the time of your upload: the import is named Bulk Import Users, with its status and progress.
- The import fails (
Errorin community logs) only when the whole file cannot be processed, for example when a required column is missing or the file cannot be read. The reason is shown on the job, such asMissing required headers - Email. - A completed import (
Complete) can still include skipped rows, for example3 users failed validation. - Custom field values go through a normal bulk update, so milestones, tiers and other rewards that use those fields react exactly as they would to a bulk update. Roles given in the file apply their role rewards.
- If your community has an active set-up-password email template, each new trader is emailed a link to set their password. Without one, no email is sent.
Request#
Headers#
bulkImportUser or bulkUpdateUser. The traders are created in this key's community.RuleBearer <API_KEY>
Body#
Send multipart/form-data with the file in field data. The first line must be a header row. Column names are not case-sensitive.
Username,Email,First Name,Last Name,Country,Join Server,Customer ID,Funded At,Challenge Revenue
sample_trader,trader@example.com,Sample,Trader,Singapore,true,<brokerCustomerId>,2026-09-18T15:50:45.414Z,1250.50
Required columns:
UsernameandEmail.- Your community's broker customer ID field, if it has one, by its display name or key. It must be filled in and unique.
- Any profile field your community makes compulsory:
First Name,Last Name,PhoneorCountry.
Optional columns: First Name, Last Name, Phone, Country, Language, Role, Tag and Join Server. A missing column is read as empty.
Username: 2 to 49 characters, letters, numbers,.and_, not starting or ending with.or_.First NameandLast Name: up to 49 characters.Phone: digits, optionally with a leading+and country code; spaces and dashes are allowed, letters are not.Country: the country's full name, such asSingapore.Language: the language name, such asEnglish.RoleandTag: comma-separated. Each role must already exist in your community.Join Server:truemakes the trader a member of your community straight away. Any other value, or no column, adds them as invited; they become a member when they set their password or join.
Custom field columns:
- Add a column for each custom field you want to fill, named by its display name or its key.
- Values follow the bulk update rules: numbers such as
1250.50,trueorfalsefor yes/no fields, and ISO 8601 for date-time fields, such as2026-09-18T15:50:45Z. A date-time can include milliseconds or an offset such as+08:00; without an offset it is read as UTC. - Number fields are written with
overwrite. Add a<column>_opcolumn withaddorsubtractto change that. - An empty cell is skipped. A row with any invalid custom value skips all of that trader's custom values, but the trader is still created.
- Columns that are not a standard column or one of your custom fields are ignored.
Duplicates:
- A row is skipped if its email, username or customer ID already belongs to a trader, or appears earlier in the same file. Emails are compared without case, so
Trader@Example.comin your file matches an existingtrader@example.com. - There is no row limit; the file can be up to 30 MB. Split a larger file and upload the parts one after another.
data.Rule.csv, max 30 MB
Eg"traders.csv"
Send the body as multipart/form-data.
curl --request POST \
--url https://api.returning.ai/v1/users/bulk-import \
--header 'Authorization: Bearer <API_KEY>' \
--form 'data=@traders.csv'
Response#
A 200 returns only a confirmation that the import is queued. It has no job ID. Check a few traders with Get User Data, and follow the Bulk import custom fields job if your file had custom field columns.
Rulesuccess
Eg"Bulk import user process is queued"
Eg{ ... }
Eg"Bulk import user process is queued. You can check in Api Log ( community settings )"
{
"status": "success",
"message": "Bulk import user process is queued",
"data": {
"message": "Bulk import user process is queued. You can check in Api Log ( community settings )"
}
}
Errors#
Errors from the API key check, such as 401 and 403, carry the code in meta.code. Other errors from this endpoint have no code, so branch on the HTTP status. Problems inside the file never cause an error here; they show up on the import in API Logs and in its row file.
Fix the request03
data, with a .csv name or a text/csv type.AUTHENTICATION_REQUIREDFix the requestAuthorization: Bearer <API_KEY> with a current community API key.API_KEY_PERMISSION_DENIEDFix the requestbulkImportUser nor bulkUpdateUser. Add Bulk Import Users in Settings > Integration > API Keys.Fix the data01
COMMUNITY_NOT_FOUNDFix the dataRetry with backoff02
500 means the import was not queued; retry with backoff. A 500 error page without JSON means the file is over 30 MB or was sent under a form field other than data; split the file or fix the upload instead.AUTHENTICATION_FAILEDRetry with backoffRetries
This endpoint is not idempotent, and it ignores an Idempotency-Key header, but uploading the same file again is safe: traders created the first time are rejected as already existing, and their custom fields are not written again. Rows that failed the first time are imported if you fix them. Over the rate limit, requests return 429; wait for the window to reset, then retry.
{
"status": "fail",
"message": "Uploaded file must be a CSV"
}