The main difference is timing
REST errors usually arrive with the request that caused them. A create may return a record ID, or an error describing a missing field, invalid value, permission problem, or validation rule. A client can show the result to the caller and decide whether to correct the payload.
Bulk API 2.0 separates submission from processing. Creating a job or uploading data can succeed while individual rows later fail. A job can also finish with a mix of successful and failed records. The job state is evidence about processing, not a guarantee that every row was accepted.
REST response handling
A REST create request has a synchronous shape:
On success, store the returned record ID and response metadata. On failure, preserve the status, error code, message, fields, endpoint, and a redacted payload. A 400 validation error should not be retried unchanged. A 401 or 403 needs credential or permission review. A timeout needs reconciliation because the server may have completed the write before the client stopped waiting.
Composite REST requests add another layer. Depending on the composite resource and `allOrNone` behavior, one subrequest can fail while others succeed or the entire transaction can roll back. Parse each subresponse; do not declare the whole operation successful from the outer HTTP status alone.
{ "LastName": "Nguyen", "Email": "person@example.com" }
Bulk job handling
For Bulk API 2.0 ingest, create a job for the object and operation, upload CSV data, close the job, and poll until the documented terminal state. Keep the job ID, object, operation, external-ID field, row count, and source file checksum. Download failed results and map each row back to the source using an external key or stable row identifier.
Bulk query jobs also complete asynchronously. A successful query job means Salesforce produced a result, not that your downloader has safely stored every page. Check result retrieval, byte ranges where applicable, row counts, and checkpoint behavior.
Salesforce describes Bulk APIs as asynchronous tools for large datasets. That makes queue state and result retrieval part of error handling, not optional monitoring.
Partial failures are first-class results
A bulk load can contain valid and invalid rows. Separate the result into succeeded, failed, and unresolved categories. Failed rows may contain required-field, duplicate, validation-rule, picklist, foreign-key, or permission errors. Fix the mapping or source data, then replay only the corrected rows.
Never rerun the entire file automatically after receiving a partial failure. That can duplicate successful records. Use upsert with a stable external ID where the business model supports it, and keep a durable import ledger.
Retry policy by failure class
| Failure | REST action | Bulk action | |---|---|---| | Invalid field/value | Fix payload | Correct failed rows | | 401/403 | Renew or repair access | Stop job and repair connection | | 429/limit | Backoff or pause | Throttle new jobs and resume safely | | Timeout | Reconcile by ID/key | Check job state before resubmitting | | 5xx/transient | Bounded retry | Poll/retry upload according to policy | | Partial row failure | Retry corrected request | Replay failed rows only |
The same HTTP status can mean different operational work depending on whether it occurred during REST execution, job creation, data upload, polling, or result download.
Reconciliation after uncertainty
If a REST client times out after a create, search by an idempotency key or external ID before creating again. If a Bulk upload connection breaks, query the job state and inspect result counts before submitting a replacement job. Record the last confirmed checkpoint, not merely the last request sent.
Compare source count, submitted count, successful count, failed count, and unresolved count. For important objects, sample records by external ID and verify field values after the job. Keep the original error file for audit and support.
Separate transport failure from data failure in the ledger. A broken upload connection means the client may not know whether Salesforce received the bytes; a row-level validation error means Salesforce did receive and evaluate that row. These cases need different recovery actions. Mark a job as unknown until its state and result counts are confirmed, and require an operator or reconciliation query before submitting a replacement file.
QA checklist
Test REST success, validation error, permission error, timeout after write, composite partial failure, and bounded retry. Test Bulk job creation, upload failure, processing failure, partial row errors, polling timeout, result download interruption, duplicate replay prevention, and resume from checkpoint. Confirm logs never contain access tokens or full personal data.
The same principle applies to a job that reaches a terminal state that sounds positive. Always retrieve the documented result artifacts and compare them with the source ledger. A result file is part of the business outcome, not merely a developer diagnostic. For operational reporting, retain the job state history as well as the final file. The timeline shows whether a failure occurred during submission, processing, polling, or retrieval, which helps separate Salesforce validation from a network or worker failure.
For operational reporting, retain the job state history as well as the final result file. The timeline shows whether a failure occurred during submission, processing, polling, or retrieval. This helps separate Salesforce validation from a network or worker failure and gives support a precise point to investigate when several imports overlap.
The trap: treating a Bulk job’s successful submission as proof that every record succeeded.
Use one ledger for REST and Bulk outcomes
Normalize both paths into source key, operation, submitted time, Salesforce ID, status, error class, retry count, and reconciliation state. REST can fill the outcome immediately; Bulk fills it after result retrieval. A shared ledger lets support see whether a row is pending, succeeded, failed, or unknown without confusing a job-level success with a row-level success.
Where these facts come from