---
title: "Create Bulk Purchase"
method: POST
path: "/bulk/purchase"
tags: ["Bulk Purchases"]
---

# Create Bulk Purchase

`POST /bulk/purchase`

## Request

- Base URL: `https://api.wistia.com/modern`
- URL: `https://api.wistia.com/modern/bulk/purchase`
- Auth: HTTP bearer

Submits either an `actions` array of up to 1000 orders or one `job` that can
resolve to up to 5000 media. Orders are placed asynchronously. Returns a
background job status whose Show endpoint reports aggregate progress and
per-order results.

Orders in the batch can incur charges, so a saved credit card is required.
Supported resource types are `captions` (Wistia-generated English captions),
`localization` (a dubbed, language-specific version of a media),
`extended_audio_description`, and `text_translation` (the media's transcript
translated into another language, audio untouched). Each order's `id` is the
hashed ID of the media to order for.

What an order costs depends on the account, not on this endpoint. Automated
captions are included at no cost on plans that provide them and billed at
the account's configured per-minute rate otherwise; human-reviewed captions
bill per minute at the account's standard or rush rate; localizations bill
per minute once the account's free-dub allowance is used up; text
translations bill as an overage once the account's included translation
minutes are used up. Check the account's plan and billing settings for its
actual rates.

Orders are priced and placed individually: failures -- an ineligible media,
a language that already has a localization, an account not entitled to buy
-- are reported per order and do not stop the rest of the batch. Pricing and
eligibility match the equivalent single-media endpoints exactly.

Use the Create Bulk Actions endpoint for create, update, and delete work; it
does not accept `purchase`, and this endpoint accepts nothing else.


## Requires api token with one of the following permissions
```
Read, update & delete anything
```

Tokens with the "Act with a team member's permissions" permission
(`all:delegate_to_contact_permissions` scope) can also be used. Requests
made with such a token are authorized using the permissions of the
contact assigned to the token.

[Expiring access tokens](https://docs.wistia.com/reference/post_expiring-token)
with authorizations cannot use this endpoint: each action is authorized
later, in a background job, as the persisted contact that submitted it,
and a token's authorizations are not carried into that job. Such requests
are forbidden.

## Headers

- `X-Wistia-API-Version` string, required

## Request body

- object: A bulk purchase request, in one of two shapes. Send `actions` to name each media individually, and `job` to order for every media under a parent. Provide exactly one of them -- a request carrying both is rejected, and so is a request carrying neither.
  - `actions` object[]: The orders to place, one per media. Maximum 1000 per request, and the request body must stay under 2 MB -- whichever limit is reached first. An oversized body is rejected with a `413` and no order in it is placed. Every order is priced and placed independently: one failing (an ineligible media, an account without a saved card, a language that already has a localization) does not stop the rest of the batch. Use `job` instead to order for a whole folder, channel, or account.
    - `operation` 'purchase', required: Always `purchase`. This endpoint places orders only; to create, update, or delete records in bulk use the Create Bulk Actions endpoint, which does not accept `purchase`.
    - `resource_type` 'captions' | 'localization' | 'extended_audio_description' | 'text_translation', required: What to order for the media. `captions` orders Wistia-generated English captions -- computer-generated or human-reviewed. `localization` orders a dubbed, language-specific version of the media. `extended_audio_description` orders an extended audio description track. `text_translation` translates the media's existing transcript into another language, leaving the audio alone.
    - `id` string, required: The hashed ID of the media to order for. Always the media's own ID: what the order produces does not exist yet.
    - `payload` object: Order options. The accepted fields depend on the resource type and match the corresponding single-media endpoint's request body. Omit it to take every default. `captions` accepts `automated` (order computer-generated captions instead of human-reviewed ones), `rush` (one business day turnaround instead of four, human-reviewed only, at a higher per-minute rate), and `automatically_enable` (show the captions on the video as soon as they are ready). Each is treated as `false` when omitted or unrecognized. What each option costs depends on the account's plan and billing settings. `localization` requires `output_language`, a 3-character IETF language code, and accepts `auto_enable` (default `true`). `extended_audio_description` accepts `enabled` (default `true`), `ai_enabled` (default `true`), `ietf_language_tag` (default `eng`), and `order_instructions`. `text_translation` requires `target_language` and accepts `source_language` (which transcript to translate from, defaulting to the media's own language). Use the bibliographic ISO 639-2 form or a supported regional or script IETF tag for either value.
  - `job` object: One order placed for many media, named by a parent (`scope`) or listed explicitly (`ids`), so ordering captions for a folder of 47 videos takes one job rather than 47 orders. A `scope` resolves to exactly what List Media returns for that parent, including media in the folder's subfolders and **archived** media, and to at most 5000 media -- beyond that the job is rejected rather than truncated. The job attempts one order for every media it resolves to. Ineligible media fail individually without placing an order; successful orders are metered and may incur charges according to the account's plan. Confirm the scope and potential cost with the customer before submitting. Not available to external contacts.
    - `operation` 'purchase', required: Always `purchase`.
    - `resource_type` 'captions' | 'localization' | 'extended_audio_description' | 'text_translation', required: What to order for the media. `captions` orders Wistia-generated English captions -- computer-generated or human-reviewed. `localization` orders a dubbed, language-specific version of the media. `extended_audio_description` orders an extended audio description track. `text_translation` translates the media's existing transcript into another language, leaving the audio alone.
    - `scope` object: The parent whose media the order applies to. An order always addresses the media, so the valid parent types are the same for every resource type here.
      - `type` 'account' | 'folder' | 'subfolder' | 'channel', required: The kind of parent `id` names. Required, because a hashed ID does not say what it belongs to -- the same value could name a folder or a channel. Use `account` to order for every media in the account.
      - `id` string: The parent's hashed ID. Required for every scope type except `account`.
    - `ids` string[]: The media to order for, named explicitly. Use this instead of `scope` when the media do not share a parent. Give either `scope` or `ids`, never both.
    - `payload` object: Order options applied to every matching media, in the same shape a single order's payload takes for this resource type.

## Response `202`

Bulk purchase accepted and queued for processing

- object
  - `message` string, required: A confirmation message that the background job has been queued.
  - `background_job_status` object, required: A background job keeps track of the progress of an asynchronous task, e.g bulk archiving media, translating media, etc.
    - `id` integer, required: The ID of the background job that's been queued for the request.
    - `hashed_id` string, required: The unguessable hashed ID of the background job. Prefer this over the numeric ID when polling for status.
    - `status` 'queued' | 'started' | 'finished' | 'failed', required: The status of the background job that's been queued for the request.

## Other responses

- `400`: Bad request
- `401`: Unauthorized, invalid or missing token
- `403`: Forbidden, token is valid but account does not have access to feature
- `500`: Internal server error

## Changes

- **2026-11-03** `b86cb2a7b420`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/wistia/apis/data-api/changes/bulk/purchase/post.md)

---

[API](https://skmtc.dev/wistia/apis/data-api.md) - [All operations](https://skmtc.dev/wistia/apis/data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/wistia/apis/data-api/revisions/b86cb2a7b420?raw)
