The lifecycle is: create job, upload CSV, mark upload complete, poll status, download results. Five API calls total for a basic job.
- Create the job
POST /services/data/v60.0/jobs/ingest with JSON body specifying Object, Operation, and externalIdFieldName (for upserts). Returns the job ID and the upload URL.
- Upload the CSV
PUT the CSV data to the upload URL. The data can be up to 150 million records or 100 GB; large files are streamed.
- Mark upload complete
PATCH /services/data/v60.0/jobs/ingest/{jobId} with state: UploadComplete. Signals Salesforce to start processing.
- Poll status
GET /services/data/v60.0/jobs/ingest/{jobId} every few seconds. Read the state field. Once state is JobComplete or Failed, proceed.
- Download results
GET /services/data/v60.0/jobs/ingest/{jobId}/successfulResults and .../failedResults. Parse and act on failures.
- Audit the Job record
Query the Job object via SOQL for historical job records; useful for ETL monitoring.
The target sObject for the operation.
Insert, Update, Upsert, Delete, HardDelete, or Query.
The unique alternate key for upsert matching.
The actual records to process.
Lifecycle status; the caller transitions Open to UploadComplete.
- Bulk API 2.0 processes work asynchronously. Code that needs synchronous response should use the REST API instead, not Bulk.
- Job processing time varies. Throughput depends on triggers, validation rules, and overall org load; do not assume linear scaling.
- Failed records are returned in a separate file. Successful upserts and inserts must be reconciled with the input file using row position or an upload-provided correlation field.
- Bulk API 2.0 has its own rate limits separate from the REST API. Heavy ETL workloads need to plan around them.