# Best practices

Prepare, test, and troubleshoot batch jobs to avoid common errors.

## Use validation for critical operations

Keep `skip_validation` set to `false` (the default) for operations where partial processing would cause issues. Validation ensures your entire file is well-formed before Stripe executes any requests.

Set `skip_validation` to `true` when you’ve already validated your input data and want faster job startup, or when processing partial results is acceptable.

## Split large workloads

Each batch job accepts an input file of up to 2 million lines and up to 5 GB. Split your workload by line count, which is easier to control than file size, and generate input files of at most 2 million lines each. If your lines are large, also confirm that each file is 5 GB or smaller.

Submit the batch jobs sequentially, and wait for one job to finish before you create the next. Running several large batch jobs concurrently might be slower and less reliable than running the same work as sequential jobs.

## Test with a small batch first

Validation can’t catch errors that depend on the state of the objects you reference, such as a subscription that isn’t active. Before you submit a large job, run a batch job with a few lines and check the results file for non-`200` statuses. A test batch catches these errors before they affect your full workload.

## Verify resource state before batching

Confirm that all target resources are in the required state before submitting a batch. For example, subscriptions must be active before they can be migrated with `/v1/subscriptions/:id/migrate`. Batch jobs execute the target operation directly and don’t change resource state as a prerequisite.

## Handle errors in results

Always check the `status` field in each result line. Individual requests within a successful batch can still fail (for example, because of insufficient funds or invalid parameters). Build your integration to filter the results file for non-`200` statuses, then fix the failed requests and include them in a new batch job.

## Prepare your file before creating the job

The upload URL expires 5 minutes after batch job creation. Generate and validate your input file before calling the create endpoint. If you need to prepare data dynamically, complete all data retrieval and file generation first, then create the batch job and upload immediately.

## Common errors

### Upload URL expired

If you don’t upload the input file before the `expires_at` timestamp (5 minutes after job creation), the batch job transitions to `upload_timeout` status. Create a new batch job and upload the file within 5 minutes. Generate your input file before creating the batch job to avoid this.

### Invalid resource state

Individual requests can fail if the target resource isn’t in the expected state. For example, when using `/v1/subscriptions/:id/migrate`:

- **Subscription isn’t active**: The subscription must have an `active` status before you can migrate it. Canceled or incomplete subscriptions return a `400` error with `resource_invalid_state`.
- **Subscription already migrated**: Attempting to migrate a subscription that has already been migrated returns an error.

These per-request errors appear in the results file with a non-`200` status code. The batch job itself still completes successfully.

### Path parameter mismatches

The keys in `path_params` must exactly match the placeholders in the endpoint path. For example, if your endpoint path is `/v1/subscriptions/:id/migrate`, your `path_params` must use `{"id": "sub_..."}`. A mismatch between the placeholder name and the key causes a validation error or a `400` status in the results file.

A mismatch returns a `400` error for that line, with a message that names the unexpected key and the expected one, such as `Received unknown path_params key "id". Expected: "coupon".` For example, the endpoint path `/v1/coupons/:coupon` requires `{"coupon": "..."}`, not `{"id": "..."}`.

### Invalid API version

If a line returns `Invalid API version.`, set `api_version` to a [valid version string](https://docs.stripe.com/batch-api/create.md#api-versions), or omit the property to use the job’s version. Empty strings, `null`, and aliases such as `latest` aren’t accepted. An invalid value doesn’t fall back to the job’s version.

### Invalid endpoint

The `endpoint.path` parameter must match a [supported endpoint](https://docs.stripe.com/batch-api/supported-endpoints.md) and `endpoint.http_method`. You can name path placeholders however you like, for example `/v1/customers/:customer` instead of `/v1/customers/:id`, as long as the keys in `path_params` use the same names. If the path doesn’t match a supported endpoint, batch job creation fails with an invalid endpoint error.

### Upload content type

The upload `PUT` request must use `Content-Type: application/jsonlines`. The upload URL rejects other content types, including `application/jsonl`.

### File format errors

Stripe detects these format errors asynchronously after the upload, not when you send the `PUT` request. A `200` response to the `PUT` doesn’t indicate the file will pass validation, so poll the job status to detect `validation_failed`.

When `skip_validation` is `false`, these errors cause the entire batch to fail with `validation_failed` status:

- Lines that aren’t complete, valid JSON objects, such as pretty-printed JSON that spans multiple lines
- Missing `id` field on any line
- Duplicate `id` values across lines
- IDs containing characters outside `A-Za-z0-9_-`

When `skip_validation` is `true`, file-level format errors can cause individual lines to fail instead of blocking the entire batch.

### Job processing timeout

Batch jobs that run longer than 7 days transition to `timeout` status. Partial results from requests that completed before the timeout are available in the results file.
