Step 5: Bulk Upsert

This Bulk API 2.0 example guides you through creating a job, uploading data for the job, notifying Salesforce servers that your upload(s) are complete, checking the status, and retrieving the results. Some of the records exist (update), and some are new records (insert).
  1. Confirm that your object is using an external ID field.

    Upserting records requires an external ID field on the object involved in the job. Bulk API 2.0 uses the external ID field to determine whether a record is used to update an existing record or create a record.

    This example assumes that the external ID field customExtIdField__c has been added to the Account object.
    To add this custom field in your org with Object Manager, use these properties.
    • Data Type—text
    • Field Label—customExtIdField
    • Select External ID
    For more information, see Custom Fields in Salesforce Help.
  2. Create a CSV file containing the records that you want to upsert.

    Save all files in this example in your terminal’s current working directory.

    Note

    The first row of the CSV file lists the field names for the object that you’re working with. Each subsequent row corresponds to a record that you want to insert.

    One column in the CSV file must correspond to the external ID field customExtIdField__c.

    For information on preparing CSV files, such as delimiter options and valid date and time formats, see Bulk API 2.0 Ingest.

    For this example, copy this information into a file named accountupsert.csv.

    1customExtIdField__c,name,NumberOfEmployees
    2123,GenePoint,800
    3234,"United Oil & Gas, UK",1467
    4345,"United Oil & Gas, Singapore",348
    5456,Edge Communications,10045
    6567,Burlington Textiles Corp of America,5876
    7678,Dickenson plc,67
    8789,Grand Hotels & Resorts Ltd,409
    9890,Express Logistics and Transport,243
    10901,University of Arizona,9506
    111350,United Oil & Gas Corp.,5467
    121579,sForce,40000
    132690,University of The Terrific,1257
  3. Create a job that includes the external ID field.

    Copy this information into a file named newupsertjob.json.

    1{
    2    "object" : "Account",
    3    "externalIdFieldName" : "customExtIdField__c",
    4    "contentType" : "CSV",
    5    "operation" : "upsert",
    6    "lineEnding" : "LF"
    7}

    URI

    1/services/data/v66.0/jobs/ingest/

    Example for creating a bulk upsert job

    1curl https://MyDomainName.my.salesforce.com/services/data/v66.0/jobs/ingest/ -H 'Authorization: Bearer 00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLEzmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1EXAMPLEDUkWe6H34r1AAwOR8B8fLEz6nEXAMPLE' -H "Content-Type: application/json" -H "X-PrettyPrint:1" -d @newupsertjob.json -X POST

    Example response body

    The response includes the job ID, with a job state of Open. Use the job ID and the URL in the contentUrl field in the next step when you upload your data.

    1{
    2  "id" : "7476gEXAMPLE4X2ZWO",
    3  "operation" : "upsert",
    4  "object" : "Account",
    5  "createdById" : "0055fEXAMPLEtG4AAM",
    6  "createdDate" : "2022-01-02T21:57:03.000+0000",
    7  "systemModstamp" : "2022-01-02T21:57:03.000+0000",
    8  "state" : "Open",
    9  "externalIdFieldName" : "customExtIdField__c",
    10  "concurrencyMode" : "Parallel",
    11  "contentType" : "CSV",
    12  "apiVersion" : 66.0,
    13  "contentUrl" : "services/data/66.0/jobs/ingest/7476gEXAMPLE4X2ZWO/batches",
    14  "lineEnding" : "LF",
    15  "columnDelimiter" : "COMMA"
    16}
  4. Upload the CSV data file that you created.

    URI

    For convenience, use the URI in the contentUrl field of the response from step 1. The URI is similar to:

    1/services/data/v66.0/jobs/ingest/jobId/batches/

    Example for uploading data

    1curl https://MyDomainName.my.salesforce.com/services/data/v66.0/jobs/ingest/7476gEXAMPLE4X2ZWO/batches/ -H 'Authorization: Bearer 00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLEzmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1EXAMPLEDUkWe6H34r1AAwOR8B8fLEz6nEXAMPLE' -H "Content-Type: text/csv" --data-binary @accountupsert.csv -X PUT

    Example response body

    No response body is returned.

  5. Set state to UploadComplete.

    After you’re done submitting data, notify Salesforce servers that the upload of job data is complete and is ready for processing.

    Create a JSON file named upload_complete.json with the contents:

    1{"state":"UploadComplete"}

    URI

    /services/data/v66.0/jobs/ingest/jobId/

    Example of setting state to UploadComplete

    1curl https://MyDomainName.my.salesforce.com/services/data/v66.0/jobs/ingest/7476gEXAMPLE4X2ZWO/ -H 'Authorization: Bearer 00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLEzmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1EXAMPLEDUkWe6H34r1AAwOR8B8fLEz6nEXAMPLE' -H "Content-Type: application/json" -H "X-PrettyPrint:1" -d @upload_complete.json -X PATCH

    Example response body

    1{
    2  "id" : "7476gEXAMPLE4X2ZWO",
    3  "operation" : "upsert",
    4  "object" : "Account",
    5  "createdById" : "0055fEXAMPLEtG4AAM",
    6  "createdDate" : "2022-01-02T21:28:22.000+0000",
    7  "systemModstamp" : "2022-01-02T21:28:22.000+0000",
    8  "state" : "UploadComplete",
    9  "externalIdFieldName" : "customExtIdField__c",
    10  "concurrencyMode" : "Parallel",
    11  "contentType" : "CSV",
    12  "apiVersion" : 66.0
    13}
  6. Get successful results.

    After a job is in the JobComplete or Failed state, you can get details about which job data records were successfully processed.

    URI

    1/services/data/v66.0/jobs/ingest/jobId/successfulResults/

    Example of getting successful results

    1curl https://MyDomainName.my.salesforce.com/services/data/v66.0/jobs/ingest/7476gEXAMPLE4X2ZWO/successfulResults/ -H 'Authorization: Bearer 00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLEzmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1EXAMPLEDUkWe6H34r1AAwOR8B8fLEz6nEXAMPLE' -H "Content-Type: application/json" -H "Accept: text/csv" -H "X-PrettyPrint:1" -X GET

    The response contains CSV formatted data, with each row containing a record ID of successfully processed records.

    Example response body

    1"sf__Id","sf__Created",customExtIdField__c,name,NumberOfEmployees
    2"0018c00002DJIpJAAX","true","123","GenePoint","800"
    3"0018c00002DJIpKAAX","true","234","United Oil & Gas, UK","1467"
    4"0018c00002DJIpLAAX","true","345","United Oil & Gas, Singapore","348"
    5"0018c00002DJIpMAAX","true","456","Edge Communications","10045"
    6"0018c00002DJIpNAAX","true","567","Burlington Textiles Corp of America","5876"
    7"0018c00002DJIpOAAX","true","678","Dickenson plc","67"
    8"0018c00002DJIpPAAX","true","789","Grand Hotels & Resorts Ltd","409"
    9"0018c00002DJIpQAAX","true","890","Express Logistics and Transport","243"
    10"0018c00002DJIpRAAX","true","901","University of Arizona","9506"
    11"0018c00002DJIpSAAX","true","1350","United Oil & Gas Corp.","5467"
    12"0018c00002DJIpTAAX","true","1579","sForce","40000"
    13"0018c00002DJIpUAAX","true","2690","University of The Terrific","1257"

    To get details about records that encountered an error, use the failedResults resource. To make sure that you’re looking at the complete result set, use the unprocessedRecords resource. See Get Job Unprocessed Record Results.