# Ads API

Create, boost, pause and measure paid campaigns on Meta, Google, LinkedIn, TikTok, Pinterest, X, Reddit, Microsoft and OpenAI through one set of endpoints.

import { Tab, Tabs } from 'fumadocs-ui/components/tabs';

The Ads API runs paid campaigns on the ad accounts your users connect. The same endpoints create ads, boost published posts, pause and resume campaigns, build audiences and read spend and results on every network.

## What you can build

- **One-click boosting.** Turn a post your app published into an ad with `POST /v1/ads/boost`. The ad keeps the post's likes and comments.
- **Campaign creation in your product.** `POST /v1/ads/create` builds the campaign, ad set and ad in one call, with budget, targeting and creative.
- **A cross-network ads dashboard.** `GET /v1/ads/campaigns` and `GET /v1/ads/tree` return campaigns from every network with spend, impressions and clicks in one shape.
- **Budget guards and automation.** Pause or resume a campaign with `PUT /v1/ads/campaigns/{campaignId}/status`, or let [ad rules](/guides/ad-rules) do it on a schedule.
- **Audiences from your data.** Upload customer lists to `POST /v1/ads/audiences` and target them on any network that supports it.

## Platforms

<ProductPlatforms product="ads" />

Every network here can create ads, pause and resume them, and report spend and results. The rest depends on what each network's API offers:

| Platform | Boost a post | Audiences | Lead forms | Conversions |
|---|---|---|---|---|
| <PlatformName slug="meta-ads" /> | Yes | Yes | Yes | Yes |
| <PlatformName slug="google-ads" /> | Yes | Yes | | Yes |
| <PlatformName slug="linkedin-ads" /> | Yes | Yes | Yes | Yes |
| <PlatformName slug="tiktok-ads" /> | Yes | Yes | | Yes |
| <PlatformName slug="pinterest-ads" /> | Yes | Yes | | Yes |
| <PlatformName slug="x-ads" /> | Yes | Yes | | |
| <PlatformName slug="reddit-ads" /> | Yes | Yes | | Yes |
| <PlatformName slug="openai-ads" /> | | | | Yes |
| <PlatformName slug="microsoft-ads" /> | | | | |

Operation by operation, the full matrix is on [ad platform support](/platforms/advertising). Each network's folder has its own create, boost and targeting pages.

## How it works

Every network has the same tree: a campaign holds ad sets (ad groups on Google, TikTok and Pinterest, line items on X), and an ad set holds ads.

Zernio stores one record per ad, with its own `_id` and the network's `platformCampaignId`, `platformAdSetId` and `platformAdId`.

- **No `platform` field on create.** The network comes from the `accountId` you send, and `adAccountId` picks the ad account.
- **Campaigns are roll-ups.** A campaign's status and metrics are computed from its ads. Status is one of `active`, `pending_review`, `paused`, `error`, `completed`, `cancelled` or `rejected`.
- **Synced, with live reads on demand.** Zernio refreshes status and metrics in the background, usually within an hour (Google up to about 3 hours). Send `live=true` to read the on/off switches now.
- **Ads made elsewhere are included.** Campaigns built in each network's own ads manager are listed too.

Ads needs the Ads add-on on legacy plans. Usage-based plans include it.

## Your first request

You need an API key and a connected ad account ([connect Meta ads](/platforms/meta-ads/connect)). This lists your campaigns with their spend over the last 90 days. It changes nothing.

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data } = await zernio.adcampaigns.listAdCampaigns({
  query: { profileId: '66a1f0c2a4b9d3e8f1a2b3c4' }
});

console.log(data.campaigns[0].metrics);
```
</Tab>
<Tab value="Python">
```python
from zernio import Zernio

client = Zernio()

listed = client.ad_campaigns.list_ad_campaigns(profile_id="66a1f0c2a4b9d3e8f1a2b3c4")

print(listed["campaigns"][0]["metrics"])
```
</Tab>
<Tab value="curl">
```bash
curl "https://zernio.com/api/v1/ads/campaigns?profileId=66a1f0c2a4b9d3e8f1a2b3c4" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"
```
</Tab>
</Tabs>

Response (`200`):

```json
{
  "campaigns": [
    {
      "platformCampaignId": "120250000000000000",
      "platform": "facebook",
      "campaignName": "Spring sale",
      "status": "active",
      "adCount": 3,
      "budget": { "amount": 75, "type": "daily" },
      "currency": "USD",
      "metrics": { "spend": 412.5, "impressions": 58210, "clicks": 1342, "ctr": 2.31 }
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 1, "pages": 1 }
}
```

Send `fromDate` and `toDate` for another range, and `platform` to keep one network.

## Examples

### Boost a published post

Send the Zernio `postId` of a post you published, a goal and a budget. Zernio builds the campaign, ad set and ad around it ([Meta boost](/platforms/meta-ads/boost)).

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: boosted } = await zernio.adcampaigns.boostPost({
  body: {
    postId: '65f1c0a9e2b5af0012ab34cd',
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    adAccountId: 'act_1234567890',
    name: 'Spring launch boost',
    goal: 'traffic',
    budget: { amount: 40, type: 'daily' },
    targeting: { ageMin: 25, ageMax: 45, countries: ['US', 'CA'] },
    linkUrl: 'https://example.com/spring',
    callToAction: 'LEARN_MORE'
  }
});

console.log(boosted.ad.status);
```
</Tab>
<Tab value="Python">
```python
boosted = client.ad_campaigns.boost_post(
    post_id="65f1c0a9e2b5af0012ab34cd",
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    ad_account_id="act_1234567890",
    name="Spring launch boost",
    goal="traffic",
    budget={"amount": 40, "type": "daily"},
    targeting={"ageMin": 25, "ageMax": 45, "countries": ["US", "CA"]},
    link_url="https://example.com/spring",
    call_to_action="LEARN_MORE",
)

print(boosted["ad"]["status"])
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/ads/boost" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "postId": "65f1c0a9e2b5af0012ab34cd",
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "adAccountId": "act_1234567890",
    "name": "Spring launch boost",
    "goal": "traffic",
    "budget": { "amount": 40, "type": "daily" },
    "targeting": { "ageMin": 25, "ageMax": 45, "countries": ["US", "CA"] },
    "linkUrl": "https://example.com/spring",
    "callToAction": "LEARN_MORE"
  }'
```
</Tab>
</Tabs>

The ad comes back `pending_review` while the network reviews it. A boost is not idempotent, so send an `Idempotency-Key` header before you retry one.

### Create a campaign paused for review

`status: "PAUSED"` creates the campaign held paused, with its ad set and ad switched on. Nothing spends until you resume the campaign ([Meta campaigns](/platforms/meta-ads/create-ads)).

<Tabs items={['Node.js', 'Python', 'curl']}>
<Tab value="Node.js">
```typescript
const { data: created } = await zernio.adcampaigns.createStandaloneAd({
  body: {
    accountId: '66b2e19d8c3f5a7e9d0b1c2d',
    adAccountId: 'act_1234567890',
    name: 'Spring sale - US feed',
    goal: 'traffic',
    budgetAmount: 75,
    budgetType: 'daily',
    headline: 'Spring sale, 30% off',
    body: 'Limited time. Upgrade today.',
    imageUrl: 'https://cdn.example.com/spring.jpg',
    callToAction: 'SHOP_NOW',
    linkUrl: 'https://example.com/spring',
    countries: ['US'],
    status: 'PAUSED'
  }
});

await zernio.adcampaigns.updateAdCampaignStatus({
  path: { campaignId: created.ad.platformCampaignId },
  body: { status: 'active', platform: 'facebook' }
});
```
</Tab>
<Tab value="Python">
```python
created = client.ad_campaigns.create_standalone_ad(
    account_id="66b2e19d8c3f5a7e9d0b1c2d",
    ad_account_id="act_1234567890",
    name="Spring sale - US feed",
    goal="traffic",
    budget_amount=75,
    budget_type="daily",
    headline="Spring sale, 30% off",
    body="Limited time. Upgrade today.",
    image_url="https://cdn.example.com/spring.jpg",
    call_to_action="SHOP_NOW",
    link_url="https://example.com/spring",
    countries=["US"],
    status="PAUSED",
)

client.ad_campaigns.update_ad_campaign_status(
    campaign_id=created["ad"]["platformCampaignId"],
    status="active",
    platform="facebook",
)
```
</Tab>
<Tab value="curl">
```bash
curl -X POST "https://zernio.com/api/v1/ads/create" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "adAccountId": "act_1234567890",
    "name": "Spring sale - US feed",
    "goal": "traffic",
    "budgetAmount": 75,
    "budgetType": "daily",
    "headline": "Spring sale, 30% off",
    "body": "Limited time. Upgrade today.",
    "imageUrl": "https://cdn.example.com/spring.jpg",
    "callToAction": "SHOP_NOW",
    "linkUrl": "https://example.com/spring",
    "countries": ["US"],
    "status": "PAUSED"
  }'

curl -X PUT "https://zernio.com/api/v1/ads/campaigns/120250000000000000/status" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "active", "platform": "facebook" }'
```
</Tab>
</Tabs>

The second call is the resume, once someone has approved the campaign. The campaign id is the `platformCampaignId` from the create response.

## Conversions

Ad networks optimize on the sales and leads your ads bring. Send each event once to `POST /v1/events`, and Zernio forwards it to every network you choose. The [Conversions API](/products/conversions-api) covers it.

## How it behaves

- **`POST /v1/ads/create` publishes live by default.** Omitting `status` creates the ads active. Send `"PAUSED"` to hold them.
- **Budgets are in whole currency units** of the ad account, not cents.
- **Billed per active ad.** Only running or in-review ads count ([ads pricing](/pricing/ads)).

## If it fails

| Error | Cause | Fix |
|---|---|---|
| `400` | A field the network requires is missing or invalid | Fix the field named in `param` |
| `403` | The account has no ads access | Add the Ads add-on, or move to a usage-based plan |
| `404` | The account was disconnected or removed | Read current ids from `GET /v1/accounts` |
| `409` | The account needs reconnection, or the same boost is still running | Reconnect it, or wait and read the result |
| `429` | A rate limit or the team cap on ads reads was hit | Wait and retry ([rate limits](/guides/rate-limits)) |

## Related

- [Ad platform support](/platforms/advertising)
- [List campaigns reference](/ad-campaigns/list-ad-campaigns)
- [Conversions API](/products/conversions-api)
- [Ad rules](/guides/ad-rules)
- [Ads webhooks](/webhooks/ads)
- [Ads pricing](/pricing/ads)