Bundles

Create Bulk - Multiple Bundles

Create multiple TokPortal bundles in one API request. Bulk creation supports one country, one or more platforms, optional video slots, and top-level Advanced Niche Warming options.

Create Bulk

Create multiple bundles in one request.

POST /bundles/bulk

The bulk endpoint creates accounts_count x platforms.length bundles for one country. Each bundle produces exactly one account, so the batch size is the account count. The successful POST is the paid creation checkout: it creates every bundle and debits credits atomically for the whole batch. Publication happens later and never repeats the setup charge. The complete batch is checked against the client workspace's rolling capacity first; Team activity is included. A batch over capacity is rejected in full before any debit.

If GET /credit-costs returns contract_bundle_allowance, qualifying slots are allocated in deterministic bundle-ID order. A batch that crosses the remaining limit receives the contract price only for the qualifying bundles still covered. Any failure rolls back the whole batch, including all debits and allowance consumption.

Each eligible account delivered from the batch receives its own single TokPortal Coverage record. The production migration records the account cutoff. Every TikTok or Instagram saved account that already exists at that instant remains permanently grandfathered; a saved account created after it is Coverage-eligible unless its creation response marks it contractually exempt. Coverage belongs to the resulting saved account and is not multiplied by later bundles created for that account.

Request Body

All fields are top-level. There is no options wrapper and no metadata field in the public contract.

FieldTypeRequiredDefaultDescription
platformsstring[]Yes-One or more platforms. Public values: tiktok, instagram — those are the only orderable platforms. accounts_count accounts are created on each listed platform. youtube is rejected with YOUTUBE_DELAYED even though GET /credit-costs lists a youtube_account_creation price.
countrystringYes-TokPortal country code or supported alias. Use GET /countries to list countries available for new account creation. Disabled countries and countries restricted to existing-account videos_only work are excluded.
accounts_countintegerYes-Number of accounts to create per platform. Min 1, max 100.
upload_accounts_countintegerNo0How many accounts per platform should receive video slots. Must be <= accounts_count. The rest are account_only bundles.
videos_per_accountintegerNo0Number of video slots for each account that receives videos. Max 500.
wants_advanced_warmingbooleanNofalseEnable Advanced Niche Warming on every account of the batch. The standard rate is 5 credits per niche target per account — per target, never per day — with a 15-credit minimum per account. A workspace-specific contract_bundle_allowance can return another effective rate; GET /credit-costs is the authority. TikTok and Instagram only. Requires advanced_warming_terms_count or advanced_warming_terms, and is mandatory whenever either of them is sent — it is never inferred, and a batch that omits it is rejected in full. See the Advanced Niche Warming guide.
advanced_warming_terms_countintegerNo-Number of niche targets purchased per account (3-30, multiples of 3). Requires wants_advanced_warming: true in the same body. Recommended flow: buy the count now, then write each bundle's targets via PUT /bundles/{id}/warming-terms. Do not send it alongside advanced_warming_terms unless it equals that list's length. Unconfigured purchases are auto-refunded after 14 days.
advanced_warming_termsstring[]No-Alternative: the same explicit targets applied to every account of the batch (3-30 terms, count multiple of 3, each 2-50 characters, all distinct). Requires wants_advanced_warming: true in the same body.
wants_niche_warmingbooleanNofalse⚠️ Deprecated. Legacy — kept only for accounts being re-created after a refund; use Advanced Niche Warming (wants_advanced_warming).
wants_deep_warmingbooleanNofalse⚠️ Deprecated. Legacy — kept only for accounts being re-created after a refund; use Advanced Niche Warming (wants_advanced_warming).
wants_moderationbooleanNofalse⚠️ Deprecated — ignored. Was the community-management add-on; replaced by the Comments API.
niche_warming_instructionsstringNo-⚠️ Deprecated. Legacy — kept only for accounts being re-created after a refund; use Advanced Niche Warming (wants_advanced_warming).
moderation_noticestringNo-⚠️ Deprecated — ignored. Use the Comments API.
auto_finalize_videosbooleanNotrueDelivered videos are approved automatically and count as final. Set it to false to review each delivery yourself — approve it or request corrections — before it is finalized. Either way, this does not disable the automatic finalization that happens about 72 hours after a video enters in_review. Changeable at any time via Update Bundle.
external_refstringNo-Optional batch-level correlation reference. Max 200 characters. The same value is copied to every resulting bundle, so it is not a unique per-bundle ID and is not a retry key.

How It Works

Example:

{
  "accounts_count": 10,
  "upload_accounts_count": 3,
  "platforms": ["tiktok", "instagram"],
  "country": "US",
  "videos_per_account": 5
}

Result:

Platformaccount_and_videosaccount_onlyTotal bundles
TikTok3710
Instagram3710
Total61420

Total video slots: 3 upload accounts x 2 platforms x 5 videos = 30.

Use each returned bundle id as the canonical per-bundle identifier. If you provide external_ref, treat it as a shared label for the whole batch. For exact retry/replay of the bulk checkout, send one stable Idempotency-Key for that logical request; never derive retry safety from external_ref.

Example

curl -X POST https://app.tokportal.com/api/ext/bundles/bulk \
  -H "X-API-Key: sk_xxx" \
  -H "Idempotency-Key: bulk-q2-launch-us-v1" \
  -H "Content-Type: application/json" \
  -d '{
    "accounts_count": 5,
    "upload_accounts_count": 2,
    "platforms": ["tiktok", "instagram"],
    "country": "US",
    "videos_per_account": 3,
    "wants_advanced_warming": true,
    "advanced_warming_terms_count": 3,
    "external_ref": "q2-launch-us"
  }'

wants_advanced_warming: true must travel with the count. Sending advanced_warming_terms_count alone rejects the whole batch with ADVANCED_WARMING_FLAG_REQUIRED, and nothing is charged.

Response

This example uses the standard current tariff. Four bundles include three video slots and six are account-only; every bundle also carries the 3 Advanced Niche Warming targets requested above, at 5 credits per target per account.

{
  "data": {
    "bundles_created": 10,
    "bundles": [
      {
        "id": "bnd_001",
        "platform": "tiktok",
        "bundle_type": "account_and_videos"
      },
      {
        "id": "bnd_002",
        "platform": "tiktok",
        "bundle_type": "account_only"
      }
    ],
    "review_mode": "auto",
    "review_mode_notice": "auto_finalize_videos defaulted to true: delivered videos are approved automatically and count as final. Send auto_finalize_videos: false (or PATCH each bundle) to review videos and request corrections before they are finalized.",
    "summary": {
      "platforms": ["tiktok", "instagram"],
      "accounts_per_platform": 5,
      "accounts_with_videos": 2,
      "accounts_without_videos": 3,
      "videos_per_account": 3
    }
  },
  "credits_charged": 494,
  "credits_remaining": 9506,
  "cost_breakdown": {
    "per_account_with_videos": {
      "account_creation": 32,
      "video_slots": 6,
      "edit_slots": 0,
      "advanced_warming": 15,
      "moderation": 0,
      "total": 53
    },
    "per_account_only": {
      "account_creation": 32,
      "video_slots": 0,
      "edit_slots": 0,
      "advanced_warming": 15,
      "moderation": 0,
      "total": 47
    },
    "accounts_with_videos": 2,
    "accounts_without_videos": 3,
    "platforms_count": 2,
    "total": 494
  }
}

The sample is shortened. The real response includes every created bundle and the server-calculated credit breakdown.

review_mode says how delivered videos are settled for every bundle in the batch: auto (the default) approves each delivery on arrival, manual holds it for your review. review_mode_notice appears only when you omitted auto_finalize_videos — it states that the default was applied and how to opt out, and it disappears as soon as you send the field explicitly.

Validation Rules

  • upload_accounts_count cannot exceed accounts_count.
  • If videos_per_account is greater than 0, set upload_accounts_count greater than 0 to create upload-capable bundles.
  • wants_advanced_warming: true is mandatory whenever advanced_warming_terms or advanced_warming_terms_count is present. TokPortal never infers it: targets sent without the flag (absent or explicitly false) reject the entire batch with ADVANCED_WARMING_FLAG_REQUIRED, and nothing is charged.
  • wants_advanced_warming requires either advanced_warming_terms_count or advanced_warming_terms (3-30 targets, multiples of 3), otherwise ADVANCED_WARMING_TERMS. Every platform of the batch must be TikTok or Instagram (ADVANCED_WARMING_PLATFORM). The standard minimum warming charge is 15 credits per account; use the active contract_bundle_allowance when present.
  • Every term in advanced_warming_terms must survive cleanup intact. Entries are trimmed and compared case-insensitively; anything dropped as a duplicate or for being outside 2-50 characters fails the batch with ADVANCED_WARMING_TERMS_REJECTED and details.dropped_terms, rather than delivering and billing fewer targets per account than requested.
  • If you send both advanced_warming_terms and advanced_warming_terms_count, they must agree. A disagreement fails with ADVANCED_WARMING_COUNT_MISMATCH; neither quantity silently wins.
  • platforms must contain only tiktok and/or instagram. youtube is rejected with YOUTUBE_DELAYED.
  • country must be enabled for your organization.
  • An unrecognised field in the body — including inside a batch item — is rejected with UNKNOWN_FIELD. This is a full-replace write, so a misspelled name is refused rather than dropped; see the note on the account configuration page.

NOTE: Legacy warming flags wants_niche_warming, wants_deep_warming and niche_warming_instructions are kept only for accounts being re-created after a refund. Their behaviour is unchanged: wants_niche_warming and wants_advanced_warming remain mutually exclusive (WARMING_CONFLICT), niche_warming_instructions is still required when wants_niche_warming is true, and wants_deep_warming: true on a new order is still rejected with DEEP_WARMING_DEPRECATED. New integrations should use Advanced Niche Warming.

Countries

The endpoint accepts one country per request. To create bundles in several countries, loop over country codes and call POST /bundles/bulk once per country.

Use:

curl -X GET https://app.tokportal.com/api/ext/countries \
  -H "X-API-Key: sk_xxx"

Use only the response's data list for bulk creation. The separate videos_only_countries list applies exclusively to video orders on existing accounts and is not valid for this endpoint.

Error Responses

StatusCodeDescription
400INVALID_BODYInvalid request body or unsupported field.
400UNKNOWN_FIELDThe body carries a field TokPortal does not recognise, at the top level or inside a batch item. This write is a full replace, so an unrecognised name is refused instead of dropped. Read details.unknown_fields and details.did_you_mean.
400INVALID_COUNTRYCountry is not enabled.
400YOUTUBE_DELAYEDplatforms contains youtube. Bulk creation is TikTok and Instagram only, even though GET /credit-costs lists a YouTube price.
400ADVANCED_WARMING_FLAG_REQUIREDadvanced_warming_terms and/or advanced_warming_terms_count was sent without wants_advanced_warming: true. The whole batch is rejected and nothing is charged.
400ADVANCED_WARMING_TERMS_REJECTEDA term was removed by cleanup — a case-insensitive duplicate, or outside 2-50 characters. details.dropped_terms lists them. The batch is refused rather than delivering fewer targets than requested.
400ADVANCED_WARMING_COUNT_MISMATCHadvanced_warming_terms and advanced_warming_terms_count state different quantities. Send one or the other.
400ADVANCED_WARMING_TERMSProvide advanced_warming_terms or advanced_warming_terms_count (3-30, multiples of 3).
400ADVANCED_WARMING_PLATFORMAdvanced Niche Warming is only available for TikTok and Instagram.
400WARMING_CONFLICTAdvanced Niche Warming cannot be combined with the legacy wants_niche_warming flag.
400DEEP_WARMING_DEPRECATEDLegacy: wants_deep_warming: true on a new order. Use Advanced Niche Warming.
402INSUFFICIENT_CREDITSNot enough credits for the full bulk operation.
409BUNDLE_PRICING_CHANGEDThe authoritative price or contract allowance changed before checkout. The full batch was rolled back with no debit or allowance consumption. Fetch GET /credit-costs again and retry with a new Idempotency-Key.
429capacity_cooldownThe full batch would exceed the workspace's rolling bundle capacity. Retry later.

Next Steps