> ## Documentation Index
> Fetch the complete documentation index at: https://growi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create ads in bulk

> Creates ads from creator content into existing ad sets. Every row runs the same checks as the dashboard ad builder and is created on its own, so one bad row does not stop the others. Responds 207 with one result per row. Ads publish to Meta in the background: poll GET /meta_ads?ids=. Needs an owner or admin key.

Creates up to 50 Meta ads from creator content, into ad sets you already have. Every row runs the same checks as the dashboard ad builder and is created on its own, so one bad row does not stop the others. Responds `207` with one result per row.

Step 3 of the [Meta ads automation flow](/essentials/meta-ads-automation).

## How it works

1. Every row is checked before anything is written. A row that fails comes back with `success: false` and an error `code`; the other rows still go ahead.
2. Each row that passes creates the ad in Growi with `state: queued`, inside the ad set in `meta_ad_set_id`.
3. A background job publishes each ad to Meta (video, creative, ad). Poll [List ads](/api-reference/endpoint/meta-ads/list-ads) with `ids=`.
4. With `launch_state: "active"` the ad turns on as soon as it publishes, and spend starts. With `paused` (the default), call [Launch an ad](/api-reference/endpoint/meta-ads/launch-ad) when you are ready.

<Tip>
  Send one bulk call of up to 50 rows with `launch_state: "active"` instead of creating ads one by one and launching each. With the default limit of 40 requests per hour per key, that keeps a daily job to a handful of calls.
</Tip>

## Row fields

| Field | Required | Notes |
| - | - | - |
| `name` | Yes | Ad name. Build it from `settings.naming` in [options](/api-reference/endpoint/meta-ads/get-builder-options). |
| `meta_ad_account_id` | Yes | From options `ad_accounts` |
| `meta_ad_set_id` | Yes | An existing ad set of that ad account, from options `meta_ad_sets`. New ad sets and campaigns are made in the dashboard. |
| `launch_state` | No | `paused` (default) or `active` |
| `meta_partnership_ad_request_id` | No | From a [sources](/api-reference/endpoint/meta-ads/list-ad-sources) row. Runs the ad as the creator. |
| `copy_template_id` | No | Records which copy template the text came from |
| `creative.primary_text`, `creative.headline`, `creative.call_to_action`, `creative.link_url` | Yes | `call_to_action` must be a Meta CTA type, `link_url` an http(s) URL |
| `creative.description`, `creative.url_tags`, `creative.page_id`, `creative.identity_display` | No | `url_tags` takes your UTM params |
| `creative.require_instagram` | No | `false` runs the ad from the Facebook page when no Instagram identity is active |
| Source keys in `creative` | Yes | The sources row's `creative_source`: `social_post_id`, `tts_external_id` + `tts_username`, or `content_submission_version_id` |

Fields that are not listed here are ignored.

## Crediting the creator

Each ad records the creator enrollment it is credited to (`campaign_affiliate_id`), which drives sales attribution and ad spend commission:

* A `social_post_id` attributed to a creator credits that creator's enrollment.
* A `content_submission_version_id` credits the enrollment the submission came from.
* Otherwise a `link_url` with the creator's Meta tag (`a=meta_<tag>`) credits the creator it belongs to. Get it from `creator_links` in [options](/api-reference/endpoint/meta-ads/get-builder-options).

Always use the creator's Meta link as `link_url` so purchases from the ad are attributed. See [How creators get credited](/essentials/meta-ads-automation#how-creators-get-credited-and-paid).

## Row error codes

| Code | Meaning |
| - | - |
| `PREFLIGHT_MISSING_FIELD` | A required field is missing or invalid. `details.field` (or `details.missing`) says which: for example `meta_ad_set_id` is missing, not one of your ad sets, or belongs to another ad account, or `launch_state` is not `paused` or `active`. |
| `PREFLIGHT_NO_AD_ACCOUNT` | `meta_ad_account_id` is not one of your ad accounts |
| `PREFLIGHT_INVALID_CTA` | `creative.call_to_action` is not a Meta CTA type |
| `PREFLIGHT_NO_PAGE` | No active Facebook page on your organization |
| `PREFLIGHT_NO_INSTAGRAM_ACTOR` | No active Instagram identity. Send `creative.require_instagram: false` to run from the Facebook page only. |
| `PREFLIGHT_LINK_URL_INVALID` | `link_url` is not an http(s) URL |
| `PREFLIGHT_NO_VIDEO_SOURCE` | `creative` has no source key |
| `PREFLIGHT_SUBMISSION_NOT_FOUND` | Unknown `content_submission_version_id`, or a submission from another organization |
| `PREFLIGHT_UNSUPPORTED_MEDIA_TYPE` | The submission is not a video or image |
| `PREFLIGHT_TTS_VIDEO_NOT_FOUND` | The TikTok Shop video has no sales in your shop |
| `PREFLIGHT_PARTNERSHIP_NOT_APPROVED` | The partnership request is missing, not approved, or the creator is not eligible |
| `PREFLIGHT_NO_PIXEL` | The ad set optimizes for conversions and the ad account has no pixel |
| `PREFLIGHT_CAMPAIGN_ARCHIVED` | The ad set's campaign is archived or deleted on Meta |
| `PREFLIGHT_AD_SET_FULL` | The ad set already holds Meta's maximum (50 ads, or 150 with Advantage+ audience) |
| `BULK_ROW_EXCEPTION` | An unexpected error on this row. Other rows are not affected. |

<Note>
  Rows run in order, and ads created by earlier rows in the same request count against the ad set. If 10 rows target an ad set that holds 45 of 50 ads, the first 5 are created and the last 5 fail with `PREFLIGHT_AD_SET_FULL`. Split rows across ad sets using `ads_count` and `max_ads` from options.
</Note>

## Authentication

Needs a **full-access** API key whose creator is an owner or admin. Legacy keys that are not tied to a person also work. View-only keys, and keys whose creator has the viewer role, get `403`. See [Rate limits and authentication](/api-reference/introduction).

## Errors

| Status | When |
| - | - |
| `401` | Missing or invalid API key, or the person who created the key is no longer an owner, admin or viewer of the organization |
| `403` | View-only API key, or the key's user is not an owner or admin |
| `404` | organization\_id is not a child brand of the key's organization |
| `422` | `ads` is empty (`BULK_EMPTY`) or has more than 50 rows (`BULK_TOO_LARGE`) |
| `429` | Rate limited: 2 seconds between requests, and 40 requests per hour per key by default |

<RequestExample>
  ```bash cURL theme={"dark"}
  curl -X POST "https://api.growi.io/api/public/v1/meta_ads/bulk" \
       -H "Authorization: Bearer YOUR_API_KEY" \
       -H "Content-Type: application/json" \
       -d '{
         "ads": [
           {
             "name": "Ava | 2026-10-14",
             "meta_ad_account_id": 1057,
             "meta_ad_set_id": 78552828,
             "launch_state": "active",
             "meta_partnership_ad_request_id": 4410,
             "creative": {
               "primary_text": "Our best seller, in Ava'"'"'s words",
               "headline": "Shop Seranova",
               "call_to_action": "SHOP_NOW",
               "link_url": "https://seranova.com/?a=meta_ava",
               "url_tags": "utm_source=meta&utm_medium=paid",
               "social_post_id": "instagram:3456789012345678901"
             }
           },
           {
             "name": "Mia | 2026-10-14",
             "meta_ad_account_id": 1057,
             "meta_ad_set_id": 78552828,
             "launch_state": "active",
             "creative": {
               "primary_text": "Mia tried it for 30 days",
               "headline": "Shop Seranova",
               "call_to_action": "SHOP_NOW",
               "link_url": "https://seranova.com/?a=meta_mia",
               "tts_external_id": "7421234567890123456",
               "tts_username": "mia.tries"
             }
           }
         ]
       }'
  ```
</RequestExample>

<ResponseExample>
  ```json 207 theme={"dark"}
  {
    "success": true,
    "results": [
      {
        "index": 0,
        "success": true,
        "data": {
          "meta_ad_id": 5012,
          "state": "queued",
          "status": "PAUSED",
          "payload": { "meta_ad_id": 5012, "state": "queued" }
        }
      },
      {
        "index": 1,
        "success": false,
        "error": {
          "code": "PREFLIGHT_MISSING_FIELD",
          "message": "meta_ad_set_id 999 is not an ad set of this organization",
          "user_message": "meta_ad_set_id 999 is not an ad set of this organization",
          "details": { "field": "meta_ad_set_id" }
        }
      }
    ],
    "summary": { "total": 2, "succeeded": 1, "failed": 1 }
  }
  ```

  ```json 422 theme={"dark"}
  {
    "success": false,
    "error": {
      "code": "BULK_TOO_LARGE",
      "message": "Batch size 51 exceeds maximum of 50",
      "details": { "received": 51, "max": 50 }
    }
  }
  ```
</ResponseExample>


## OpenAPI

````yaml POST /meta_ads/bulk
openapi: 3.0.1
info:
  title: Growi API
  description: >-
    API documentation for Growi platform, showcasing the public tracking event
    endpoint
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.growi.io/api/public/v1
security:
  - bearerAuth: []
paths:
  /meta_ads/bulk:
    post:
      summary: Creates up to 50 Meta ads
      description: >-
        Creates ads from creator content into existing ad sets. Every row runs
        the same checks as the dashboard ad builder and is created on its own,
        so one bad row does not stop the others. Responds 207 with one result
        per row. Ads publish to Meta in the background: poll GET /meta_ads?ids=.
        Needs an owner or admin key.
      parameters:
        - name: organization_id
          in: query
          required: false
          schema:
            type: integer
          description: >-
            Act on a child brand of the API key's organization. Omit it to act
            on the key's own organization. Any other id returns 404.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - ads
              properties:
                ads:
                  type: array
                  minItems: 1
                  maxItems: 50
                  items:
                    type: object
                    required:
                      - name
                      - meta_ad_account_id
                      - meta_ad_set_id
                      - creative
                    properties:
                      name:
                        type: string
                      meta_ad_account_id:
                        type: integer
                        description: From options ad_accounts
                      meta_ad_set_id:
                        type: integer
                        description: >-
                          An existing ad set of that ad account, from options
                          meta_ad_sets
                      launch_state:
                        type: string
                        enum:
                          - paused
                          - active
                        default: paused
                        description: >-
                          active turns the ad on (and starts spend) as soon as
                          it publishes
                      meta_partnership_ad_request_id:
                        type: integer
                        description: >-
                          From a sources row: run the ad as the creator
                          (partnership ad)
                      copy_template_id:
                        type: integer
                        description: >-
                          Records which copy template the text came from. Send
                          the text itself in creative.
                      creative:
                        type: object
                        required:
                          - primary_text
                          - headline
                          - call_to_action
                          - link_url
                        properties:
                          primary_text:
                            type: string
                          headline:
                            type: string
                          description:
                            type: string
                          call_to_action:
                            type: string
                            description: >-
                              A Meta CTA type, for example SHOP_NOW. See
                              allowed.call_to_actions in options.
                          link_url:
                            type: string
                            description: >-
                              http(s) destination. Use the creator link from
                              options so purchases and spend credit the creator.
                          url_tags:
                            type: string
                            description: >-
                              Meta URL parameters, for example
                              utm_source=meta&utm_medium=paid
                          page_id:
                            type: string
                            description: >-
                              Facebook page external id. Defaults to the active
                              page.
                          identity_display:
                            type: string
                          require_instagram:
                            type: boolean
                            description: >-
                              Send false to run from the Facebook page when no
                              Instagram identity is active
                          social_post_id:
                            type: string
                            description: From a sources row's creative_source
                          tts_external_id:
                            type: string
                            description: >-
                              From a TikTok Shop video row's creative_source
                              (with tts_username)
                          tts_username:
                            type: string
                          content_submission_version_id:
                            type: integer
                            description: From a submission row's creative_source
            example:
              ads:
                - name: Ava | 2026-10-14
                  meta_ad_account_id: 1057
                  meta_ad_set_id: 78552828
                  launch_state: active
                  meta_partnership_ad_request_id: 4410
                  creative:
                    primary_text: Our best seller, in Ava's words
                    headline: Shop Seranova
                    call_to_action: SHOP_NOW
                    link_url: https://seranova.com/?a=meta_ava
                    url_tags: utm_source=meta&utm_medium=paid
                    social_post_id: instagram:3456789012345678901
                - name: Mia | 2026-10-14
                  meta_ad_account_id: 1057
                  meta_ad_set_id: 78552828
                  launch_state: active
                  creative:
                    primary_text: Mia tried it for 30 days
                    headline: Shop Seranova
                    call_to_action: SHOP_NOW
                    link_url: https://seranova.com/?a=meta_mia
                    tts_external_id: '7421234567890123456'
                    tts_username: mia.tries
      responses:
        '207':
          description: One result per row, in request order
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        index:
                          type: integer
                        success:
                          type: boolean
                        data:
                          type: object
                          properties:
                            meta_ad_id:
                              type: integer
                              description: >-
                                Growi ad id. Use it with GET /meta_ads/{id},
                                launch and pause.
                            state:
                              type: string
                            status:
                              type: string
                        error:
                          type: object
                          properties:
                            code:
                              type: string
                            message:
                              type: string
                            user_message:
                              type: string
                            details:
                              type: object
                              additionalProperties: true
                  summary:
                    type: object
                    properties:
                      total:
                        type: integer
                      succeeded:
                        type: integer
                      failed:
                        type: integer
              example:
                success: true
                results:
                  - index: 0
                    success: true
                    data:
                      meta_ad_id: 5012
                      state: queued
                      status: PAUSED
                      payload:
                        meta_ad_id: 5012
                        state: queued
                  - index: 1
                    success: false
                    error:
                      code: PREFLIGHT_TTS_VIDEO_NOT_FOUND
                      message: >-
                        TikTok Shop video 7421234567890123456 has no sales in
                        this organization's shop
                      user_message: >-
                        TikTok Shop video 7421234567890123456 has no sales in
                        this organization's shop
                      details:
                        field: creative.tts_external_id
                summary:
                  total: 2
                  succeeded: 1
                  failed: 1
        '401':
          description: >-
            Missing or invalid API key, or the person who created the key is no
            longer an owner, admin or viewer of the organization
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  error:
                    type: string
              examples:
                missing_key:
                  summary: Missing or invalid key
                  value:
                    error: Not Authenticated
                creator_left:
                  summary: Key creator no longer in the organization
                  value:
                    success: false
                    error: >-
                      The user who created this API key no longer has access to
                      this organization.
        '403':
          description: View-only API key, or the key's user is not an owner or admin
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  error:
                    type: string
              examples:
                viewer_key:
                  summary: View-only API key
                  value:
                    error: This API key is read-only and cannot modify data.
                viewer_role:
                  summary: The key's user is not an owner or admin
                  value:
                    success: false
                    error: >-
                      Only an organization owner or admin can make changes with
                      this API key.
        '404':
          description: organization_id is not a child brand of the key's organization
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  error:
                    type: string
              example:
                success: false
                error: Organization not found
        '422':
          description: ads is empty or has more than 50 rows
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                      details:
                        type: object
                        additionalProperties: true
              examples:
                empty:
                  summary: No rows
                  value:
                    success: false
                    error:
                      code: BULK_EMPTY
                      message: '`ads` must be a non-empty array'
                too_large:
                  summary: More than 50 rows
                  value:
                    success: false
                    error:
                      code: BULK_TOO_LARGE
                      message: Batch size 51 exceeds maximum of 50
                      details:
                        received: 51
                        max: 50
        '429':
          description: >-
            Rate limited: 2 seconds between requests, and 40 requests per hour
            per key by default
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  retry_after:
                    type: integer
                  reset_time:
                    type: string
              examples:
                burst:
                  summary: Under 2 seconds since the last request
                  value:
                    error: >-
                      Rate limit exceeded. Please wait 2 seconds between
                      requests.
                    retry_after: 2
                hourly:
                  summary: Hourly cap reached
                  value:
                    error: Hourly rate limit of 40 requests exceeded.
                    retry_after: 1520
                    reset_time: '2026-10-12T15:00:00Z'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.