# Meta Ads

Run Facebook and Instagram campaigns from the API, with conversation ads, creative testing, reach and frequency buys, previews and live insights.

Meta Ads covers Facebook, Instagram, Messenger, Threads, Audience Network and WhatsApp Status placements. It is the most complete network in Pozy: every operation in the [ads overview](https://pozy.ai/docs/ads#what-each-network-supports) works here, plus several that only Meta offers.

## Before you start

On Meta's side you need:

- A Business portfolio with at least one ad account (`act_<number>`). You can also [create one through the API](#create-an-ad-account).
- A Facebook Page. Ads run as the Page. Instagram placements also need an Instagram professional account linked to that Page.
- A pixel or dataset if the campaign optimizes for conversions. See [Pixels and tags](https://pozy.ai/docs/ads/pixels).

> **Instagram Login accounts cannot run ads:**
> 
> An Instagram account connected with Instagram Login never carries ads permissions. Ads calls made with its `accountId` return `422` with code `linked_account_required`. Connect a `facebook` or `metaads` account in the same profile and use that one.

## Connect

Call `GET /v1/connect/facebook/ads` (or `/v1/connect/instagram/ads`) with the `profileId`. Meta offers two login modes, picked with `loginMode`:

| `loginMode` | What it creates | Use it when |
| --- | --- | --- |
| `classic` (default) | A `metaads` account linked to the profile's Facebook account. If that account already has the ads permissions, no consent screen appears. | You already publish to the Page through Pozy. |
| `business` | A standalone `metaads` account with a system-user token. No Facebook posting account is created. | You only run ads, or you want a token that does not depend on one person staying an admin. |

Either way, ads need a Page as the actor. With business login, pass `pageId` on the connect call or change the default Page later with `PUT /v1/accounts/{accountId}/facebook-page`.

A `metaads` account is for ads only. Page and Instagram insights need the organic [Facebook](https://pozy.ai/docs/platforms/facebook) or [Instagram](https://pozy.ai/docs/platforms/instagram) connection, which is a separate connected account.

### Create an ad account

`POST /v1/ads/accounts` creates a Meta ad account inside the customer's own Business portfolio. Find the portfolio with `GET /v1/ads/businesses`, then send `accountId` (an active `metaads` connection), `businessId`, `name`, `currency` and `timezoneId`. `timezoneId` is Meta's numeric timezone id, not an IANA name. The connection needs Business admin access.

> **Warning:**
> 
> A new ad account has no payment method. The customer adds one in Meta Ads Manager before anything delivers. The create is not idempotent: after a timeout, check the portfolio in Ads Manager before you try again.

## Create a campaign

`POST /v1/ads/create` builds a campaign, an ad set and an ad in one call. Every field sits at the top level, and the network is inferred from `accountId`.

```bash cURL
curl -X POST https://api.pozy.ai/v1/ads/create \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: spring-sale-us-feed" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "adAccountId": "act_1234567890",
    "name": "Spring sale - US",
    "goal": "traffic",
    "budgetAmount": 40,
    "budgetType": "daily",
    "status": "PAUSED",
    "headline": "30% off everything this week",
    "body": "Free shipping on orders over $50.",
    "imageUrl": "https://cdn.example.com/spring-1080.jpg",
    "linkUrl": "https://shop.example.com/spring",
    "callToAction": "SHOP_NOW",
    "countries": ["US"],
    "ageMin": 25,
    "ageMax": 54
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/ads/create", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Idempotency-Key": "spring-sale-us-feed",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    accountId: "66b2e19d8c3f5a7e9d0b1c2d",
    adAccountId: "act_1234567890",
    name: "Spring sale - US",
    goal: "traffic",
    budgetAmount: 40,
    budgetType: "daily",
    status: "PAUSED",
    headline: "30% off everything this week",
    body: "Free shipping on orders over $50.",
    imageUrl: "https://cdn.example.com/spring-1080.jpg",
    linkUrl: "https://shop.example.com/spring",
    callToAction: "SHOP_NOW",
    countries: ["US"],
    ageMin: 25,
    ageMax: 54,
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.ad.platformCampaignId, data.ad.reviewStatus);
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/ads/create",
    headers={
        "Authorization": f"Bearer {os.environ['POZY_API_KEY']}",
        "Idempotency-Key": "spring-sale-us-feed",
    },
    json={
        "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
        "adAccountId": "act_1234567890",
        "name": "Spring sale - US",
        "goal": "traffic",
        "budgetAmount": 40,
        "budgetType": "daily",
        "status": "PAUSED",
        "headline": "30% off everything this week",
        "body": "Free shipping on orders over $50.",
        "imageUrl": "https://cdn.example.com/spring-1080.jpg",
        "linkUrl": "https://shop.example.com/spring",
        "callToAction": "SHOP_NOW",
        "countries": ["US"],
        "ageMin": 25,
        "ageMax": 54,
    },
    timeout=60,
)
resp.raise_for_status()
print(resp.json()["ad"]["_id"])
```

```json
{
  "ad": {
    "_id": "66d4a1b2c3e4f5a6b7c8d9e2",
    "name": "Spring sale - US",
    "platform": "facebook",
    "status": "paused",
    "configuredStatus": "PAUSED",
    "reviewStatus": "in_review",
    "adType": "standalone",
    "goal": "traffic",
    "budget": { "amount": 40, "type": "daily" },
    "platformAdAccountId": "act_1234567890",
    "platformCampaignId": "120250000000000000",
    "platformAdSetId": "120250000000000001",
    "platformAdId": "120260000000000000",
    "creative": { "creativeId": "120250000000000005", "pageId": "811889972008357" }
  },
  "message": "Ad created"
}
```

Keep three ids from the response: `_id` for per-ad calls, `platformCampaignId` for campaign calls and `platformAdSetId` for ad set calls. `creative.creativeId` can be reused later as `existingCreativeId`.

Without `status: "PAUSED"` the ad is created active and goes straight into Meta's review. To provision a campaign container first and add ad sets to it later, use `POST /v1/ads/campaigns`, then pass the campaign id as `existingCampaignId` on each create. `POST /v1/ads/campaigns` creates the campaign paused unless you send `status: "ACTIVE"`.

### Check a request without spending

Add `validateOnly: true` to run the whole tree through Meta's own validator. Nothing is created. You get `200` with one result per node (`validated` or `skipped`) and Meta's error verbatim when something is wrong. Meta can only validate an ad set against a real campaign, so on a fresh tree the ad set reports `skipped` unless you pass `existingCampaignId`. Dry runs work on the single-ad shape only, not with `creatives[]`, `adSetId` or reserved buying.

## Goals

`goal` picks the Meta objective and a sensible optimization goal for it.

| `goal` | Meta objective | Needs |
| --- | --- | --- |
| `traffic` | Traffic | `linkUrl` |
| `engagement` | Engagement | |
| `awareness` | Awareness | |
| `video_views` | Engagement, ThruPlay | A video creative |
| `lead_generation` | Leads, instant form | `leadGenFormId` ([Lead forms](https://pozy.ai/docs/ads/lead-forms)) |
| `lead_conversion` | Leads, website | `promotedObject` with a pixel |
| `conversions` | Sales | `promotedObject.pixelId` and `customEventType` |
| `app_promotion` | App promotion | `promotedObject.applicationId` and `objectStoreUrl` |
| `catalog_sales` | Sales, catalog | `promotedObject.productCatalogId` / `productSetId` ([Catalogs](https://pozy.ai/docs/ads/catalogs)) |
| `page_likes`, `page_visits` | Engagement | A Page |

`optimizationGoal`, `billingEvent` and `promotedObject` override the defaults when you need Meta's exact vocabulary (for example `LINK_CLICKS` or `OFFSITE_CONVERSIONS`).

## Budgets and bidding

| Field | Values | Notes |
| --- | --- | --- |
| `budgetAmount` | number | Whole units of the ad account currency. |
| `budgetType` | `daily`, `lifetime` | A lifetime budget needs `endDate`. |
| `budgetLevel` | `adset` (default), `campaign` | `campaign` is Meta's Advantage campaign budget (CBO). Read `budgetLevel` on the tree before you change a budget, because the update goes to whichever level owns it. |
| `startDate`, `endDate` | ISO 8601 | A value without an offset is read in the ad account's timezone. |
| `buyingType` | `AUCTION` (default), `RESERVED` | `RESERVED` is a [reach and frequency](#reach-and-frequency) buy. |

Bid strategies go inside `platformSpecificData` on create:

| `bidStrategy` | Companion field | Behavior |
| --- | --- | --- |
| `LOWEST_COST_WITHOUT_CAP` (default) | | Meta spends the budget for the most results. |
| `LOWEST_COST_WITH_BID_CAP` | `bidAmount` | Hard ceiling per auction. |
| `COST_CAP` | `bidAmount` | Target average cost per result. |
| `LOWEST_COST_WITH_MIN_ROAS` | `roasAverageFloor` | Value-optimized sales campaigns only. `2.0` means 2x ROAS. |

`platformSpecificData` also takes `dailyMinSpendTarget` and `lifetimeMinSpendTarget` (reserve part of a campaign budget for one ad set), `customerLifecycle` (new customers only, or all) and `adSchedule` (dayparting rows of `dayOfWeek`, `startHour`, `endHour`, which need a lifetime budget).

**Budget schedules** raise a budget for a sale or a busy weekend: `POST /v1/ads/high-demand-periods` with exactly one of `campaignId` or `adSetId`, and `GET /v1/ads/high-demand-periods` to read them.

**Value rule sets** raise or lower the bid for segments worth more or less to you, without splitting them into separate ad sets. Create one with `POST /v1/ads/value-rule-sets`, then attach it with `valueRuleSetId` on the create or on `PUT /v1/ads/ad-sets/{adSetId}`. Meta allows 6 sets per ad account, 10 rules per set and 4 criteria per rule. An update replaces the whole set, so read it first and send every rule you want to keep.

## Creatives and formats

Exactly one media source per creative. Pick the shape:

| Shape | Fields | Notes |
| --- | --- | --- |
| Single image | `imageUrl` | JPEG or PNG, up to 30 MB. |
| Single video | `video: { url, thumbnailUrl? }` or `video: { id }` | MP4 or MOV up to 4 GB. The create waits for Meta to finish processing, up to 10 minutes, so set your HTTP timeout above that. |
| Carousel | `carouselCards` (2 to 10) | Each card has its own image and optional headline, description, link and button. `optimizeCardOrder` lets Meta reorder. |
| N ads in one ad set | `creatives[]` | See [creative testing](#creative-testing). |
| Asset pool | `dynamicCreative` | Up to several images, videos, bodies and titles that Meta recombines. |
| Per-placement assets | `placementAssets` | A 9:16 asset on Stories and Reels and a 1:1 on Feed, in one ad. All images or all videos. 1 to 10 rules. |
| Several languages | `translations` + `defaultLocale` | Up to 10 locales on one ad, so engagement stays on one post. Every locale needs its own headline, body and description. |
| Reuse a creative | `existingCreativeId` | From an earlier create or `POST /v1/ads/creatives`. |

`callToAction` takes the values listed in the [API reference](https://pozy.ai/docs/api/createStandaloneAd), such as `SHOP_NOW`, `LEARN_MORE`, `SIGN_UP` and `CONTACT_US`. Media URLs must be public: Meta downloads them itself, and a hotlink-protected host fails.

### Advantage+ options

- `creativeFeatures` opts a creative in or out of Meta's individual Advantage+ creative enhancements, keyed by Meta's feature names. Anything you leave out is opted out.
- `advantageAudience: 1` lets Meta expand delivery beyond your targeting. Pozy sends `0` for you unless you set it.
- `budgetLevel: "campaign"` turns on Advantage campaign budget.
- `promotion` attaches an offer (`PERCENTAGE_OFF`, `AMOUNT_OFF`, `FREE_SHIPPING`, `FREE_RETURN`, `PROMO_CODE`) to the creative. Check `ad.creative.promotionStatus` on a later read to see whether Meta kept it.

## Conversation and call ads

`POST /v1/ads/messaging` creates an ad that opens a chat instead of a web page. `destination` picks where:

| `destination` | Opens in | Requires |
| --- | --- | --- |
| `whatsapp` | WhatsApp | The Page paired with a verified WhatsApp Business number. `whatsappPhoneNumber` picks one of several. |
| `messenger` | The Page's Messenger inbox | A Page |
| `instagram_direct` | Instagram Direct | An Instagram professional account linked to the Page |

The ad set is built with conversation optimization and the matching destination type. Useful extras:

- `welcomeMessage` replaces Meta's greeting and pre-fills the person's first message.
- `workflowId` starts one of your [workflows](https://pozy.ai/docs/automations/workflows) in the conversation the click opens.
- `existingPostId` or `objectStoryId` runs an organic post as the ad instead of a new creative.
- `adSetId` adds creatives to a running conversation ad set without resetting its learning.

`POST /v1/ads/call` builds a call ad that dials `phoneNumber` (E.164). `linkUrl` must be a real web page, not a `tel:` link. `POST /v1/ads/ctwa` is the older click-to-WhatsApp endpoint; it still works, but new code should use `/v1/ads/messaging` with `destination: "whatsapp"`.

## Creative testing

Send `creatives[]` instead of the single-creative fields to get one campaign, one ad set and N ads that share the budget. The ads are named `<name> #1`, `<name> #2` and so on, and the response returns `ads[]` instead of `ad`. To add a variation later, call the create again with the ad set's id in `adSetId` and a single creative: it inherits the budget, targeting and schedule and keeps the learning Meta has done.

Rank variations with the per-ad metrics on `GET /v1/ads/tree`, or split one ad's results by the asset Meta served with the breakdowns `image_asset`, `video_asset`, `body_asset` and `title_asset`.

`GET /v1/ads/studies` lists the A/B tests and lift studies on the ad account.

### Partnership ads

Partnership ads run a creator's Instagram post under your ad account, with both handles on the ad. Ask a creator for permission with `POST /v1/ads/partnership-permissions`, track who has granted it with `GET /v1/ads/partnership-permissions`, and find their eligible posts with `GET /v1/ads/partnership-content`. This is in private beta, so the calls can return `feature_not_available` until it opens to your workspace.

## Reach and frequency

Reach and frequency buys lock a price for a fixed audience and frequency before you spend.

### Quote

`POST /v1/ads/rf-predictions` with the targeting, explicit `placements`, the window and either `budgetAmount` (Meta predicts reach) or `reach` (Meta predicts budget). `frequencyCap` is 1 to 90. Show the returned minimum and maximum budget and reach.

### Reserve

`POST /v1/ads/rf-predictions/{predictionId}/reserve` holds the inventory for about 14 days and returns a new prediction id. Release it with `DELETE /v1/ads/rf-predictions/{predictionId}`.

### Buy

`POST /v1/ads/create` with `buyingType: "RESERVED"` and `rfPredictionId` set to the reserved id. Send no budget or bid fields: they come from the reservation. Targeting and placements must match the quote.

## Previews

`GET /v1/ads/{adId}/preview?formats=INSTAGRAM_STORY,MOBILE_FEED_STANDARD` renders an existing ad. `POST /v1/ads/preview` renders a creative before it is on an ad, from `existingCreativeId` or a raw `creativeSpec`. Both return Meta's iframe HTML, one per format, up to 10 formats per call. Embed the snippet as it is and fetch it when the user opens the preview: the signed URL inside is short-lived.

## Ad comments

`GET /v1/ads/{adId}/comments` reads the comments on an ad, including dark posts that never appeared on the Page. An ad that runs on both Facebook and Instagram has two threads: pick one with `placement=facebook` or `placement=instagram`. Reply through the normal [comments endpoints](https://pozy.ai/docs/inbox/comments) with the post id the response returns in `meta.effectiveStoryId`. Hiding and deleting through the ad endpoints is TikTok only and returns `501` on Meta.

## Manage running ads

| Job | Call |
| --- | --- |
| Pause or resume a campaign, ad set or ad | `PUT /v1/ads/campaigns/{campaignId}/status`, `PUT /v1/ads/ad-sets/{adSetId}/status`, `PUT /v1/ads/{adId}/status` with `status` (`active` or `paused`) and `platform` |
| Pause or resume up to 50 campaigns | `POST /v1/ads/campaigns/bulk-status` |
| Change an ad set budget, bid, schedule or optimization | `PUT /v1/ads/ad-sets/{adSetId}`, delivery settings inside `platformSpecificData` |
| Change a campaign budget (CBO) | `PUT /v1/ads/campaigns/{campaignId}` with `budget` |
| Change targeting | `PUT /v1/ads/{adId}` with `targeting` (applies to the ad's ad set) |
| Swap the creative | `PUT /v1/ads/{adId}` with `creative`. Meta creatives are immutable, so Pozy builds a new one and points the ad at it. The ad goes through review again. |
| Duplicate | `POST /v1/ads/campaigns/{campaignId}/duplicate`, `/ad-sets/{adSetId}/duplicate`, `/{adId}/duplicate` |
| Delete | `DELETE` on the campaign, ad set or ad. Deletes cascade down. |

Each switch is independent. Pausing a campaign stops everything under it, but resuming it only restarts children whose own switch is on. When a status is already what you asked for, nothing is written and the response says `skipped: 1`.

Duplicates default to a deep copy created paused (`statusOption: "PAUSED"`), so nothing spends until you activate it. `renameStrategy`, `renamePrefix` and `renameSuffix` name the copies, and the response maps each source id to its copy in `adSetIdMap` and `adIdMap`. A large campaign takes minutes because Meta copies one object at a time.

`GET /v1/ads/ad-sets/{adSetId}` reads the ad set live from Meta, including `learning_stage_info.status` (`LEARNING`, `SUCCESS`, `FAIL`, `WAIVING`). A missing status means unknown, not "out of learning".

## Reporting

| You want | Call | Cost to the account |
| --- | --- | --- |
| Spend, CPC, CPM, conversions and ROAS per level | `GET /v1/ads/tree`, `GET /v1/ads`, `GET /v1/ads/{adId}` | None, served from synced metrics |
| A daily series | `GET /v1/ads/timeline` | None |
| One ad or campaign split by a dimension | `GET /v1/ads/{adId}/analytics`, `GET /v1/ads/campaigns/{campaignId}/analytics` with `breakdowns` | One Meta call per dimension |
| Any Meta field, breakdown or filter | `GET /v1/ads/insights` with `objectId`, `level`, `fields`, `breakdowns` | One live call |
| Long ranges or whole accounts | `POST /v1/ads/insights/reports`, then poll `GET /v1/ads/insights/reports/{reportRunId}` | A Meta job |

Breakdown dimensions: `age`, `gender`, `country`, `region`, `publisher_platform`, `platform_position`, `device_platform`, `impression_device`, `image_asset`, `video_asset`, `body_asset`, `title_asset`.

Metrics on each node include `spend`, `impressions`, `reach`, `clicks`, `ctr`, `cpc`, `cpm`, `conversions`, `actions`, `actionValues`, `purchaseValue` and `roas`. Reach is de-duplicated within a day only, so never sum it across days. A freshly connected ad account can answer `202` with `backfillPending: true` while history loads: retry after the `Retry-After` delay.

When polling an async report, stop on `Job Completed`, `Job Failed` or `Job Skipped`. Start at 5 seconds and back off to a minute. More in [Reporting](https://pozy.ai/docs/ads/reporting).

## Regulated ads

- `specialAdCategories` declares housing, employment, credit, financial products, politics or gambling ads. Meta then restricts targeting (no income or ZIP targeting).
- Ads delivered in the EU need `dsaBeneficiary` and `dsaPayor` unless the ad account has defaults (`GET /v1/ads/dsa-defaults`).
- Other regimes (Brazil, Thailand, India and more) use `regionalRegulatedCategories` and `regionalRegulationIdentities`.

## Limits and errors

| Error | Cause | Fix |
| --- | --- | --- |
| `422` `linked_account_required` | The account is an Instagram Login account | Use a `facebook` or `metaads` account from the same profile. |
| `400` with `type: "platform_error"` | Meta refused the request. Meta's code and subcode are in `platformError`. | Read Meta's message: it usually names the field. |
| `400` naming two fields | Two creative shapes that exclude each other | Send one media source per creative. |
| `409` `BUDGET_LEVEL_MISMATCH` | You sent an ad set budget to a CBO campaign | Update the budget on the campaign instead. |
| `409` on a boost or create retry | An identical request is still in flight | Wait, then read `GET /v1/ads`. Send an `Idempotency-Key`. |
| `502` `video_thumbnail_unavailable` | Meta produced no poster for the video | Send `thumbnailUrl`. |

Locations overlap is the most common targeting error: do not send a city together with the country that contains it. See [Targeting](https://pozy.ai/docs/ads/targeting#meta).

## Endpoints

Campaigns, ad sets, ads and reporting:

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/ads/create`](https://pozy.ai/docs/api/createStandaloneAd) | Create standalone ad |
| [`POST /v1/ads/campaigns`](https://pozy.ai/docs/api/createAdCampaign) | Create a standalone campaign |
| [`POST /v1/ads/boost`](https://pozy.ai/docs/api/boostPost) | Boost post as ad |
| [`GET /v1/ads/tree`](https://pozy.ai/docs/api/getAdTree) | Get campaign tree |
| [`GET /v1/ads/campaigns`](https://pozy.ai/docs/api/listAdCampaigns) | List campaigns |
| [`GET /v1/ads/campaigns/{campaignId}`](https://pozy.ai/docs/api/getAdCampaignDetails) | Get live campaign details |
| [`PUT /v1/ads/campaigns/{campaignId}`](https://pozy.ai/docs/api/updateAdCampaign) | Update a campaign |
| [`PUT /v1/ads/campaigns/{campaignId}/status`](https://pozy.ai/docs/api/updateAdCampaignStatus) | Pause or resume a campaign |
| [`POST /v1/ads/campaigns/bulk-status`](https://pozy.ai/docs/api/bulkUpdateAdCampaignStatus) | Pause or resume many campaigns |
| [`POST /v1/ads/campaigns/{campaignId}/duplicate`](https://pozy.ai/docs/api/duplicateAdCampaign) | Duplicate a campaign |
| [`DELETE /v1/ads/campaigns/{campaignId}`](https://pozy.ai/docs/api/deleteAdCampaign) | Delete a campaign |
| [`GET /v1/ads/ad-sets`](https://pozy.ai/docs/api/listAdSets) | List ad sets |
| [`GET /v1/ads/ad-sets/{adSetId}`](https://pozy.ai/docs/api/getAdSetDetails) | Get live ad-set details |
| [`PUT /v1/ads/ad-sets/{adSetId}`](https://pozy.ai/docs/api/updateAdSet) | Update an ad set |
| [`PUT /v1/ads/ad-sets/{adSetId}/status`](https://pozy.ai/docs/api/updateAdSetStatus) | Pause or resume a single ad set |
| [`POST /v1/ads/ad-sets/{adSetId}/duplicate`](https://pozy.ai/docs/api/duplicateAdSet) | Duplicate an ad set |
| [`DELETE /v1/ads/ad-sets/{adSetId}`](https://pozy.ai/docs/api/deleteAdSet) | Delete an ad set |
| [`GET /v1/ads/{adId}`](https://pozy.ai/docs/api/getAd) | Get ad details |
| [`PUT /v1/ads/{adId}`](https://pozy.ai/docs/api/updateAd) | Update ad |
| [`PUT /v1/ads/{adId}/status`](https://pozy.ai/docs/api/updateAdStatus) | Pause or resume a single ad |
| [`POST /v1/ads/{adId}/duplicate`](https://pozy.ai/docs/api/duplicateAd) | Duplicate an ad |
| [`DELETE /v1/ads/{adId}`](https://pozy.ai/docs/api/deleteAd) | Cancel an ad |
| [`GET /v1/ads/{adId}/review`](https://pozy.ai/docs/api/getAdReview) | Read the platform's review verdict for an ad |
| [`GET /v1/ads/{adId}/analytics`](https://pozy.ai/docs/api/getAdAnalytics) | Get ad analytics |
| [`GET /v1/ads/campaigns/{campaignId}/analytics`](https://pozy.ai/docs/api/getCampaignAnalytics) | Get campaign analytics |
| [`GET /v1/ads/timeline`](https://pozy.ai/docs/api/getAdsTimeline) | Get daily account metrics |
| [`GET /v1/ads/insights`](https://pozy.ai/docs/api/queryAdInsights) | Flexible live insights query |
| [`POST /v1/ads/insights/reports`](https://pozy.ai/docs/api/createAdInsightsReport) | Submit async insights report |
| [`GET /v1/ads/insights/reports/{reportRunId}`](https://pozy.ai/docs/api/getAdInsightsReport) | Poll an async insights report run |

Meta-only tools:

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/ads/businesses`](https://pozy.ai/docs/api/listMetaBusinesses) | Businesses list |
| [`GET /v1/ads/businesses/users`](https://pozy.ai/docs/api/listMetaBusinessUsers) | Business users |
| [`POST /v1/ads/accounts`](https://pozy.ai/docs/api/createAdAccount) | Create ad account |
| [`PATCH /v1/ads/accounts`](https://pozy.ai/docs/api/updateAdAccount) | Update ad account settings |
| [`GET /v1/ads/accounts/health`](https://pozy.ai/docs/api/getAdAccountHealth) | Check an ad account before creating ads |
| [`GET /v1/ads/accounts/finance`](https://pozy.ai/docs/api/getAdAccountFinance) | Ad account finances |
| [`GET /v1/ads/instagram-accounts`](https://pozy.ai/docs/api/listAdsInstagramAccounts) | List Instagram ad identities |
| [`GET /v1/ads/instagram-posts`](https://pozy.ai/docs/api/listAdsInstagramPosts) | List Instagram posts to boost |
| [`GET /v1/ads/value-rule-sets`](https://pozy.ai/docs/api/listValueRuleSets) | List value rule sets |
| [`POST /v1/ads/value-rule-sets`](https://pozy.ai/docs/api/createValueRuleSet) | Create a value rule set |
| [`GET /v1/ads/value-rule-sets/{valueRuleSetId}`](https://pozy.ai/docs/api/getValueRuleSet) | Read a value rule set |
| [`PUT /v1/ads/value-rule-sets/{valueRuleSetId}`](https://pozy.ai/docs/api/updateValueRuleSet) | Replace a value rule set |
| [`DELETE /v1/ads/value-rule-sets/{valueRuleSetId}`](https://pozy.ai/docs/api/deleteValueRuleSet) | Delete a value rule set |
| [`GET /v1/ads/studies`](https://pozy.ai/docs/api/listAdStudies) | A/B tests and lift studies |
| [`GET /v1/ads/activity`](https://pozy.ai/docs/api/getAdsActivityLog) | Ad account change / audit log |
| [`GET /v1/ads/high-demand-periods`](https://pozy.ai/docs/api/listHighDemandPeriods) | List high-demand periods |
| [`POST /v1/ads/high-demand-periods`](https://pozy.ai/docs/api/createHighDemandPeriod) | Schedule a budget increase |
| [`GET /v1/ads/advertisable-applications`](https://pozy.ai/docs/api/listAdvertisableApplications) | List advertisable apps |
| [`GET /v1/ads/ios-fourteen-campaign-limits`](https://pozy.ai/docs/api/getIosFourteenCampaignLimits) | Get iOS 14 campaign limits |
| [`GET /v1/ads/page-users`](https://pozy.ai/docs/api/listPageUsers) | Page users of a business |
| [`POST /v1/ads/page-users`](https://pozy.ai/docs/api/assignPageUser) | Assign a user to a Page |
| [`DELETE /v1/ads/page-users`](https://pozy.ai/docs/api/removePageUser) | Remove a user from a Page |
| [`GET /v1/ads/dsa-defaults`](https://pozy.ai/docs/api/getDsaDefaults) | Get ad account DSA defaults |
| [`GET /v1/ads/dsa-recommendations`](https://pozy.ai/docs/api/getDsaRecommendations) | Get DSA recommendations |
| [`POST /v1/ads/preview`](https://pozy.ai/docs/api/generateAdPreviews) | Render pre-create ad previews |
| [`GET /v1/ads/{adId}/preview`](https://pozy.ai/docs/api/getAdPreviews) | Render previews of an existing ad |
| [`GET /v1/ads/{adId}/media`](https://pozy.ai/docs/api/getAdMedia) | Direct video and image URLs for an ad |
| [`GET /v1/ads/creatives`](https://pozy.ai/docs/api/listAdCreatives) | Creative library |
| [`POST /v1/ads/creatives`](https://pozy.ai/docs/api/createAdCreative) | Create a standalone creative |
| [`GET /v1/ads/images`](https://pozy.ai/docs/api/listAdImages) | Ad image library |
| [`POST /v1/ads/images`](https://pozy.ai/docs/api/uploadAdImage) | Upload an ad image from base64 |
| [`GET /v1/ads/videos`](https://pozy.ai/docs/api/listAdVideos) | Ad video library |
| [`POST /v1/ads/videos`](https://pozy.ai/docs/api/uploadAdVideo) | Upload an ad video |
| [`GET /v1/ads/{adId}/comments`](https://pozy.ai/docs/api/getAdComments) | List comments on an ad |
| [`GET /v1/ads/partnership-content`](https://pozy.ai/docs/api/listPartnershipAdContent) | List partnership ad content |
| [`GET /v1/ads/partnership-permissions`](https://pozy.ai/docs/api/listPartnershipAdPermissions) | List partnership permissions |
| [`POST /v1/ads/partnership-permissions`](https://pozy.ai/docs/api/setPartnershipAdPermission) | Set partnership permission |
| [`GET /v1/ads/library`](https://pozy.ai/docs/api/searchAdLibrary) | Search the public Ad Library |

Conversation ads and reach and frequency:

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/ads/messaging`](https://pozy.ai/docs/api/createMessagingAd) | Create messaging ad |
| [`POST /v1/ads/call`](https://pozy.ai/docs/api/createCallAd) | Create Click-to-Call ad |
| [`POST /v1/ads/ctwa`](https://pozy.ai/docs/api/createCtwaAd) | Create CTWA ad (deprecated) |
| [`POST /v1/ads/rf-predictions`](https://pozy.ai/docs/api/createRfPrediction) | Create reach-frequency prediction |
| [`GET /v1/ads/rf-predictions/{predictionId}`](https://pozy.ai/docs/api/getRfPrediction) | Get reach-frequency prediction |
| [`DELETE /v1/ads/rf-predictions/{predictionId}`](https://pozy.ai/docs/api/cancelRfReservation) | Cancel reach-frequency booking |
| [`POST /v1/ads/rf-predictions/{predictionId}/reserve`](https://pozy.ai/docs/api/reserveRfPrediction) | Reserve reach-frequency inventory |
