---
title: "Generate Google Ads text assets"
method: POST
path: "/api/apps/{app_id}/google-ads/assets/generate-text"
---

# Generate Google Ads text assets

`POST /api/apps/{app_id}/google-ads/assets/generate-text`

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Writes ad copy for the app and returns it as previews.

Nothing reaches Google Ads here. Each line comes back with a `session_asset_id` you pass to [Accept a generated Google Ads asset](/api-reference/accept-a-generated-google-ads-asset) to put it on a live campaign. Use [Generate Google Ads assets](/api-reference/generate-google-ads-assets) instead when you want copy and pictures in one call.

Pick the lines you want with `asset_field_types`. `SEARCH` campaigns do not take `LONG_HEADLINE`, so Google rejects that combination.

Grounding is flexible here, unlike image generation. You may send `final_url` and `freeform_prompt` together, and sending neither grounds the copy in the app's own home page.

The copy is written in the language of the app's own site, not the language you ask in, and the response reports which one in `language`.

<Note>Only one generation of each kind runs per app at a time. Calling this while one is running returns a 200 carrying `in_flight` set to `true`, that run's `session_id`, and an empty `assets`. Poll the `session_id` you were given rather than retrying.</Note>

This endpoint costs 2 of 20 copy requests a minute per app, shared with the other Google Ads endpoints that write copy, and up to 4 when the retry below happens.

<Note>When Google refuses the page you pointed at, Base44 retries once on the app's own published URL, and that retry costs the same again. Pace requests against the doubled figure, not the single one, or a refusal-heavy app hits a 429 part-way through its own recovery.</Note>

<Note>Generating creative does not spend credits and is not billed. It is capped by rate limits instead, so a burst gets a 429 rather than a bill.</Note>

<Note>This endpoint accepts a personal API key. Workspace API keys are not authorized for it and are rejected with a 403.</Note>

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time. Send only the fields documented here. Other request fields are not supported and their behavior can change.</Warning>

## Path parameters

- `app_id` string, required — ID of the app whose Google Ads creative you want to generate or read.

## Request body

- object
  - `channel_type` string — Campaign type the copy is for, one of `PERFORMANCE_MAX`, `DEMAND_GEN`, `SEARCH` or `DISPLAY`.
  - `asset_field_types` string[] — Which kinds of line to write, from `HEADLINE`, `LONG_HEADLINE` or `DESCRIPTION`. Send between one and three entries. An image field type here is rejected with a 422.
  - `final_url` string — Page to base the copy on. Unlike image generation you may send this together with `freeform_prompt`, and sending neither grounds the copy in the app's own home page.
  - `freeform_prompt` string — What the copy should say, in your own words, up to 1500 characters.
  - `keywords` string[] — Keywords to work into the copy. Send between 1 and 15 of them, and none of them blank. Omit the field entirely rather than sending an empty list.

## Response `200`

The copy that was generated.

- GenerateTextAssetsResult — The copy one text generation produced.
  - `session_id` string, required — ID of this generation. Pass it as `session_id` to [List Google Ads generated assets](/api-reference/list-google-ads-generated-assets), or to [Accept a generated Google Ads asset](/api-reference/accept-a-generated-google-ads-asset) with one of the returned `session_asset_id` values.
  - `assets` GeneratedAssetSummary[], required — The copy that was generated, one entry per line. Empty when the engine produced nothing and when a generation for this app was already running, which `in_flight` tells apart.
    - `session_asset_id` string, required — ID of this asset within the session. Pass it as `session_asset_id` to [Accept a generated Google Ads asset](/api-reference/accept-a-generated-google-ads-asset).
    - `asset_field_type` string, required — Which slot on the ad this asset fills. Text assets are `HEADLINE`, `LONG_HEADLINE` or `DESCRIPTION`. Image assets are `MARKETING_IMAGE`, `SQUARE_MARKETING_IMAGE`, `PORTRAIT_MARKETING_IMAGE` or `TALL_PORTRAIT_MARKETING_IMAGE`.
    - `kind` string, required — Whether the asset is copy (`text`) or a picture (`image`).
    - `text` string, nullable, required — The generated copy, or `null` on an image asset.
    - `image_url` string, nullable, required — URL of the generated picture, or `null` on a text asset. It is a Base44 preview URL, not a Google one, and it stops resolving once the asset is cleaned up.
    - `source` string, required — Which engine wrote it. The value is `google` for Google's own asset generation and `inhouse` for the Base44 model that covers languages Google does not generate for, and that stands in when Google's call fails.
    - `language` string, nullable, required — Language of a text asset as a lowercase two-letter code, or `null` on an image, because image assets carry no copy.
    - `channel_type` string, required — Campaign type the asset was generated for, one of `SEARCH`, `PERFORMANCE_MAX`, `DISPLAY` or `DEMAND_GEN`.
    - `created_at` string, date-time, required — When the asset was generated.
  - `language` string, nullable — Language the copy was written in, as a lowercase two-letter code. It comes from the app's own site rather than from anything you send. The field is absent on a lock-miss response unless you asked for a specific campaign.
  - `in_flight` boolean, nullable — Present and `true` only when a text generation for this app was already running, in which case `session_id` is that run's and `assets` is empty. Poll the returned `session_id` rather than retrying. The field is absent on every other response.

## Other responses

- `400` — Google Ads rejected the generation request, for example a landing page it could not read. The response message carries Google's reason.
- `401` — Missing or invalid credentials.
- `403` — You don't have access to this app, the app does not exist, or you used a workspace API key. A missing app and an app you cannot reach are deliberately the same answer.
- `409` — The request to Google Ads timed out after being sent. Nothing was attached to a campaign, so retrying is safe.
- `422` — `asset_field_types` contains a value that is not a text field type, is empty, or has more than three entries; `keywords` is present but empty, longer than 15 entries, or contains a blank entry; `freeform_prompt` is longer than 1500 characters; or the channel is `SEARCH` and you asked for `LONG_HEADLINE`.
- `429` — The app has used up one of the creative rate limits, either 20 copy requests a minute or 10 picture requests a minute. Retry later.

## Changes

- **2026-09-01** `d8572ad3094a` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/idealspot/apis/base44-app-management-api/changes/api/apps/:app_id/google-ads/assets/generate-text/post.md)

---

[API](https://skmtc.dev/idealspot/apis/base44-app-management-api.md) · [All operations](https://skmtc.dev/idealspot/apis/base44-app-management-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/idealspot/base44-app-management-api/revisions/56dc45634956/schema)
