REST API reference

Base URL https://lazyads.ai/api/v1. Every request requires Authorization: Bearer <api_key> and a Growth, Scale, or Enterprise plan; BYOA and Starter keys use the MCP server instead. Download the machine-readable spec from openapi.json (OpenAPI 3.1, importable into Postman, Insomnia, or any generator).

curl https://lazyads.ai/api/v1/campaigns?status=active&limit=20 \
  -H "Authorization: Bearer la_your_api_key"

Conventions

  • JSON in, JSON out. Successful responses wrap the payload in data; list endpoints add pagination (limit, offset, has_more or a next_cursor).
  • Response fields are snake_case; request bodies use the same camelCase names as the dashboard and MCP tools (for example dailyBudgetUsd, platformCampaignId).
  • Writes propagate to the live ad platform (pause, resume, budgets, bids, targeting, keywords, live copy, destination URL, Meta url_tags, ad-set name, and end date) and are recorded in your Activity Log with the API actor. Published edits the platform cannot apply fail instead of silently changing only Lazy Ads.
  • Optional key scopes are listed per group below. Agencies pass X-LazyAds-Client: <client user id> to act inside a client workspace; viewer members are read-only.
  • Rate limits: Growth 100, Scale 500, Enterprise 1000 requests per minute per key, with X-RateLimit-* headers on every response.

Campaigns

scope: campaigns
GET/campaignsList campaigns
POST/campaignsCreate a draft campaign
GET/campaigns/{id}Get a campaign
PATCH/campaigns/{id}Update campaign name or end date
POST/campaigns/{id}/pausePause a campaign
POST/campaigns/{id}/resumeResume a campaign
POST/campaigns/{id}/budgetSet a campaign daily budget
POST/campaigns/{id}/automationToggle Tracking and Management on a campaign

AI campaign builds

scope: campaigns
POST/campaigns/buildStart an AI campaign buildAI plan
GET/campaigns/build/{taskId}Poll an AI campaign buildAI plan

Import live campaigns

scope: campaigns
GET/campaigns/importableList live campaigns you can import
POST/campaigns/importImport live campaigns into Lazy Ads

Ad sets and ads

scope: campaigns
PATCH/ad-sets/{id}Rename an ad set or set its end date
POST/ad-sets/{id}/pausePause an ad set
POST/ad-sets/{id}/resumeResume an ad set
POST/ad-sets/{id}/budgetSet an ad set daily budget
POST/ad-sets/{id}/bidSet a manual ad-set bid (LinkedIn, TikTok, ChatGPT, Snapchat)
POST/ad-sets/{id}/targetingUpdate ad set targeting
POST/ad-sets/{id}/keywordsAdd search keywords
POST/ad-sets/{id}/optimization-eventSwitch the conversion event a Meta ad set optimizes for
PATCH/ads/{id}Update ad copy, destination URL / UTMs, or attach a creative
POST/ads/{id}/pausePause an ad
POST/ads/{id}/resumeResume an ad

Account signals: audiences and conversion events

scope: audiences
GET/account-signalsAccount signals: pixels, conversion events, audiences, and the AI plan
GET/audiencesList custom, lookalike, and retargeting audiences
POST/audiencesCreate a first-party audience
GET/conversion-eventsList pixel / tag conversion events with fire volume

Performance, creatives, and activity

scope: performance · creatives · campaigns
GET/performanceGet performance metrics
GET/creativesList creatives
GET/activityList user activity

Competitors

scope: competitors
GET/competitorsList tracked competitors
POST/competitorsAdd a competitor
GET/competitors/adsSearch competitor ads (Meta Ad Library)AI plan

Account, AI chat, and conversions

scope: connections · notifications · chat · performance
GET/connectionsList platform connections
POST/connectionsConnect ChatGPT
GET/notificationsList recent notifications
POST/chatChat with the LazyAds AI assistantAI plan
POST/eventsIngest first-party conversion events

Webhook subscriptions (REST Hooks)

scope: webhooks
GET/webhooksList webhook subscriptions
POST/webhooksSubscribe to webhook events (REST Hook)
DELETE/webhooksUnsubscribe by id or url
DELETE/webhooks/{id}Unsubscribe a webhook by id
GET/webhooks/eventsList webhook event types with sample payloads

Start and poll an AI campaign build

POST /campaigns/build accepts the same fields as the dashboard builder and MCP build_ai_campaign. Omit platforms to default to Meta. Poll GET /campaigns/build/{taskId} until status is completed or failed; the campaign lands in review (paused) on the platform.

curl -X POST https://lazyads.ai/api/v1/campaigns/build \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "platforms": ["meta", "tiktok"],
    "dailyBudgetUsd": 40,
    "objectiveOverride": "OUTCOME_SALES",
    "creativeMix": "existing",
    "skipCompetitorAnalysis": false,
    "confirmNotPolitical": true,
    "preferredCreativeIds": ["cr_..."],
    "customInstructions": "Avoid medical claims"
  }'
# → 201 { "data": { "task_id": "...", "campaign_id": "...", "builds": [...] } }

curl "https://lazyads.ai/api/v1/campaigns/build/TASK_ID?campaignId=CAMPAIGN_ID" \
  -H "Authorization: Bearer la_your_api_key"
# → { "data": { "status": "running", "output": { "steps": [...] } } }

creativeMix is ai_decide (default), images_only, videos_only, images_and_videos, or existing (reuse the library, no new generation). Other optional fields: preferredPagePostId, socialContentIds, referenceCreativeIds. Live creative performance is on GET /creatives as lifetime_roas, lifetime_spend, and related fields.

Import campaigns you already run

List what is live on each connected account, then link the ones you want. A worker pulls structure and daily stats; trackingEnabled feeds the dashboard and AI analysis, and aiManaged (Management) lets the optimizer edit budgets, bids, and ads. Tracking defaults on for Starter and above and stays off on Free and BYOA. Management is off on every plan when you import, until you turn it on for a campaign (send aiManaged: true or call POST /campaigns/{id}/automation; Starter and above).

curl "https://lazyads.ai/api/v1/campaigns/importable?platform=google,meta" \
  -H "Authorization: Bearer la_your_api_key"

curl -X POST https://lazyads.ai/api/v1/campaigns/import \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "campaigns": [ { "platform": "google", "platformCampaignId": "123456", "trackingEnabled": true } ] }'

Edit a live ad or ad set

PATCH /ads/{id} edits live copy, destination URL, and Meta url_tags / UTMs where supported. Optional fields: name, headline, body, description, callToAction, destinationUrl, urlTags, and creativeId (library attach; a live creative swap only works when a platform creative id already exists). Same action from MCP: update_ad.

PATCH /ad-sets/{id} renames an ad set or sets its end date where supported (name on Meta, TikTok, LinkedIn, Snapchat, and ChatGPT; end date on Meta and TikTok). Send name and/or endDate as YYYY-MM-DD. Same action from MCP: update_ad_set.

Both write Lazy Ads and the live platform when that API supports the field. Published entities fail instead of a silent DB-only write. Check appliedOnPlatform on success; unsupported published edits return 400.

curl -X PATCH https://lazyads.ai/api/v1/ads/AD_ID \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "headline": "Fresh headline",
    "destinationUrl": "https://example.com/offer",
    "urlTags": "utm_source=meta&utm_medium=paid&utm_campaign=spring"
  }'
# → { "data": { "appliedOnPlatform": true, ... }, "message": "Ad updated on the live platform." }

curl -X PATCH https://lazyads.ai/api/v1/ad-sets/ADSET_ID \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Retargeting · week 2", "endDate": "2026-10-31" }'
# → { "data": { "appliedOnPlatform": true, ... } }

Read and shape account signals

GET /account-signals returns what the AI sees before it builds or optimizes: pixels and tags with fire volume, every conversion event with 7-day and 30-day counts, custom / lookalike / retargeting audiences with sizes, and the plan derived from them (the event to optimize for now, the event to graduate to once volume clears the learning threshold, exclusion and warm-audience ids, and audiences worth creating). GET /audiences and GET /conversion-events return the same data split by concern. POST /audiences creates a first-party audience where the platform API allows it (Meta, TikTok, Snapchat, Google); an equivalent existing audience is returned instead of a duplicate. POST /ad-sets/{id}/optimization-event switches a live Meta ad set to a different pixel event or custom conversion.

curl "https://lazyads.ai/api/v1/account-signals?platform=meta" \
  -H "Authorization: Bearer la_your_api_key"
# → { "data": { "pixels": [...], "conversionEvents": [{ "eventName": "Purchase", "count7d": 42, ... }],
#                "audiences": [...], "eventPlan": { "primary": {...}, "graduateTo": {...} }, "audiencePlan": {...} } }

curl -X POST https://lazyads.ai/api/v1/audiences \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "meta", "kind": "lookalike", "seedAudienceId": "23851234567890123", "lookalikeRatio": 0.01 }'
# → 201 { "data": { "created": true, "audience": { "id": "...", "name": "LazyAds · Lookalike 1% ..." } } }

curl -X POST https://lazyads.ai/api/v1/ad-sets/ADSET_ID/optimization-event \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "customEventType": "PURCHASE", "reason": "Purchases cleared 50/week" }'
# → { "data": { "optimizationEvent": {...}, "warnings": [] } }

Send server-side conversions

POST /events takes a batch of up to 100 events. Reuse the browser pixel's eventId so Meta CAPI deduplicates; set googleConversionActionId to also forward Google Enhanced Conversions. User fields are hashed before forwarding.

curl -X POST https://lazyads.ai/api/v1/events \
  -H "Authorization: Bearer la_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "eventName": "Purchase",
      "eventId": "order_8841",
      "value": 129.00,
      "currency": "USD",
      "orderId": "8841",
      "fbclid": "IwAR...",
      "user": { "email": "buyer@example.com" }
    }]
  }'

Errors

Errors return a JSON body with a stable code and the HTTP status:

{
  "error": {
    "message": "The REST API requires the Growth plan or above. Your API key still works with the Lazy Ads MCP server at https://mcp.lazyads.ai/mcp.",
    "code": "PLAN_INSUFFICIENT",
    "status": 403
  }
}
MISSING_AUTH / INVALID_AUTH_FORMAT / INVALID_KEY401 — send Authorization: Bearer la_… with an active key
PLAN_INSUFFICIENT403 — REST needs Growth+; AI endpoints need Starter+ AI access
PERMISSION_DENIED403 — the key was scoped and lacks this endpoint’s scope
CLIENT_ACCESS_DENIED / VIEWER_READ_ONLY403 — agency header without an active grant, or viewer attempted a write
RATE_LIMIT_EXCEEDED429 — wait for Retry-After seconds
INVALID_BODY / INVALID_INPUT400 — the first Zod issue is returned in message
NOT_FOUND404 — the resource is not owned by this account

Same actions from your agent

Every write above is also an MCP tool (for example pause_resume_campaign, update_ad, update_ad_set, set_ad_set_budget, import_platform_campaign, create_audience, build_ai_campaign). If your automation lives in Claude, Cursor, Codex, or Hermes, the MCP server is the shorter path and works on every paid plan.