Here is the shape of a Bulk API 2.0 ingest job using the REST endpoints. The flow is the same whether you call it from code or watch a tool like Data Loader do it for you.
- Create the job
Send a POST to the ingest jobs endpoint with the target object and the operation (insert, update, upsert, delete, or hardDelete). For upsert, name the external ID field. The response returns a job ID and an Open state.
- Upload the CSV data
PUT your record data to the job's batches endpoint as CSV. The header row lists field API names; each later row is one record. Keep the payload within the size limit, around 150 MB per job.
- Close the upload
PATCH the job state to UploadComplete. This signals Salesforce that the data is ready and lets the platform move the job to InProgress and start batching it internally.
- Poll for completion
Call the job info endpoint on an interval until the state reads JobComplete or Failed. Large jobs take minutes; do not poll aggressively, since each call is still an API request.
- Download the results
Retrieve the successful records and the failed records from their separate result endpoints. Parse the failed-row file, fix the errors, and resubmit only those rows.
The API name of the target sObject for the job, for example Account or a custom object like Invoice__c.
One of insert, update, upsert, delete, or hardDelete. This sets what the job does with each row in the CSV.
Required only for upsert. The external ID field used to match incoming rows to existing records so reruns update instead of duplicate.
The format of the uploaded data. For Bulk API 2.0 ingest this is CSV, and the upload itself is sent as text/csv.
- Forgetting to PATCH the job to UploadComplete leaves it in Open forever; the platform never starts processing until you close the upload.
- The CSV header must use field API names, not labels. A label like Account Name instead of Name silently maps nothing and the row fails.
- The daily record allocation runs on a rolling 24-hour window, so a large overnight load can throttle a job you start the next morning.
- Resubmitting a whole job after partial failure burns your allocation and can create duplicates; resubmit only the failed rows, ideally via upsert.