# Apogee MCP server and REST API: full documentation > Everything of https://www.apogee.ad/developers/ in one Markdown file, generated from the code of the Apogee API. The index: https://www.apogee.ad/developers/llms.txt. - MCP server: https://app.apogee.ad/mcp (Streamable HTTP, stateless; OAuth 2.1 with PKCE, or an API key). 80 tools. - REST API: https://api.apogee.ad/api/v1 (JSON over HTTPS, an API key: `Authorization: Bearer apk_…`). 48 operations. OpenAPI 3.1: https://api.apogee.ad/api/v1/openapi.json - Contract: v1, `Apogee-Version: 2026-10-06`; within v1 changes only add. - Plans: the Pro plan (10,000 credits a month, 5,000 free reads a day) and the Scale plan (30,000 credits a month, 10,000 free reads a day, twice Pro's rate limits). Not in Starter. Pricing: https://www.apogee.ad/en/pricing/ - Platforms: Meta (Facebook, Instagram), TikTok, Snapchat. Each platform's numbers in its own currency: never add them up. - Safety: nothing is created, changed or sent without the user's explicit yes to the whole recap; ads are created paused unless asked otherwise; a prepared plan or action expires after 24 hours and runs once. ## Quickstart 1. Have an Apogee account on the Pro or Scale plan, with your Meta, TikTok or Snapchat ad accounts connected in the app. 2. Connect your assistant to https://app.apogee.ad/mcp: ### Claude (claude.ai and the Claude desktop app) 1. Open **Customize → Connectors**, click **+ Add**, then **Add custom connector**. 2. Name it Apogee, paste `https://app.apogee.ad/mcp` and click **Continue**. 3. Under Authentication choose **Sign in now**, keep the recommended OAuth client (**Use Claude's published identity**), and click **Add**. 4. Log in to Apogee on the page that opens, check what Claude may do, and click **Authorize**. 5. In a chat, turn Apogee on from **+ → Connectors**, then ask. Plans: Free (one custom connector), Pro, Max, Team and Enterprise. Note: Team and Enterprise: an owner adds it first in **Organization settings → Connectors** (Add → Custom → Web); then each member clicks **Connect** on it in Customize → Connectors. ### ChatGPT (chatgpt.com) 1. Turn on developer mode: **Settings → Apps → Advanced settings → Developer mode**. 2. Go to **Settings → Apps → Create**. Name it Apogee, paste `https://app.apogee.ad/mcp` and choose **OAuth** as the authentication. 3. Click **Scan Tools**, log in to Apogee on the page that opens and click **Authorize**, then click **Create**. 4. The app shows with a **Dev** label: use it in a new chat. Plans: Business, Enterprise and Edu, on the web. On Pro, developer mode connects read tools only: you can read your numbers, not launch. Note: ChatGPT cannot send an API key: it connects with Apogee's sign-in only. Note: Workspaces: an admin allows it first in **Workspace settings → Permissions & Roles → Connected Data** (Developer mode / Create custom MCP connectors). Note: OpenAI is moving custom MCP servers to [chatgpt.com/plugins](https://chatgpt.com/plugins) (**+ → Add custom MCP server**). If your menus look different, start there. ### Perplexity (perplexity.ai) 1. Open **Account settings → Connectors**, click **+ Custom connector**, then **Remote**. 2. Name it Apogee, paste `https://app.apogee.ad/mcp`, choose **OAuth** as the authentication and **Streamable HTTP** as the transport. 3. Tick the acknowledgement and click **Add**. 4. Click the Apogee card, log in to Apogee and click **Authorize**. Plans: Pro, Max and Enterprise. On Enterprise, an admin first allows members to add custom connectors (off by default). Note: Perplexity also offers an API Key option but does not document how it sends the key: use OAuth. ### Mistral Vibe (formerly Le Chat, at chat.mistral.ai) 1. Open **Connectors** from the sidebar, click **+ Add Connector**, then the **Custom MCP Connector** tab. 2. Name it `apogee` (no spaces) and paste `https://app.apogee.ad/mcp` as the server URL. 3. Click **Connect**. Vibe detects Apogee's sign-in: log in to Apogee and click **Authorize**. Plans: Adding a connector is for administrators: on Free, Pro and Student, the account owner is one. Organization admins can turn connectors off. ### Claude Code (the terminal) 1. Add the server from your terminal: 2. In Claude Code, type `/mcp`, pick **apogee** and sign in to Apogee in your browser. Terminal: ``` claude mcp add --transport http apogee https://app.apogee.ad/mcp ``` Note: Add `--scope user` to have Apogee in all your projects (default: this project, for you only). With an API key (Terminal, with an API key): ``` claude mcp add --transport http apogee https://app.apogee.ad/mcp \ --header "Authorization: Bearer apk_…" ``` ### Cursor (the editor) 1. Create an API key in [Settings → API](https://app.apogee.ad/home?settings=api) with the scopes Cursor needs. 2. Open `~/.cursor/mcp.json` (create it if needed) and add Apogee with your key: 3. Turn Apogee on from **Customize** in the sidebar, then ask in the agent chat. ~/.cursor/mcp.json: ``` { "mcpServers": { "apogee": { "url": "https://app.apogee.ad/mcp", "headers": { "Authorization": "Bearer apk_…" } } } } ``` Note: Keep the key in `~/.cursor/mcp.json` (yours only), never in a project's `.cursor/mcp.json` that you share. Or write `"Bearer ${env:APOGEE_API_KEY}"` and set that variable. Note: Cursor also signs in with OAuth. Apogee accepts it from the desktop app (its sign-in returns to your computer), not from Cursor's web agents. ### VS Code (GitHub Copilot agent mode) 1. Run **MCP: Add Server** from the Command Palette, choose **HTTP**, paste `https://app.apogee.ad/mcp` and name it `apogee`. 2. Trust the server when VS Code asks. On the first connection a browser window opens: log in to Apogee and click **Authorize**. 3. Open Copilot Chat in **Agent** mode and ask. .vscode/mcp.json (by hand): ``` { "servers": { "apogee": { "type": "http", "url": "https://app.apogee.ad/mcp" } } } ``` Plans: Copilot Free, Pro, Pro+ and Max: nothing to turn on. Copilot Business and Enterprise: an admin turns on the **MCP servers in Copilot** policy (off by default). With an API key (.vscode/mcp.json, with an API key (VS Code asks for it once and stores it)): ``` { "inputs": [ { "type": "promptString", "id": "apogee-key", "description": "Apogee API key", "password": true } ], "servers": { "apogee": { "type": "http", "url": "https://app.apogee.ad/mcp", "headers": { "Authorization": "Bearer ${input:apogee-key}" } } } } ``` ### Gemini CLI (the terminal) 1. Add the server from your terminal: 2. In Gemini CLI, run `/mcp auth apogee` and sign in to Apogee in your browser (it needs a browser on this computer). Run it again when the sign-in expires. Terminal: ``` gemini mcp add --transport http apogee https://app.apogee.ad/mcp ``` Plans: Gemini Code Assist Standard and Enterprise, and paid Gemini API keys. Google moved Google AI Pro, Ultra and free users to Antigravity CLI in June 2026: check its MCP settings. Note: Add `--scope user` to have Apogee in all your projects (default: this project). With an API key (Terminal, with an API key): ``` gemini mcp add --transport http \ --header "Authorization: Bearer apk_…" apogee https://app.apogee.ad/mcp ``` ### Devin Desktop (formerly Windsurf) 1. Create an API key in [Settings → API](https://app.apogee.ad/home?settings=api) with the scopes it needs. 2. Open the MCP config file: in the Cascade panel, the **…** menu at the top right, MCPs section (the file is `~/.config/devin/mcp_config.json`). 3. Add Apogee with your key, save, and turn it on with its toggle: mcp_config.json: ``` { "mcpServers": { "apogee": { "url": "https://app.apogee.ad/mcp", "headers": { "Authorization": "Bearer apk_…" } } } } ``` Plans: Teams and Enterprise: an admin allows MCP servers (and may limit them to an allowlist) in the team settings. Note: Devin Desktop signs in with OAuth too, but does not document where its sign-in returns: use an API key. ### Any MCP client (Streamable HTTP) 1. Server URL `https://app.apogee.ad/mcp`, transport **Streamable HTTP**. 2. Sign-in: OAuth 2.1 with PKCE, discovered from the server, with Dynamic Client Registration. Apogee accepts it from the assistants of this guide and from your own computer (`http://localhost`); any other client uses an API key. 3. API key: create one in [Settings → API](https://app.apogee.ad/home?settings=api) with only the scopes the client needs, and send `Authorization: Bearer apk_…`. A typical client configuration: ``` { "mcpServers": { "apogee": { "type": "http", "url": "https://app.apogee.ad/mcp", "headers": { "Authorization": "Bearer apk_…" } } } } ``` Note: The key names differ by client (`url`, `serverUrl`, `httpUrl`…): check your client's MCP settings. 3. Or call the REST API: create a key in the app (Settings → Connections → API → Create key; shown once), then: ```bash export APOGEE_API_KEY="apk_…" curl https://api.apogee.ad/api/v1/me -H "Authorization: Bearer $APOGEE_API_KEY" ``` How a launch works (MCP): 1. Pick the ad account: `list_ad_accounts` (with `platform` for TikTok or Snapchat). The assistant asks which one when there are several. 2. The interview: `get_launch_questions` returns the questions the app asks before every launch, in order, with the choices read from your account and a recommended answer. The assistant asks them one topic at a time, in your language, and never answers for you: starting point, campaign, upload structure, creatives, nomenclature, ads and formats, identity, audience, tracking, destination, texts, Advantage+ enhancements, catalog, budget and launch status. 3. The creatives: Files on your computer: `get_upload_urls`, an upload per file, `confirm_uploads`. A Frame.io, Dropbox or Google Drive link: `import_link` + `import_status`. A Drive folder: `list_drive_folder`. Then the interview reads the file names with your naming convention and proposes the ads: one creative in 9:16, 4:5 and 1:1 is ONE ad. Snapchat and TikTok take videos and images (on TikTok an image runs as a carousel), from Apogee storage: a Drive file goes through `import_link` first. On TikTok, an existing post can also run as a Spark Ad (`list_tiktok_posts`). 4. The texts: A primary text, a headline and a description per ad (from each video's transcript when it can), in the language of the targeted market, for you to validate. A copied ad brings its own. 5. The recap: `prepare_launch` creates nothing. It refuses while a question is open (`ready: false` and what to ask), applies only the app's documented defaults and lists them, checks with Meta (`validate_only`) the ads made from existing posts, and returns the whole recap with a `plan_id`. 6. Your explicit yes: The assistant shows the whole recap (settings, ads and files, texts, Advantage+ choices, defaults applied) and waits for your yes. Any change means a new `prepare_launch`. 7. The launch: `launch(plan_id, confirm: true)`, then `launch_status` until it is finished. Ads are created paused unless you chose otherwise. A plan launches once, within 24 hours. Through the REST API: upload or import the creatives, `POST /ad-accounts/{acc}/launch-questions`, `POST /launch-plans` (422 missing_answers lists what is open; 201 returns the recap), then after the yes `POST /launch-plans/{id}/confirm` (202) and `GET /launches/{id}` until finished. ## Authentication - API keys (`apk_…`): made in the app (Settings → Connections → API), shown once, only their hash stored. Scopes chosen at creation, expiry 30, 90, 365 days or never, 10 active keys at most. They work on the REST API and the MCP server. - OAuth 2.1 for the MCP server: authorization code with PKCE (S256), Dynamic Client Registration or client metadata documents, discovery from the 401 of https://app.apogee.ad/mcp (resource metadata https://app.apogee.ad/.well-known/oauth-protected-resource/mcp). Access tokens (`apo_at_…`) last 1 hour, refresh tokens 60 days. Redirects accepted: claude.ai, claude.com, chatgpt.com, www.perplexity.ai, enterprise.perplexity.ai, chat.mistral.ai, console.mistral.ai, vscode.dev, and http://localhost (any port). - Scopes: - `read`: Read ad accounts, campaigns, ad sets, ads, insights, and follow imports and launches. - `launch`: Import and upload creatives, prepare and launch ads, create a campaign. - `actions`: Pause, activate, change budgets and targeting, duplicate, create ad sets and ads from existing posts. - Every request checks the plan: 403 `upgrade_required` with `feature`, `required_plan` and `current_plan` when the plan does not include it. ## Credits - 1 credit = 0.01 of the billing currency. Pro includes 10,000 credits a month, Scale 30,000; a 7-day trial a quarter of them. The month is the calendar month in UTC; no rollover. A workspace pools its seats' credits. - Reads (lists, numbers, statuses, launch questions, plans and actions prepared) are free up to 5,000 a day on Pro and 10,000 on Scale, then 1 credit each up to 3 times the cap, then 429 `daily_read_cap` until midnight UTC. - What costs credits: - read: 0 credits per read past the daily free reads - platform_action: 2 credits per change - ad_created: 15 credits per ad created - bridge_ai_texts: 120 credits per transfer to Meta - comment_sync: 10 credits per sync - website_read: 150 credits per page read (0 if cached) - Credit packs: 2,500 credits for 25, 10,000 credits for 90, 50,000 credits for 425 (billing currency, excl. tax), valid 12 months, spent after the month's credits. - A failed call and a replayed answer cost nothing. Past the credits: 402 `credits_exhausted` (with `buy_url`); reads go on. ## Rate limits - Per 15 minutes, Pro / Scale: REST reads 600 / 1200, writes 120 / 240, confirmations 30 / 60; MCP requests 600 / 1200. Per key, and per account (every key together). Headers: `RateLimit`, `RateLimit-Policy`; 429 with `Retry-After`. - Meta budget of the account: 1,800 units per 15 minutes, 900 per ad account (a launch interview or plan 15, any other Meta call 1). TikTok and Snapchat: 600 calls per 15 minutes per account and platform, 300 per ad account. Meta's throttling: 429 `meta_rate_limited`, 2 minutes without calling Meta. - Daily caps (24 h): 2,000 units per API key or assistant connection (read 1, change 2, AI 5, launch or transfer 10, status poll 0); 25 launches and transfers per ad account. - Uploads: 50 files per request, 500 MB per file; 2,000 files and 100 GB a day on Pro, twice on Scale. - A daily budget above 500 in the ad account's currency is refused (422 `budget_above_cap`). ## Errors Errors are `application/problem+json` (RFC 9457): `type`, `title`, `status`, `detail`, `code` (stable), `request_id`, and when relevant `errors`, `missing`, `required_scope`, `feature`, `required_plan`, `current_plan`, `retry_after`, `buy_url`, `meta`. On the MCP server, a refused tool call is a result with `isError: true` and the reason in words. - `idempotency_key_required` (400): Idempotency-Key header required. This write needs an `Idempotency-Key` header: 1 to 255 visible ASCII characters, for example a UUID. Reuse the same key when you retry the same request. - `not_supported` (400): Not supported. This combination is not supported (yet). `detail` says what is not. - `request_refused` (400): Request refused. Apogee refused the request for the reason in `detail`, the same message the app shows. Fix what it says before you retry: the same request is refused the same way. - `validation_failed` (400): Validation failed. The request does not match what the route accepts. `errors` lists each refused field (`param`), with a code and a message. Unknown fields are refused too: send only the documented ones. - `invalid_api_key` (401): Invalid API key. The `Authorization` header is missing, or the key is unknown, revoked or expired: Apogee answers the same for all three. Send `Authorization: Bearer apk_…` with an active key from Settings → Connections → API. OAuth tokens (`apo_at_…`) work on the MCP server only. - `cost_ceiling_reached` (402): The account's monthly safety limit is reached. A monthly safety limit of the account was reached. Write to hello@apogee.ad with the `request_id`. - `credits_exhausted` (402): This month's API credits are used. This month's credits and every credit pack are used. Requests that cost credits are refused until the 1st of next month (UTC); reads go on within their daily cap. `buy_url` opens the page that sells credit packs. - `free_daily_limit_reached` (402): The free plan's MCP credits of the day are used. A free Apogee account used its MCP credits of the day. They come back at midnight UTC; the message says in how many hours and links to the plans, to subscribe and keep going. Calls that cost nothing, such as who am I or following a job, still answer. - `member_cap_reached` (402): The member's monthly credits are used. The workspace owner set a monthly credit cap for this member, and it is used. Ask the owner to raise it (Workspace → Usage). Reads go on within their daily cap. - `payment_failed` (402): The account's last payment failed. The account's last payment failed, for example the first charge at the end of a 7-day trial: the features of its plan are paused. Update the payment method in Settings → Billing, then retry. - `plan_required` (402): A paid plan is required. This needs a paid Apogee plan. Subscribe in the app, then retry. - `account_not_connected` (403): Ad account not connected. The TikTok or Snapchat ad account is not one this Apogee account connected. Connect it in the app (Settings → Workspace → Ad accounts), then find its id with `GET /ad-accounts?platform=…`. - `forbidden` (403): Forbidden. The request is not allowed for this account. Check that the ad account, page or object belongs to the Apogee account of the key. - `insufficient_scope` (403): Insufficient scope. The key lacks the scope this route needs: `required_scope` names it. Create a key with that scope (a key's scopes are chosen when it is created). - `meta_permission_denied` (403): Meta refused the access. Meta refused the access with this Apogee account's Facebook connection (no role on the ad account, the page or the business). Fix the access in Meta Business Settings, then log in to Apogee again. - `platform_permission_denied` (403): TikTok or Snapchat refused the access. TikTok or Snapchat refused the access: a role or a permission is missing on the ad account. Fix it on the platform, then reconnect it in the app. - `upgrade_required` (403): The Apogee plan does not include this feature. The account's plan does not include this feature: `feature` names it, `required_plan` is the plan that includes it, `current_plan` the account's. The key works again as soon as the plan includes it. - `not_found` (404): Not found. No such object, or not one of this account's. A wrong path answers it too: check the reference. - `method_not_allowed` (405): Method not allowed. The path exists with another HTTP method: the `Allow` header lists them. - `action_already_ran` (409): Action already ran. This action already ran: an action runs once. Prepare a new one to do it again. - `idempotency_in_progress` (409): A request with this Idempotency-Key is in progress. A request with this `Idempotency-Key` is still running. Retry the same request after `Retry-After` seconds: you get its answer. - `plan_already_launched` (409): Plan already launched. This launch plan already ran: a plan launches once. Prepare a new plan to launch again. - `same_error_again` (409): The last launches failed with the same error. MCP only: the last launches on this ad account failed with the same error. Fix its cause first, then launch with `previous_error_fixed: true`. - `too_many_keys` (409): Too many API keys. The account has 10 active API keys, the most it can have. Revoke one in Settings → Connections → API first. - `action_expired` (410): Action expired. The action was prepared more than 24 hours ago. Prepare it again. - `plan_expired` (410): Plan expired. The launch plan was prepared more than 24 hours ago. Prepare it again: the recap is read again too. - `drive_not_connected` (412): Google Drive connection needed. Google Drive is not connected to this Apogee account, or the folder is not shared with it. Connect Drive in the app, or share the folder as "Anyone with the link". - `meta_not_connected` (412): Facebook connection needed. The Facebook connection of this Apogee account is missing or expired: log in to app.apogee.ad again. It never means the API key is wrong (that is a 401). - `platform_not_connected` (412): TikTok or Snapchat connection needed. The TikTok or Snapchat connection was revoked or expired: reconnect it in the app. - `payload_too_large` (413): Payload too large. The JSON body is larger than 2 MB. Send files with `POST /uploads`, never inside a body. - `budget_above_cap` (422): Budget above the safety cap. The daily budget is above Apogee's safety cap (500 a day in the ad account's currency). Lower it, or change the budget in the app. - `idempotency_key_reused` (422): Idempotency-Key reused with another request. This `Idempotency-Key` was already used with another request (another path or body). Use a new key for a new request. - `launch_refused` (422): Launch refused. Apogee's checks or the platform's pre-flight refused the launch: `detail` says why. Nothing was created. - `missing_answers` (422): The launch plan has open questions. The launch plan still has open questions: `missing` lists each one, with what it needs and how to answer it. Nothing was saved: answer them and send the whole plan again. - `daily_read_cap` (429): Daily reads used. The account made three times its daily free reads today. Reads are refused until midnight UTC. - `meta_rate_limited` (429): Meta is rate limiting this account. Meta is throttling the ad account, or the whole Facebook connection. Apogee stops calling Meta for it for 2 minutes instead of retrying: wait `Retry-After` seconds. - `platform_rate_limited` (429): TikTok or Snapchat is rate limiting this account. TikTok or Snapchat is throttling the account. Wait `Retry-After` seconds. - `quota_exceeded` (429): Daily quota reached. A 24-hour quota is reached: the units of one API key or assistant connection, the launches and transfers of one ad account, or the comment idea runs of the account. `Retry-After` says when it frees up. - `rate_limited` (429): Too many requests. Too many requests in the window: the rate limits of the plan, or the Meta, TikTok or Snapchat budget of the account. Wait `Retry-After` seconds. - `internal_error` (500): Internal error. Apogee failed on its side. It was recorded: retry later, and give the `request_id` to support if it keeps failing. - `drive_error` (502): Google Drive error. Google Drive did not answer properly. Retry later. - `launch_failed` (502): Launch failed. The launch failed on the platform: `detail` says why. Check the ad account in Ads Manager before you launch again. - `meta_error` (502): Meta refused the request. Meta refused the request: `detail` gives Meta's message and `meta.code` its error code. - `platform_error` (502): TikTok or Snapchat refused the request. TikTok or Snapchat refused the request: `detail` gives their message. - `upstream_error` (502): Upstream error. A service Apogee relies on failed. Retry later; give the `request_id` to support if it keeps failing. - `ai_paused` (503): AI features paused until next month. Apogee's AI features are paused for the API and the MCP until the 1st of next month. Everything else works, and the app keeps them. - `feature_not_ready` (503): This feature is not available yet. This feature is not available on this server yet. - `service_disabled` (503): Paused for maintenance. The API and the MCP are paused for maintenance: everything, or only what writes (reads go on). Retry later; the Apogee app keeps working. - `meta_timeout` (504): Meta did not answer in time. Meta did not answer in time. Retry; before you repeat a write, check Ads Manager that it did not go through. ## Idempotency Writes that create something take an `Idempotency-Key` header (1 to 255 visible ASCII characters, e.g. a UUID). Same key and same request within 24 hours: the first successful answer again, with `Idempotent-Replayed: true`. Same key, other request: 422 `idempotency_key_reused`. While the first runs: 409 `idempotency_in_progress` with `Retry-After`. A failed request frees its key. Required on: `createImport`, `createLaunchPlan`, `confirmLaunchPlan`, `createAction`, `confirmAction`, `createBridgeTransfer`, `confirmBridgeTransfer`. ## MCP tools 80 tools in 10 groups. Each one: its page, what it does, its effect, scope, plans and credits, its parameters (JSON Schema of the server's tools/list, as a list) and a tools/call example. ### Account Who is connected, and the ad accounts to work on. #### whoami https://www.apogee.ad/developers/mcp/whoami/ Who am I. The Apogee account this connection acts as, the credential used (an assistant's connection or an API key) and its scopes. The Apogee account this connection acts as, the credential used (an AI assistant's connection or an API key) and what it may do (scopes). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get the account and key (`GET /me`) Parameters: - None (empty arguments). Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "whoami", "arguments": {} } } ``` #### list_ad_accounts https://www.apogee.ad/developers/mcp/list-ad-accounts/ List ad accounts. The ad accounts connected to Apogee. List the ad accounts connected to Apogee. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List ad accounts (`GET /ad-accounts`) Parameters: - `platform` (string, optional): Ad platform, default meta. one of `meta`, `tiktok`, `snapchat`. - `search` (string, optional): Filter on the name. up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_ad_accounts", "arguments": { "platform": "meta", "search": "spring" } } } ``` ### Campaigns, ad sets and ads Names and ids, no metrics: to find where to launch and what to copy. #### list_campaigns https://www.apogee.ad/developers/mcp/list-campaigns/ List campaigns. The campaigns of an ad account: objective, CBO budget, status. Archived ones on request. List the campaigns of an ad account (id, name, objective, CBO budget, status). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List campaigns (`GET /ad-accounts/{acc}/campaigns`) Parameters: - `ad_account_id` (string, required): Ad account id (list_ad_accounts). matches `^[\w-]{1,64}$`. - `platform` (string, optional): Ad platform, default meta. one of `meta`, `tiktok`, `snapchat`. - `search` (string, optional). up to 100 characters. - `include_archived` (boolean, optional) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_campaigns", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_adsets https://www.apogee.ad/developers/mcp/list-adsets/ List ad sets. The ad sets (TikTok ad groups, Snapchat ad squads) of an ad account, or of one campaign. List the ad sets of an ad account, optionally of one campaign. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List ad sets (`GET /ad-accounts/{acc}/adsets`) Parameters: - `ad_account_id` (string, required): Ad account id (list_ad_accounts). matches `^[\w-]{1,64}$`. - `platform` (string, optional): Ad platform, default meta. one of `meta`, `tiktok`, `snapchat`. - `campaign_id` (string, optional). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `search` (string, optional). up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_adsets", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_ads https://www.apogee.ad/developers/mcp/list-ads/ List ads. The ads of an ad account, a campaign or an ad set (200 at most). List the ads of an ad account, optionally of one campaign or ad set (e.g. to pick the ad whose page, texts, URL and CTA are copied). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List ads (`GET /ad-accounts/{acc}/ads`) Parameters: - `ad_account_id` (string, required): Ad account id (list_ad_accounts). matches `^[\w-]{1,64}$`. - `platform` (string, optional): Ad platform, default meta. one of `meta`, `tiktok`, `snapchat`. - `campaign_id` (string, optional). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_id` (string, optional). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `search` (string, optional). up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_ads", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### get_latest_in_campaign https://www.apogee.ad/developers/mcp/get-latest-in-campaign/ Latest ad set and ad of a campaign. The newest ad set and ad of a campaign, by creation time: "copy the latest ad set and ad of campaign X". Their ids go in prepare_launch `copy_adset_from` and `copy_ad_from`. The most recently created ad set and ad of a campaign (ids, names, creation times): "copy the latest ad set and ad of campaign X". Use them as prepare_launch copy_adset_from / copy_ad_from (get_templates shows what each one holds; changes go in adset_settings / ad_settings). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get latest in campaign (`GET /ad-accounts/{acc}/campaigns/{id}/latest`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, required): A campaign (list_campaigns). matches `^\d{1,40}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_latest_in_campaign", "arguments": { "ad_account_id": "act_1234567890", "campaign_id": "120200000000000001" } } } ``` #### get_templates https://www.apogee.ad/developers/mcp/get-templates/ Show what a copy would take. What a copy would take: from an ad set its targeting, event, bid and budget; from an ad its page, Instagram, texts, URL and call to action. Show the settings that would be copied from a Meta ad set (targeting, event, bid, budget) and/or ad (page, Instagram, texts, URL, CTA). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get an ad set template (`GET /adsets/{id}/template`), Get ad template (`GET /ads/{id}/template`) Parameters: - `adset_id` (string, optional): An ad set (list_adsets). matches `^\d{1,40}$`. - `ad_id` (string, optional): An ad (list_ads). matches `^\d{1,40}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_templates", "arguments": { "adset_id": "120200000000000002", "ad_id": "120200000000000003" } } } ``` #### list_launches https://www.apogee.ad/developers/mcp/list-launches/ List launches. Your launches (the app's History), newest first: status, files, dates. The user's launches (the app's History), newest first: status (processing, completed, failed, interrupted), files, dates. Optionally of one ad account. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List launches (`GET /launches`) Parameters: - `ad_account_id` (string, optional). up to 40 characters. - `status` (string, optional). one of `processing`, `completed`, `failed`, `interrupted`. - `limit` (integer, optional): Default 25. 1 to 100. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_launches", "arguments": { "ad_account_id": "act_1234567890", "status": "processing" } } } ``` ### Pages, pixels, audiences and posts Meta only: the choices the launch interview offers. #### list_pages https://www.apogee.ad/developers/mcp/list-pages/ List Facebook pages. The Facebook pages usable with an ad account, with their linked Instagram account. Facebook pages (and their linked Instagram account) usable with a Meta ad account. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List pages (`GET /ad-accounts/{acc}/pages`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_pages", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_instagram_accounts https://www.apogee.ad/developers/mcp/list-instagram-accounts/ List a page's Instagram accounts. The Instagram account linked to a page. The Instagram accounts an ad of a Facebook page can run with. With ad_account_id: the page's own first, then the ones assigned to that ad account in Business Settings, the one a launch uses marked recommended (a page without its own Instagram account runs with the ad account's, never as "Use Facebook Page"). Without it: the page's linked account only (the account whose posts list_posts reads). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List instagram accounts (`GET /pages/{page}/instagram-accounts`) Parameters: - `page_id` (string, required): A Facebook page (list_pages). matches `^\d{1,40}$`. - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_instagram_accounts", "arguments": { "page_id": "100000000000001" } } } ``` #### list_pixels https://www.apogee.ad/developers/mcp/list-pixels/ List pixels. The pixels of an ad account. Meta pixels of an ad account. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List pixels (`GET /ad-accounts/{acc}/pixels`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_pixels", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_lead_forms https://www.apogee.ad/developers/mcp/list-lead-forms/ List instant forms. The instant forms of a page. Instant forms (lead forms) of a Facebook page. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List lead forms (`GET /pages/{page}/lead-forms`) Parameters: - `page_id` (string, required): A Facebook page (list_pages). matches `^\d{1,40}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_lead_forms", "arguments": { "page_id": "100000000000001" } } } ``` #### list_custom_audiences https://www.apogee.ad/developers/mcp/list-custom-audiences/ List custom audiences. Custom and lookalike audiences, to include or exclude. Custom and lookalike audiences of an ad account (for adset_settings.custom_audience_ids / excluded_audience_ids). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List custom audiences (`GET /ad-accounts/{acc}/custom-audiences`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `search` (string, optional). up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_custom_audiences", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### search_targeting https://www.apogee.ad/developers/mcp/search-targeting/ Search targeting. Countries, regions and cities, interests, behaviors, life events and languages, with the keys a launch takes. Search Meta targeting: countries, locations (regions and cities: their key goes in adset_settings.regions / cities), interests, behaviors, demographics (life events), locales (languages). Use the ids / keys in prepare_launch adset_settings. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Search targeting (`GET /targeting/search`) Parameters: - `type` (string, required). one of `countries`, `locations`, `interests`, `locales`, `behaviors`, `demographics`. - `q` (string, optional): The search (required except for behaviors and demographics, where it filters the list). up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_targeting", "arguments": { "type": "countries" } } } ``` #### list_posts https://www.apogee.ad/developers/mcp/list-posts/ List posts usable as ads. Existing posts a new ad can reuse with their likes and comments: the posts behind your ads, a page's posts, or its Instagram posts. Existing posts a new ad can reuse, keeping their likes, comments and shares (create_ad_from_post): - source "ads" (default): the posts behind the ad account's ads (dark posts included), with the ads using each one: the usual "reuse my winning ad's post"; - source "facebook": the page's published posts (page_id); - source "instagram": the posts of the page's Instagram account (page_id + instagram_account_id). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List ad posts (`GET /ad-accounts/{acc}/posts`), List page posts (`GET /pages/{page}/posts`) Parameters: - `source` (string, optional). one of `ads`, `facebook`, `instagram`. - `ad_account_id` (string, optional): source ads. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): source ads: only this campaign's ads. matches `^\d{1,40}$`. - `page_id` (string, optional): source facebook / instagram. matches `^\d{1,40}$`. - `instagram_account_id` (string, optional): source instagram (list_instagram_accounts). matches `^\d{1,40}$`. - `limit` (integer, optional). 1 to 50. - `cursor` (string, optional): next_cursor of the previous page. up to 500 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_posts", "arguments": { "source": "ads", "ad_account_id": "act_1234567890" } } } ``` ### Creatives Bring the files in before a launch. Nothing reaches Meta at this point. #### import_link https://www.apogee.ad/developers/mcp/import-link/ Import creatives from a link. Imports the creatives behind a Frame.io, Dropbox or Google Drive link (a file or a folder) through the account connected to Apogee. A public Frame.io share works without a Frame.io account when downloads are allowed; a password-protected share does not. Import the creatives behind a link into Apogee: Frame.io (project, folder or file address as next.frame.io shows it, or a legacy app.frame.io asset, through the Frame.io account connected to Apogee), Dropbox (shared file or folder) or Google Drive (file or folder). A public Frame.io share link (f.io/..., next.frame.io/share/...) works too, without any Frame.io account, when the sender left downloads on: reading it takes 20 to 30 s. A password-protected share is not supported: ask for the files instead. Returns import jobs (the first 200 files of a link at most: truncated says when there were more; they count in the day's upload budget); call import_status until every job is COMPLETED, then pass each job's `ref` in prepare_launch files. - Effect: Changes Apogee only (imports, uploads, settings, comment syncs, AI drafts): nothing on the ad account. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create import (`POST /imports`) Parameters: - `url` (string, required). up to 2,048 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "import_link", "arguments": { "url": "https://www.dropbox.com/scl/fo/example0000/nordlicht-creatives" } } } ``` #### import_status https://www.apogee.ad/developers/mcp/import-status/ Follow an import. The progress of an import. Each finished file gets a `ref` for the launch. Progress of the jobs started by import_link. A COMPLETED job has a `ref` (its URL in Apogee storage) usable in prepare_launch files. Formats are not detected here: read them from the file names, or ask. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get import (`GET /imports/{id}`) Parameters: - `source` (string, required): The source returned by import_link. one of `frameio`, `google_drive`, `dropbox`. - `job_ids` (array of strings, required). 1 to 500 items; each: up to 64 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "import_status", "arguments": { "source": "frameio", "job_ids": [ "120200000000000001" ] } } } ``` #### list_drive_folder https://www.apogee.ad/developers/mcp/list-drive-folder/ List a Google Drive folder. The videos and images of a Google Drive folder, subfolders included, each with its format. Inventory the videos and images of a Google Drive folder (link or id), subfolders included. A folder shared as "Anyone with the link" needs no connection; a private one is read through the Drive connected to Apogee when that account can open it. Each file has a `ref` to pass to prepare_launch; its format (9:16, 4:5, 1:1, 16:9) is detected from Drive. One listing stops at 2000 files or 200 folders (truncated: list a subfolder for the rest). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List drive folder (`GET /drive-folders`) Parameters: - `url` (string, required): Drive folder link or id. up to 2,048 characters. - `recursive` (boolean, optional) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_drive_folder", "arguments": { "url": "https://www.dropbox.com/scl/fo/example0000/nordlicht-creatives" } } } ``` #### get_upload_urls https://www.apogee.ad/developers/mcp/get-upload-urls/ Get upload URLs. For files on your computer: one signed upload URL per file (50 per call, 500 MB per file, valid 1 hour). The assistant uploads each file with an HTTP `PUT`. For creatives on the user's computer: returns one signed upload URL per file (max 50 per call, 500 MB per file, valid 1 hour), for a Meta, TikTok or Snapchat launch (ad_account_id: the account the files are for). Upload each file with: curl -sS -X PUT -H "Content-Type: " --data-binary @"" "" then call confirm_uploads with the returned paths (it checks each file arrived and is not empty), and pass each `ref` in prepare_launch files. Detect each file's format (width/height, e.g. with ffprobe) yourself: 9:16, 4:5, 1:1 or 16:9. - Effect: Changes Apogee only (imports, uploads, settings, comment syncs, AI drafts): nothing on the ad account. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create uploads (`POST /uploads`) Parameters: - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `files` (array of objects, required). 1 to 50 items. - `name` (string, required): File name with its extension. 1 to 255 characters. - `size` (integer, required): Bytes (required: e.g. stat -f%z, or wc -c). > 0; ≤ 524,288,000. - `mimeType` (string, optional). up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_upload_urls", "arguments": { "ad_account_id": "act_1234567890", "files": [ { "name": "NORDLICHT_RAIN_HOOK1_9x16.mp4", "size": 48213112, "mimeType": "video/mp4" } ] } } } ``` #### confirm_uploads https://www.apogee.ad/developers/mcp/confirm-uploads/ Check uploaded files. Checks each uploaded file arrived and is not empty. Check the files uploaded with get_upload_urls: each one present in Apogee storage and not empty (0 bytes = upload it again). Returns each file's ref (for prepare_launch files) and size. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Confirm uploads (`POST /uploads/confirm`) Parameters: - `paths` (array of strings, required): The paths (or refs) get_upload_urls returned. 1 to 50 items; each: up to 2,048 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "confirm_uploads", "arguments": { "paths": [ "120200000000000001" ] } } } ``` ### Launch The interview, the recap, your yes, then the launch. #### get_launch_questions https://www.apogee.ad/developers/mcp/get-launch-questions/ Launch interview. The questions the app asks before every launch, in order, with the choices read from your account and a recommended answer (TikTok's and Snapchat's own with `platform`). Called again with the files (it reads their names with your naming convention and proposes the ads) and with the answers so far. The launch interview, Apogee's way: every question the Apogee app asks before a launch, in order, each with the choices read from the user's own account (ad sets and ads with their spend, saved templates, campaigns, pages, pixels and events, catalogs, naming conventions) and the recommended answer. Call it first, then ask the questions whose status is to_ask ONE TOPIC AT A TIME, in the user's language, showing the choices. Never answer for the user. Call it again with files once the creatives are known (nomenclature read + proposed ads grouped by format), and with the answers so far to get the choices that depend on them (a pixel's events, a page's forms, a catalog's product sets, what a copied ad set / ad holds). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get launch questions (`POST /ad-accounts/{acc}/launch-questions`) Parameters: - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `platform` (string, optional): Default meta. tiktok / snapchat: the same interview and arguments where they mean the same, their own settings under `tiktok` / `snapchat`, groups made by hand under `adgroups` / `adsquads`. one of `meta`, `tiktok`, `snapchat`. - `files` (array of objects, optional). up to 500 items. - `name` (string, required): File name with its extension. 1 to 255 characters. - `ref` (string, optional): drive: or the URL of the file in Apogee storage. up to 2,048 characters. - `mimeType` (string, optional). up to 100 characters. - `kind` (string, optional). one of `video`, `image`. - `format` (string, optional): Measured format (list_drive_folder, ffprobe): 9:16, 4:5, 1:1, 16:9. up to 10 characters. - `folder` (string, optional): Subfolder (list_drive_folder). up to 500 characters. - `durationS` (number, optional): Length in seconds (list_drive_folder durationS, ffprobe): files of one length in different formats are flagged as maybe one creative. 0 to 36,000. - `answers` (one of, optional): The answers so far, with prepare_launch's argument names - Shape `option 1`: - `campaign_id` (string or integer or null, optional): Question "campaign". matches `^\d{1,40}$`. - `existing_adset_ids` (array of strings or integers or null, optional). matches `^\d{1,40}$`. - `copy_adset_from` (string or integer or null, optional): An ad set to duplicate. matches `^\d{1,40}$`. - `copy_ad_from` (string or integer or null, optional): An ad to duplicate. matches `^\d{1,40}$`. - `adset_settings` (object or null, optional) - `pixel_id` (string or integer or null, optional): A pixel. matches `^\d{1,40}$`. - `custom_conversion_id` (string or integer or null, optional): A custom conversion. matches `^\d{1,40}$`. - `application_id` (string or integer or null, optional): app_installs. matches `^\d{1,40}$`. - `ad_settings` (object or null, optional) - `page_id` (string or integer or null, optional): A Facebook page. matches `^\d{1,40}$`. - `instagram_id` (string or integer or null, optional): An Instagram account. matches `^\d{1,40}$`. - `lead_form_id` (string or integer or null, optional): An instant form. matches `^\d{1,40}$`. - `partnership` (object or null, optional) - `creator_ig_id` (string or integer or null, optional): An approved creator. matches `^\d{1,40}$`. - `creator_fb_page_id` (string or integer or null, optional): The creator's Facebook page (alone: a Facebook creator). matches `^\d{1,40}$`. - `products` (object or null, optional) - `catalog_id` (string or integer or null, optional): A catalog. matches `^\d{1,40}$`. - `product_set_id` (string or integer or null, optional): A product set. matches `^\d{1,40}$`. - `ads` (array of objects or null, optional) - `source_ad_id` (string or integer or null, optional): An existing ad to reuse with its post. matches `^\d{1,40}$`. - `adset_variants` (array of objects or null, optional) - `source_campaign_id` (string or integer or null, optional): Lists the existing ads of this campaign only. matches `^\d{1,40}$`. - `nomenclature` (object or null, optional) - `config_id` (string or null, optional). up to 64 characters. - `grouping_mode` (string, optional): The app's switch: "names" reads the file names with a saved convention, "by_hand" groups the files by hand (formats of one creative linked by name, every other file its own ad). Default: names only when a convention reads every file, else by hand. one of `by_hand`, `names`. - Shape `option 2`: - `campaign_id` (string or null, optional): Question "campaign". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `copy_adset_from` (string or null, optional): An ad group / ad squad to copy, or "latest". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `copy_ad_from` (string or null, optional): An ad to copy, or "latest". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `existing_adset_ids` (array of strings or null, optional). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_launch_questions", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### prepare_launch https://www.apogee.ad/developers/mcp/prepare-launch/ Prepare a launch. Prepares the launch without creating anything: the whole recap and a `plan_id`, valid 24 hours. Refuses while a question is open. Starts from scratch, a copied ad set and ad, or saved templates; the ads are new files (grouped by your naming convention or by hand), or existing ads reused with their post ID. Prepare a launch from the interview's answers (get_launch_questions) WITHOUT creating anything. Returns the full recap and a plan_id, valid 24 hours. Refuses a plan with an open question: answers ready:false + missing (question ids) + what to ask. Ask those, then call it again. It applies only the Apogee app's documented defaults and lists them in defaults_applied. Files: drive: refs, or your own files in Apogee storage (get_upload_urls + confirm_uploads, import_link + import_status); each one is checked present. Advantage+ enhancements: only those in advantage_plus.opt_in are ON, every other one is sent OFF. Existing ads (Duplicate with post ID, likes and comments kept): ads[].source_ad_id instead of files; no nomenclature, texts, identity, destination, Advantage+ or catalog. New ad sets: structure all_in_one + adset_variants (one cost cap / bid / ROAS floor each, copy_adset_from for the audience and conversion; adset_variants[].ads = each one's own ads); existing ad sets: insert_in_adset. The plan is checked with Meta (validate_only, nothing created) before it is saved. By hand: ads[].files = the formats of one creative (one file per format, never a video with an image); adsets = the ads arranged into new ad sets, each ad in exactly one. - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create launch plan (`POST /launch-plans`) Parameters: - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `ads` (array of objects, required): The confirmed ads (get_launch_questions proposed_ads, corrected by the user), or the existing ads to reuse (each either files or source_ad_id, all the same kind; 50 existing ads max). 1 to 200 items. - `source_ad_id` (string, optional): An ad of this account to reuse with its post (likes and comments stay). Instead of files (get_launch_questions → creatives.choices.existing_ads). matches `^\d{1,40}$`. - `files` (array of objects, optional): New files: the formats of ONE creative, linked by hand or as proposed (one file per format: 9:16, 4:5, 1:1, 16:9; never a video with an image). Leave out with source_ad_id. 1 to 10 items. - `ref` (string, required): drive: (list_drive_folder), or the Apogee storage URL returned by get_upload_urls / confirm_uploads / import_status (your own files only). 1 to 2,048 characters. - `name` (string, optional). up to 255 characters. - `mimeType` (string, optional). up to 100 characters. - `format` (string, optional): 9:16, 4:5, 1:1, 16:9 or auto. up to 10 characters. - `concept` (string, optional): Concept = ad set name in 1 Concept 1 Adset. Files without it: the ad's own (its name), or "Group 1" once other ads have one (the app's Group by hand). 1 to 255 characters. - `ad_name` (string, optional): Ad name = ad set name in 1 Adset 1 Ad. Required with files; existing ads: default = the source ad's name + " - Copy with post ID". 1 to 255 characters. - `copy` (object, optional): This ad's own texts (question "copy"). Not with source_ad_id: an existing ad keeps its post - `primary_texts` (array of strings, optional). up to 5 items; each: up to 5,000 characters. - `headlines` (array of strings, optional). up to 5 items; each: up to 255 characters. - `description` (string, optional). up to 255 characters. - `website_url` (string, optional). up to 2,048 characters. - `call_to_action` (string, optional). up to 40 characters. - `spark_post` (object, optional): TikTok Spark Ads: an existing TikTok post run as this ad instead of files (its caption is the ad text, its likes and comments stay), as list_tiktok_posts gives it - `tiktok_item_id` (string, required): The TikTok post (list_tiktok_posts: its spark_post). matches `^\d{1,40}$`. - `identity` (object, required) - `id` (string, required): The account that posted it. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `type` (string, required): TT_USER / BC_AUTH_TT: one of the brand's TikTok accounts; AUTH_CODE: a creator who authorized the post for this ad account. one of `TT_USER`, `BC_AUTH_TT`, `AUTH_CODE`. - `bc_id` (string or null, optional): BC_AUTH_TT: its Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `platform` (string, optional): Default meta. tiktok / snapchat: the same interview and arguments where they mean the same, their own settings under `tiktok` / `snapchat`, groups made by hand under `adgroups` / `adsquads`. one of `meta`, `tiktok`, `snapchat`. - `campaign_id` (string, optional): Question "campaign": an existing campaign. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `structure` (string, optional): Question "test_structure": 1 Adset 1 Ad, 1 Concept 1 Adset, All in One, Insert in Adset. one of `one_adset_one_ad`, `one_concept_one_adset`, `all_in_one`, `insert_in_adset`. - `all_in_one_name` (string, optional): All in One: the ad set name (required, unless adset_variants). up to 255 characters. - `existing_adset_ids` (array of strings, optional): Insert: the existing ad sets (Meta), ad groups (TikTok) or ad squads (Snapchat). up to 50 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_variants` (array of objects, optional): all_in_one: the same ads in 1-5 new ad sets, each with its own bid / budget (e.g. two cost caps), or each with its own ads (ads). 1 to 5 items. - `name` (string, required): The new ad set's name. 1 to 255 characters. - `bid_amount` (number, optional): Cost cap / bid cap, in the account currency. > 0. - `roas_floor` (number, optional): Minimum ROAS strategy, e.g. 2.5. > 0. - `daily_budget` (number, optional): ABO only, account currency. > 0. - `ads` (array of strings, optional): Existing ads (source_ad_id) only: the ads that go into this new ad set, each by its ad_name or its source_ad_id. Left out on every ad set: every ad in each. Given on one: every ad set needs its own, and every ad must be in one at least. up to 50 items; each: 1 to 255 characters. - `adsets` (array of objects, optional): New files: the ads arranged into new ad sets by hand, on top of structure one_adset_one_ad, one_concept_one_adset or all_in_one. Launched as 1 Concept 1 Adset (each ad set = a concept), the ad names unchanged; every ad set takes the plan's ad set settings. Not with insert_in_adset, adset_variants or existing ads. 1 to 200 items. - `name` (string, required): The new ad set's name. 1 to 255 characters. - `ads` (array of strings, required): The ads that go into it, by their ad_name (each ad in exactly one ad set). up to 200 items; each: 1 to 255 characters. - `nomenclature` (object, optional): Question "nomenclature", when a saved convention reads the names: named in the recap. Files grouped by hand: leave it out - `confirmed` (constant, required): true = the user validated the parsed concepts / iterations / formats. always `true`. - `config_id` (string or null, optional): The saved naming convention used, null when none. up to 64 characters. - `name` (string, optional). up to 255 characters. - `grouping_confirmed` (constant, optional): Question "grouping": true = the user validated the ads (their names and files), read by a convention or grouped by hand. always `true`. - `copy_adset_from` (string, optional): Question "starting_point": the ad set / ad group / ad squad whose settings are copied ("latest" = TikTok: the latest ad group of campaign_id). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_template_id` (string, optional): Question "starting_point": a saved Apogee ad set template. up to 64 characters. - `adset_settings` (object, optional): A new ad set (questions "audience", "tracking"), or the changes to a copied ad set / template - `goal` (string, optional): New ad set only. Default: from the campaign objective. one of `sales`, `leads_website`, `leads_instant_form`, `traffic`, `engagement`, `awareness`, `app_installs`. - `countries` (array of strings, optional): ISO codes, e.g. ["FR"] (a new ad set needs countries, regions or cities). up to 250 items; matches `^[A-Za-z]{2}$`. - `regions` (array of objects, optional): Region keys from search_targeting(type: locations). up to 200 items. - `key` (string, required). 1 to 32 characters. - `cities` (array of objects, optional): City keys from search_targeting(type: locations). up to 250 items. - `key` (string, required). 1 to 32 characters. - `radius` (number, optional). 1 to 80. - `distance_unit` (string, optional). one of `kilometer`, `mile`. - `age_min` (integer, optional). 13 to 65. - `age_max` (integer, optional). 13 to 65. - `genders` (string, optional): Default all. one of `all`, `men`, `women`. - `locales` (array of integers, optional): Language keys from search_targeting(type: locales). up to 50 items; each: ≥ -9,007,199,254,740,991. - `advantage_audience` (boolean, optional): Default true (broad) - `custom_audience_ids` (array of strings, optional): list_custom_audiences. up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional): list_custom_audiences. up to 100 items; matches `^\d{1,40}$`. - `interests` (array of objects, optional): search_targeting(type: interests). up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `behaviors` (array of objects, optional): search_targeting(type: behaviors). up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `life_events` (array of objects, optional): search_targeting(type: demographics): the app's demographics / life events. up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `placements` (one of, optional): "advantage" (Advantage+ placements, the default) or the platforms to keep - Shape `option 1`: (no fields) - Shape `option 2`: - `publisher_platforms` (array of strings, required). one of `facebook`, `instagram`, `messenger`, `audience_network`; at least 1 item. - `pixel_id` (string, optional): A pixel (list_pixels). matches `^\d{1,40}$`. - `event` (string, optional): Meta enum: PURCHASE, LEAD, CONTENT_VIEW, INITIATED_CHECKOUT, ADD_TO_CART, COMPLETE_REGISTRATION…. up to 64 characters. - `custom_event_str` (string, optional): A pixel custom event name (sent as OTHER + this name). up to 100 characters. - `custom_conversion_id` (string, optional): A custom conversion (sent alone). matches `^\d{1,40}$`. - `optimization_goal` (string, optional): New ad set: overrides the default optimization (e.g. LINK_CLICKS for traffic). up to 64 characters. - `bid_strategy` (string, optional): ABO: the new ad sets' strategy (default Lowest cost, the highest volume). CBO: must be the campaign's (or leave it out). one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. - `bid_amount` (number, optional): COST_CAP / LOWEST_COST_WITH_BID_CAP: the cost per result goal (cost cap) or bid cap of every new ad set, account currency (adset_variants: one each). > 0. - `roas_floor` (number, optional): LOWEST_COST_WITH_MIN_ROAS: the minimum ROAS of every new ad set, e.g. 2.5 (adset_variants: one each). > 0. - `attribution` (object, optional): Default 7-day click, 1-day view, 1-day engaged view - `click` (string, required). one of `1_day`, `7_days`. - `view` (string, optional). one of `none`, `1_day`, `7_days`. - `engaged_view` (string, optional). one of `none`, `1_day`, `7_days`. - `application_id` (string, optional): app_installs. matches `^\d{1,40}$`. - `object_store_url` (string, optional): app_installs: App Store / Play Store link. up to 2,048 characters. - `copy_ad_from` (string, optional): Question "starting_point": the ad whose settings are copied ("latest" = TikTok: the latest ad). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `ad_template_id` (string, optional): Question "starting_point": a saved Apogee ad template. up to 64 characters. - `ad_settings` (object, optional): A new ad (questions "identity", "destination"), or the changes to a copied ad / template - `page_id` (string, optional): A Facebook page (list_pages). matches `^\d{1,40}$`. - `instagram_id` (string or null, optional): Default: the page's linked Instagram. matches `^\d{1,40}$`. - `partnership` (object or null, optional): A partnership ad with a creator who approved the brand's Instagram account, or whose Facebook Page accepted the brand's Page; null = none (default) - `creator_ig_id` (string, optional): An Instagram creator (get_launch_questions → identity.choices.partnership_creators, source instagram). matches `^\d{1,40}$`. - `creator_username` (string, optional). up to 100 characters. - `creator_fb_page_id` (string or null, optional): An Instagram creator's Page (default: read from the approved creators; null: none), or alone a Facebook creator (source facebook). matches `^\d{1,40}$`. - `creator_name` (string, optional): A Facebook creator's Page name (as listed). up to 200 characters. - `ad_code` (string, optional): Not used: a partnership ad code boosts the creator's own post, Apogee launches your files. Use creator_ig_id. up to 200 characters. - `identity_mode` (string, optional): advertiser_primary = your brand first (default); creator_primary = the creator first (on Instagram only when the creator has no Facebook Page). one of `advertiser_primary`, `creator_primary`. - `header_display` (string, optional): dynamic (default, Meta picks; optimizes on Instagram only), both identities, or the first identity only. one of `dynamic`, `both`, `primary_only`. - `website_url` (string, optional): Required except for instant forms. up to 2,048 characters. - `url_parameters` (string, optional): UTM parameters, "" = none (required for a new ad). up to 1,000 characters. - `call_to_action` (string, optional): Required for a new ad, e.g. SHOP_NOW, LEARN_MORE, SIGN_UP. up to 40 characters. - `lead_form_id` (string, optional): Instant form leads (list_lead_forms). matches `^\d{1,40}$`. - `display_link` (string, optional). up to 255 characters. - `advantage_plus` (object, optional): Question "advantage_plus" (required) - `opt_in` (array of strings, required): The enhancements the user turned ON; every other one is sent OFF. [] = all off. one of `enhance_cta`, `inline_comment`, `reveal_details_over_time`, `show_destination_blurbs`, `show_summary`, `text_optimizations`, `product_tags`, `optimize_website_destination`, `shop`, `product_browsing`, `browser_addons`, `image_brightness_and_contrast`, `image_templates`, `image_touchups`, `video_auto_crop`, `video_filtering`. - `products` (object, optional): Question "products" (required when the account has a catalog) - `show` (boolean, required): "Show relevant products" from the catalog - `catalog_id` (string, optional): A catalog (get_launch_questions → products). matches `^\d{1,40}$`. - `product_set_id` (string, optional): A product set of that catalog. matches `^\d{1,40}$`. - `daily_budget` (number, optional): ABO: daily budget of each new ad set, account currency (required). > 0. - `launch_status` (string, optional): Created Paused (default), Live Now, Scheduled & Paused, Scheduled & Live. one of `CREATED_PAUSED`, `LIVE_NOW`, `SCHEDULED_PAUSED`, `SCHEDULED_LIVE`. - `schedule` (object, optional): Scheduled & Paused / Scheduled & Live - `date` (string, required): YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `time` (string, required): HH:MM, account time zone. matches `^\d{2}:\d{2}$`. - `ad_overrides` (object, optional): Advanced: raw ad template fields (description, display_link, url_parameters, website_url, call_to_action, instagram_account_id, lead_gen_form_id, primary_texts, headlines) - `description` (any, optional) - `display_link` (any, optional) - `url_parameters` (any, optional) - `website_url` (any, optional) - `call_to_action` (any, optional) - `instagram_account_id` (any, optional) - `lead_gen_form_id` (any, optional) - `primary_texts` (any, optional) - `headlines` (any, optional) - `adset_overrides` (object, optional): Advanced: raw ad set template fields (name, billing_event, optimization_goal, destination_type, daily_min_spend_target, daily_spend_cap, start_time, end_time, bid_strategy, bid_amount, bid_constraints, attribution_spec, attribution_setting, targeting), Meta's units - `name` (any, optional) - `billing_event` (any, optional) - `optimization_goal` (any, optional) - `destination_type` (any, optional) - `daily_min_spend_target` (any, optional) - `daily_spend_cap` (any, optional) - `start_time` (any, optional) - `end_time` (any, optional) - `bid_strategy` (any, optional) - `bid_amount` (any, optional) - `bid_constraints` (any, optional) - `attribution_spec` (any, optional) - `attribution_setting` (any, optional) - `targeting` (any, optional) - `adgroups` (array of objects, optional): TikTok: ad groups made by hand, instead of structure. 1 to 100 items. - `ads` (array of strings, required): The ad_name of each ad in it (every ad in exactly one group). 1 to 200 items; each: 1 to 255 characters. - `name` (string, optional): A new ad group's name. 1 to 255 characters. - `existing_adgroup_id` (string, optional): An existing ad group to fill instead (all groups existing, or all new). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `settings` (object, optional): This ad group's own settings, over the shared ones - `pixel_id` (string, optional): A pixel (get_launch_questions → tracking.choices.pixels). matches `^\d{1,40}$`. - `optimization_event` (string, optional): TikTok's optimization_event: SHOPPING (purchase), INITIATE_ORDER, ON_WEB_CART, ON_WEB_DETAIL, ON_WEB_REGISTER, FORM…. up to 64 characters. - `bid_type` (string, optional): Default BID_TYPE_NO_BID (maximum delivery); BID_TYPE_CUSTOM = cost cap (bid). one of `BID_TYPE_NO_BID`, `BID_TYPE_CUSTOM`. - `bid` (number, optional): Cost cap per conversion (BID_TYPE_CUSTOM), account currency. > 0. - `pacing` (string, optional). one of `PACING_MODE_SMOOTH`, `PACING_MODE_FAST`. - `countries` (array of strings, optional): ISO codes, read into TikTok location ids (or location_ids). up to 250 items; matches `^[A-Za-z]{2}$`. - `location_ids` (array of strings, optional). up to 500 items; matches `^\d{1,40}$`. - `age_groups` (array of strings, optional): [] or absent = every age. one of `AGE_13_17`, `AGE_18_24`, `AGE_25_34`, `AGE_35_44`, `AGE_45_54`, `AGE_55_100`; up to 6 items. - `gender` (string, optional). one of `GENDER_UNLIMITED`, `GENDER_MALE`, `GENDER_FEMALE`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `action_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `placements` (array of strings, optional): Default TikTok only. one of `PLACEMENT_TIKTOK`, `PLACEMENT_PANGLE`, `PLACEMENT_GLOBAL_APP_BUNDLE`; 1 to 3 items. - `daily_budget` (number, optional): ABO: this one's budget (default daily_budget). > 0. - `adsquads` (array of objects, optional): Snapchat: ad squads made by hand, instead of structure. 1 to 100 items. - `ads` (array of strings, required): The ad_name of each ad in it (every ad in exactly one group). 1 to 200 items; each: 1 to 255 characters. - `name` (string, optional): A new ad squad's name. 1 to 255 characters. - `existing_adsquad_id` (string, optional): An existing ad squad to fill instead (all groups existing, or all new). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `settings` (object, optional): This ad squad's own settings, over the shared ones - `optimization_goal` (string, optional): Default PIXEL_PURCHASE. The PIXEL_ goals need pixel_id (and enough of that event on the pixel lately: get_launch_questions tracking.choices.optimization_goals); SWIPES and LANDING_PAGE_VIEW need none. one of `PIXEL_PURCHASE`, `PIXEL_ADD_TO_CART`, `PIXEL_SIGNUP`, `PIXEL_PAGE_VIEW`, `SWIPES`, `LANDING_PAGE_VIEW`. - `pixel_id` (string, optional): A Snap pixel (get_launch_questions → tracking.choices.pixels). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `bid_strategy` (string, optional): Default AUTO_BID. one of `AUTO_BID`, `LOWEST_COST_WITH_MAX_BID`, `TARGET_COST`. - `bid` (number, optional): LOWEST_COST_WITH_MAX_BID / TARGET_COST: the bid, account currency. > 0. - `pacing_type` (string, optional): Default STANDARD. one of `STANDARD`, `ACCELERATED`. - `conversion_window` (string, optional): Default Snapchat's SWIPE_28DAY_VIEW_1DAY. one of `SWIPE_28DAY_VIEW_1DAY`, `SWIPE_7DAY`. - `countries` (array of strings, optional): ISO codes (at least one for a new ad squad). 1 to 250 items; matches `^[A-Za-z]{2}$`. - `region_ids` (array of strings, optional). up to 200 items; each: up to 32 characters. - `min_age` (integer, optional): Default 18 for a new ad squad: Snapchat refuses its goal for under-18s in the EU, the EEA, Switzerland and the UK. 13 to 65. - `max_age` (integer, optional). 13 to 65. - `genders` (string, optional). one of `all`, `men`, `women`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_ids` (array of strings, optional). up to 200 items; each: up to 64 characters. - `segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `excluded_segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `daily_budget` (number, optional): ABO: this one's budget (default daily_budget). > 0. - `tiktok` (object, optional): TikTok's own settings (platform tiktok) - `new_campaign` (object, optional): A new Sales campaign (website conversions; Standard, or Upgraded Smart+), created by the launch (instead of campaign_id) - `name` (string, required). 1 to 255 characters. - `budget_type` (string, required): abo = a budget per ad group (daily_budget); cbo = the campaign's budget. one of `abo`, `cbo`. - `budget` (number, optional): CBO: the campaign budget, account currency. > 0. - `budget_mode` (string, optional): Default daily. one of `daily`, `lifetime`. - `smart_plus` (boolean, optional): true = an Upgraded Smart+ campaign (website Sales), as the app's Create Campaign: up to 30 ad groups of 30 ads, one ad per creative (its texts rotate inside), TikTok's dynamic CTA (ad_settings.cta_language / cta_texts), no Spark post. Default: a Standard campaign - `adgroup_settings` (object, optional): A new ad group's settings, or the changes to a copied one (copy_adset_from) - `pixel_id` (string, optional): A pixel (get_launch_questions → tracking.choices.pixels). matches `^\d{1,40}$`. - `optimization_event` (string, optional): TikTok's optimization_event: SHOPPING (purchase), INITIATE_ORDER, ON_WEB_CART, ON_WEB_DETAIL, ON_WEB_REGISTER, FORM…. up to 64 characters. - `bid_type` (string, optional): Default BID_TYPE_NO_BID (maximum delivery); BID_TYPE_CUSTOM = cost cap (bid). one of `BID_TYPE_NO_BID`, `BID_TYPE_CUSTOM`. - `bid` (number, optional): Cost cap per conversion (BID_TYPE_CUSTOM), account currency. > 0. - `pacing` (string, optional). one of `PACING_MODE_SMOOTH`, `PACING_MODE_FAST`. - `countries` (array of strings, optional): ISO codes, read into TikTok location ids (or location_ids). up to 250 items; matches `^[A-Za-z]{2}$`. - `location_ids` (array of strings, optional). up to 500 items; matches `^\d{1,40}$`. - `age_groups` (array of strings, optional): [] or absent = every age. one of `AGE_13_17`, `AGE_18_24`, `AGE_25_34`, `AGE_35_44`, `AGE_45_54`, `AGE_55_100`; up to 6 items. - `gender` (string, optional). one of `GENDER_UNLIMITED`, `GENDER_MALE`, `GENDER_FEMALE`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `action_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `placements` (array of strings, optional): Default TikTok only. one of `PLACEMENT_TIKTOK`, `PLACEMENT_PANGLE`, `PLACEMENT_GLOBAL_APP_BUNDLE`; 1 to 3 items. - `ad_settings` (object, optional): The ads' TikTok account (and whether their videos show on its profile), destination and CTA (new, or the changes to a copied ad); Smart+ and carousel settings - `identity` (object, optional) - `id` (string, required): The identity id (get_launch_questions → identity.choices.identities). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `type` (string, required): One of the brand's TikTok accounts: TT_USER, or BC_AUTH_TT through its Business Center (TikTok refuses a custom identity on these ads). one of `TT_USER`, `BC_AUTH_TT`. - `bc_id` (string or null, optional): BC_AUTH_TT: the Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `show_on_profile` (boolean, optional): The app's "Also show the video on the TikTok profile": true = the video is also posted on the TikTok account's profile; default: it only runs as an ad (TikTok's default). Not where TikTok keeps the videos ads-only (get_launch_questions identity.show_on_profile) - `website_url` (string, optional): Apogee's TikTok tracking replaces its own utm_ parameters (no UTM option on TikTok: the app's final URL). up to 2,048 characters. - `call_to_action` (string, optional): Default SHOP_NOW (a Standard ad's; a Smart+ ad runs TikTok's dynamic CTA: cta_language, cta_texts). one of `SHOP_NOW`, `BUY_NOW`, `ORDER_NOW`, `LEARN_MORE`, `SIGN_UP`, `DOWNLOAD_NOW`, `CONTACT_US`, `BOOK_NOW`. - `cta_language` (string, optional): Smart+: the language of TikTok's dynamic call to action when the ad group lends none (default en). one of `en`, `fr`, `de`, `es`, `it`, `nl`, `pt`, `pl`, `sv`. - `cta_texts` (array of strings, optional): Smart+: the calls to action kept among the ones TikTok offers in cta_language (default: TikTok's whole list). up to 20 items; each: 1 to 40 characters. - `automations` (object, optional): Smart+: TikTok's automations of the ads (as Meta's Advantage+ creative enhancements), each off unless turned on - `auto_add` (boolean, optional): TikTok adds assets of its own - `enhancements` (array of strings, optional). one of `TRANSLATE_AND_DUB`, `MUSIC_REFRESH`, `VIDEO_QUALITY`, `IMAGE_QUALITY`, `IMAGE_RESIZE`; up to 5 items. - `music_id` (string, optional): Carousels (image ads): the music, one of list_tiktok_music; default TikTok's pick at the launch. matches `^\d{1,40}$`. - `carousel_mode` (string, optional): Images run as carousels (TikTok's image ads), as the app's Configure switch: single (default) = each image ad a carousel of its image; by_concept = a concept's images one carousel ad named after the concept (35 images at most, its first ad's settings and texts). one of `single`, `by_concept`. - `adgroup_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad group template (list_platform_templates level adgroup), the base of the new ad groups' settings (instead of copy_adset_from); adgroup_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `ad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad template (list_platform_templates level ad), the base of the ads' settings (instead of copy_ad_from); ad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `time_zone` (string, optional): The IANA zone of schedule (only when the advertiser's time zone is unknown). up to 64 characters. - `end_date` (string, optional): A lifetime campaign budget (CBO, budget_mode lifetime, a new campaign or an existing one): the date it runs to, YYYY-MM-DD in the schedule's time zone, after the launch (a scheduled launch: TikTok spreads the budget until then). Ignored for a daily budget. matches `^\d{4}-\d{2}-\d{2}$`. - `end_time` (string, optional): HH:MM of end_date, default the start's time. matches `^\d{2}:\d{2}$`. - `snapchat` (object, optional): Snapchat's own settings (platform snapchat) - `new_campaign` (object, optional): A new Sales campaign, created by the launch (instead of campaign_id) - `name` (string, required). 1 to 255 characters. - `budget_type` (string, required): abo = a budget per ad squad (daily_budget, 5 minimum); cbo = the campaign's (20 minimum per day), Snapchat's Smart Budget: its ad squads share their optimisation goal, pixel, bid strategy and conversion window (get_launch_questions tracking.choices); a lower budget is raised to it. one of `abo`, `cbo`. - `budget` (number, optional): CBO: the campaign budget per day, account currency. > 0. - `adsquad_settings` (object, optional): A new ad squad's settings, or the changes to a copied one (copy_adset_from) - `optimization_goal` (string, optional): Default PIXEL_PURCHASE. The PIXEL_ goals need pixel_id (and enough of that event on the pixel lately: get_launch_questions tracking.choices.optimization_goals); SWIPES and LANDING_PAGE_VIEW need none. one of `PIXEL_PURCHASE`, `PIXEL_ADD_TO_CART`, `PIXEL_SIGNUP`, `PIXEL_PAGE_VIEW`, `SWIPES`, `LANDING_PAGE_VIEW`. - `pixel_id` (string, optional): A Snap pixel (get_launch_questions → tracking.choices.pixels). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `bid_strategy` (string, optional): Default AUTO_BID. one of `AUTO_BID`, `LOWEST_COST_WITH_MAX_BID`, `TARGET_COST`. - `bid` (number, optional): LOWEST_COST_WITH_MAX_BID / TARGET_COST: the bid, account currency. > 0. - `pacing_type` (string, optional): Default STANDARD. one of `STANDARD`, `ACCELERATED`. - `conversion_window` (string, optional): Default Snapchat's SWIPE_28DAY_VIEW_1DAY. one of `SWIPE_28DAY_VIEW_1DAY`, `SWIPE_7DAY`. - `countries` (array of strings, optional): ISO codes (at least one for a new ad squad). 1 to 250 items; matches `^[A-Za-z]{2}$`. - `region_ids` (array of strings, optional). up to 200 items; each: up to 32 characters. - `min_age` (integer, optional): Default 18 for a new ad squad: Snapchat refuses its goal for under-18s in the EU, the EEA, Switzerland and the UK. 13 to 65. - `max_age` (integer, optional). 13 to 65. - `genders` (string, optional). one of `all`, `men`, `women`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_ids` (array of strings, optional). up to 200 items; each: up to 64 characters. - `segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `excluded_segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `ad_settings` (object, optional): The ads' profile, destination and CTA (new, or the changes to a copied ad) - `profile_id` (string, optional): A public profile (get_launch_questions → identity.choices.profiles). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `website_url` (string, optional): https only. up to 2,048 characters. - `utm` (object, optional): UTM parameters appended to the URL (the app's UTM toggle): given = on, the app's defaults for the fields left out - `source` (string, optional). up to 200 characters. - `medium` (string, optional). up to 200 characters. - `campaign` (string, optional). up to 200 characters. - `id` (string, optional). up to 200 characters. - `content` (string, optional). up to 200 characters. - `call_to_action` (string, optional): Default SHOP_NOW. one of `SHOP_NOW`, `ORDER_NOW`, `SIGN_UP`, `GET_NOW`, `BOOK_NOW`, `VIEW`, `MORE`, `READ`, `WATCH`, `DOWNLOAD`, `APPLY_NOW`, `BUY_TICKETS`, `PLAY_GAME`, `LISTEN`. - `brand_name` (string, optional). up to 32 characters. - `adsquad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad squad template (list_platform_templates level adgroup), the base of the new ad squads' settings (instead of copy_adset_from); adsquad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `ad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad template (list_platform_templates level ad), the base of the ads' settings (instead of copy_ad_from); ad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `time_zone` (string, optional): The IANA zone of schedule (only when the ad account's time zone is unknown). up to 64 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "prepare_launch", "arguments": { "ad_account_id": "act_1234567890", "campaign_id": "120200000000000001", "structure": "one_concept_one_adset", "copy_adset_from": "120200000000000002", "copy_ad_from": "120200000000000003", "ads": [ { "ad_name": "NORDLICHT_RAIN_HOOK1", "concept": "Rain test", "files": [ { "ref": "drive:EXAMPLE_FILE_916", "name": "NORDLICHT_RAIN_HOOK1_9x16.mp4", "format": "9:16" }, { "ref": "drive:EXAMPLE_FILE_45", "name": "NORDLICHT_RAIN_HOOK1_4x5.mp4", "format": "4:5" } ], "copy": { "primary_texts": [ "Rain-proof, wind-proof, and light enough to forget you are wearing it." ], "headlines": [ "The Nordlicht shell jacket" ], "description": "Free returns within 30 days" } } ], "grouping_confirmed": true, "advantage_plus": { "opt_in": [] }, "daily_budget": 50, "launch_status": "CREATED_PAUSED" } } } ``` #### launch https://www.apogee.ad/developers/mcp/launch/ Launch a prepared plan. Launches a prepared plan (Meta, TikTok or Snapchat: the plan knows), once, after your explicit yes to its recap. Launch a plan prepared by prepare_launch, only after the user explicitly said yes to its full recap. A plan launches once, within 24 hours of being prepared. Returns as soon as the launch started; follow it with launch_status. Refused when the last two launches on the same ad account failed with the same error, unless the user says its cause is fixed (previous_error_fixed). - Effect: Changes the ad account or acts as the Page, once, after the user's explicit yes. - Scope: `launch` - Plans: Pro and Scale - Credits: 15 credits per ad created, counted at the end (an ad that failed is free) - REST API: Confirm launch plan (`POST /launch-plans/{id}/confirm`) Parameters: - `plan_id` (string, required). up to 64 characters. - `confirm` (constant, required): true = the user said yes to the recap. always `true`. - `previous_error_fixed` (constant, optional): true only when the user says the cause of the last failed launches on this ad account is fixed (launch_status what_to_do), or chose a change that removes it. always `true`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "launch", "arguments": { "plan_id": "7b0f3c52-4d1e-4c8a-9f61-2a5e8d3c1b90", "confirm": true } } } ``` #### launch_status https://www.apogee.ad/developers/mcp/launch-status/ Follow a launch. Follows a launch until it is finished. `interrupted` means an Apogee deploy cut it: check Ads Manager before launching again. Follow a launch by its plan_id. status interrupted = a deploy of Apogee cut the launch: some ads may exist, check Ads Manager before launching again. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get launch plan status (`GET /launch-plans/{id}/status`) Parameters: - `plan_id` (string, required). up to 64 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "launch_status", "arguments": { "plan_id": "7b0f3c52-4d1e-4c8a-9f61-2a5e8d3c1b90" } } } ``` #### create_campaign https://www.apogee.ad/developers/mcp/create-campaign/ Prepare a new campaign. Prepares a new campaign, always created paused: name, objective, budget per ad set (ABO) or per campaign (CBO, with its bid strategy). `execute_action` creates it after your OK. Prepare a new Meta campaign (always created PAUSED): returns an action_id and what will be created. Nothing is created until execute_action, after the user explicitly approved its name, objective and budget type. execute_action returns the new campaign id (details) for prepare_launch campaign_id. - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `name` (string, required). 1 to 255 characters. - `objective` (string, required). one of `OUTCOME_SALES`, `OUTCOME_LEADS`, `OUTCOME_TRAFFIC`, `OUTCOME_ENGAGEMENT`, `OUTCOME_AWARENESS`, `OUTCOME_APP_PROMOTION`. - `budget_type` (string, required): abo = budget per ad set; cbo = campaign budget. one of `abo`, `cbo`. - `budget` (number, optional): CBO only: daily campaign budget, in the account currency. > 0. - `bid_strategy` (string, optional): CBO: the bid strategy shared by all ad sets; each new ad set then gets its own bid_amount / roas_floor (prepare_launch adset_variants). ABO: leave it out and use adset_settings.bid_strategy.. one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_campaign", "arguments": { "ad_account_id": "act_1234567890", "name": "Nordlicht | Prospecting", "objective": "OUTCOME_SALES", "budget_type": "cbo", "budget": 100, "bid_strategy": "LOWEST_COST_WITHOUT_CAP" } } } ``` ### Performance The same reads as Apogee's Autopilot, with the same numbers as the app. Ad names follow your naming convention: ask per concept or per hook. #### get_account_overview https://www.apogee.ad/developers/mcp/get-account-overview/ Get account overview. Spend, impressions, clicks, CTR, CPC, ROAS and conversions of the ad account for a period. Get overall ad account performance metrics (spend, impressions, clicks, CTR, CPC, ROAS, conversions) for a date range. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required): Date preset for the metrics. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_account_overview", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_campaigns https://www.apogee.ad/developers/mcp/get-campaigns/ Get campaigns. The campaigns with their budget, spend and results. Get list of campaigns with their performance metrics. Use to see which campaigns are active, their budget, spend, and results. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required): Date preset for metrics. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `status_filter` (string, optional): Filter by campaign status. one of `all`, `active`, `paused`. - `limit` (number, optional): Max number of campaigns to return (default 25) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_campaigns", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_adsets https://www.apogee.ad/developers/mcp/get-adsets/ Get adsets. The ad sets with their targeting, budget and metrics. Get ad sets with targeting info and metrics. Use to analyze audience targeting and budgets at ad set level. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Optional: filter by specific campaign ID - `limit` (number, optional) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_adsets", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_ads https://www.apogee.ad/developers/mcp/get-ads/ Get ads. The ads with their creative and metrics. Get individual ads with creative info and performance metrics. Use to identify top/worst performing creatives. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Optional: filter by campaign - `adset_id` (string, optional): Optional: filter by ad set - `limit` (number, optional) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_ads", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_top_performers https://www.apogee.ad/developers/mcp/get-top-performers/ Get top performers. The best and the worst campaigns, ad sets or ads by ROAS, CPA, CTR or spend: what to scale, what to cut. Get top and worst performing ads/adsets ranked by a metric. Perfect for "what is working" or "what should I scale/kill" questions. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `metric` (string, required): Metric to rank by. one of `roas`, `cpa`, `ctr`, `spend`. - `level` (string, required): Level to analyze. one of `campaign`, `adset`, `ad`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `limit` (number, optional) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_top_performers", "arguments": { "date_preset": "last_7d", "metric": "roas", "level": "campaign", "ad_account_id": "act_1234567890" } } } ``` #### get_daily_breakdown https://www.apogee.ad/developers/mcp/get-daily-breakdown/ Get daily breakdown. Spend, ROAS, CPA and add to carts day by day. Get day-by-day spend, ROAS, CPA, add_to_cart data for the account or a specific campaign. Use when user asks for a chart, graph, daily trend, or day-by-day analysis. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `last_7d`, `last_14d`, `last_30d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Optional: filter by specific campaign Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_daily_breakdown", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_ad_performance_by_placement https://www.apogee.ad/developers/mcp/get-ad-performance-by-placement/ Get ad performance by placement. Results by placement (Feed, Stories, Reels…). Get performance breakdown by placement (Feed, Stories, Reels, etc.). Use for placement optimization analysis. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Campaign ID - `adset_id` (string, optional): Ad set ID Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_ad_performance_by_placement", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_ad_performance_by_age_gender https://www.apogee.ad/developers/mcp/get-ad-performance-by-age-gender/ Get ad performance by age gender. Results by age and gender. Get performance breakdown by age and gender demographics. Use for audience optimization analysis. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Campaign ID - `adset_id` (string, optional): Ad set ID Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_ad_performance_by_age_gender", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_ad_performance_by_device https://www.apogee.ad/developers/mcp/get-ad-performance-by-device/ Get ad performance by device. Results by device. Get performance breakdown by device (mobile, desktop, tablet). Use for device optimization analysis. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `date_preset` (string, required). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Campaign ID - `adset_id` (string, optional): Ad set ID Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_ad_performance_by_device", "arguments": { "date_preset": "last_7d", "ad_account_id": "act_1234567890" } } } ``` #### get_creative_fatigue_metrics https://www.apogee.ad/developers/mcp/get-creative-fatigue-metrics/ Get creative fatigue metrics. Frequency, CTR trend and CPC over time; flags tired creatives. Detect creative fatigue by analyzing frequency, CTR trend, and CPC over time. Flags fatigue if frequency > 3 and CTR declining > 15%. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_id` (string, optional): Campaign ID to analyze - `adset_id` (string, optional): Ad set ID to analyze - `date_preset` (string, optional): Date range (default last_14d). one of `last_7d`, `last_14d`, `last_30d`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_creative_fatigue_metrics", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### get_video_metrics https://www.apogee.ad/developers/mcp/get-video-metrics/ Get video metrics. Hook rate, hold rate, completion and ThruPlay per video ad. Get creative-level video performance: Hook Rate (3s views / impressions, >25% strong, <15% weak), Hold Rate (p25 watched / 3s views, >70% strong, <50% weak), Completion Rate (p100 / impressions, >25% strong), ThruPlay Rate, quartile retention, cost per 3s view, cost per ThruPlay. Use this for creative strategist analysis: diagnosing weak hooks, retention issues, winning ad breakdowns. Accepts ad_ids (preferred) OR adset_id OR campaign_id. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `ad_ids` (array of strings, optional): Array of Meta ad IDs (preferred — gives per-ad metrics) - `adset_id` (string, optional): Ad set ID (aggregate metrics across all ads in the ad set) - `campaign_id` (string, optional): Campaign ID (aggregate across all ads in the campaign) - `date_preset` (string, optional): Date range (default last_14d). one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_video_metrics", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### get_ad_creatives https://www.apogee.ad/developers/mcp/get-ad-creatives/ Get ad creatives. The images, videos and texts of the ads of an ad set or a campaign. Get creative details (images, videos, copy) for ads in an ad set or campaign. Use to analyze which creatives are running. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `adset_id` (string, optional): Ad set ID to get creatives from - `campaign_id` (string, optional): Campaign ID to get creatives from - `limit` (number, optional): Max number of creatives (default 25) Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_ad_creatives", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### get_audience_insights https://www.apogee.ad/developers/mcp/get-audience-insights/ Get audience insights. The targeting and estimated audience size of ad sets. Get detailed targeting and estimated audience size for ad sets. Use to understand who is being targeted and reach potential. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `adset_ids` (array of strings, required): Array of ad set IDs to analyze - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_audience_insights", "arguments": { "adset_ids": [ "120200000000000002" ], "ad_account_id": "act_1234567890" } } } ``` #### get_account_spend_limit https://www.apogee.ad/developers/mcp/get-account-spend-limit/ Get account spend limit. The account's spend cap, amount spent and balance. Get account spend cap, amount spent, balance, and currency. Use when user asks about account limits or remaining budget. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_account_spend_limit", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### get_campaign_budget_optimization https://www.apogee.ad/developers/mcp/get-campaign-budget-optimization/ Get campaign budget optimization. Whether a campaign uses a campaign budget (CBO), and that budget. Check if CBO (Campaign Budget Optimization) is enabled and get budget details for a campaign. Use to understand budget strategy. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `campaign_id` (string, required): Campaign ID to check - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_campaign_budget_optimization", "arguments": { "campaign_id": "120200000000000001", "ad_account_id": "act_1234567890" } } } ``` ### Brand and research Context for creative strategy: your business profile, your comments, reviews and competitors. #### get_business_profile https://www.apogee.ad/developers/mcp/get-business-profile/ Get business profile. What Apogee knows about the brand of this ad account: website, margin, targets (CAC, ROAS). Read the stored business profile for the current ad account. Returns known fields (website_url, brand_name, category, business_type, target_cac, target_roas, target_mer, product_margin_pct, ltv, repeat_purchase_rate_pct, primary_objective, etc.) plus the list of fields still missing. USE THIS FIRST when a prompt requires business context (e.g. scaling decisions, customer review analysis). If required fields are missing, ASK the user for them in plain language, then call update_business_profile to persist. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_business_profile", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### update_business_profile https://www.apogee.ad/developers/mcp/update-business-profile/ Update the business profile. Saves what you told the assistant about the brand, so it does not ask again. Apogee's data only. Persist or update fields on the business profile (user_id + ad_account_id is implicit). Call this AFTER the user tells you a value (website URL, margin, CAC target, ROAS target, brand name, etc.) so we do not have to ask again next time. Pass only the fields you are updating; others stay as-is. Numeric fields: product_margin_pct, target_cac, target_roas, target_mer, ltv, repeat_purchase_rate_pct, avg_order_value, shipping_cost. Text fields: website_url, brand_name, category, business_type (ecommerce/lead_gen/saas/b2b/awareness), trustpilot_url, primary_objective (scale/profit/break_even/awareness), free_notes. JSONB arrays: reddit_subreddits, competitor_pages. - Effect: Changes Apogee only (imports, uploads, settings, comment syncs, AI drafts): nothing on the ad account. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `fields` (object, required): Object with one or more writable fields. Example: { "website_url": "https://mybrand.com", "product_margin_pct": 35, "target_roas": 2.5 } - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "update_business_profile", "arguments": { "fields": {}, "ad_account_id": "act_1234567890" } } } ``` #### fetch_website_content https://www.apogee.ad/developers/mcp/fetch-website-content/ Fetch website content. Reads the brand's website: voice, positioning, persona, claims. Fetch and summarize the user's brand website to extract brand voice, product positioning, target persona, and key claims. Call this when you need product context for a creative strategy prompt and website_url is known in the business profile. Cache 7 days — if cached, reuses the stored summary unless the user asks to refresh. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: 150 credits per page read, free when served from the cache Parameters: - `url` (string, required): Full URL (https://...). Use website_url from the business profile unless the user gives another. - `force_refresh` (boolean, optional): Set true to bypass cache and re-crawl - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "fetch_website_content", "arguments": { "url": "https://www.dropbox.com/scl/fo/example0000/nordlicht-creatives" } } } ``` #### fetch_ad_comments https://www.apogee.ad/developers/mcp/fetch-ad-comments/ Fetch ad comments. The comments of your ads, as Comment Analysis stores them. Read ad comments from the shared DB (populated by the Comment Analysis feature). Returns messages, authors, like counts, whether the page replied. Use for extracting customer language, objections, praise, and pain points. IMPORTANT: if the user asks about "all ads" or "account-wide", call WITHOUT ad_ids/post_ids to get every comment for the ad account in one query. Use ad_ids/post_ids only to narrow down to specific ads. If no data is found, tell the user to run Comment Analysis sync first. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `ad_ids` (array of strings, optional): Optional: narrow to specific Meta ad IDs. - `post_ids` (array of strings, optional): Optional: narrow to specific Facebook/Instagram post IDs. - `limit` (number, optional): Max comments to return (default 200, max 1000). - `since_days` (number, optional): Only return comments created in the last N days (default 30). Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "fetch_ad_comments", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### trigger_comment_sync https://www.apogee.ad/developers/mcp/trigger-comment-sync/ Sync ad comments. Fetches fresh comments from Meta (1 to 5 minutes). Trigger a Comment Analysis sync to fetch fresh ad comments from Meta. Use this when fetch_ad_comments returns stale data (sync_status="stale_suggest_refresh") or no data at all. The sync runs asynchronously (takes 1-5 minutes depending on the number of ads). After triggering, tell the user the sync has started and that fresh comments will be available shortly — or in a follow-up conversation. Choose period_days: 30 (default, faster) or 90 (deeper, slower). The synced comments are shared with the Comment Analysis page. - Effect: Changes Apogee only (imports, uploads, settings, comment syncs, AI drafts): nothing on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: 10 credits per sync started Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `period_days` (constant, optional): How far back to sync comments. 30 days (faster, default) or 90 days (deeper).. always `90`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "trigger_comment_sync", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### fetch_customer_reviews https://www.apogee.ad/developers/mcp/fetch-customer-reviews/ Fetch customer reviews. Public reviews of the brand, positive or negative. Search public customer reviews about the brand (Trustpilot, Judge.me, Google Reviews, etc.) via web_search with targeted queries. Use rating_filter="positive" for 4-5 star reviews (what customers love) or "negative" for 1-3 star reviews (objections, gaps — the best angles for creatives). Requires brand_name in the business profile. Cache 2 days. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `brand_name` (string, optional): Brand name. Defaults to business_profile.brand_name if omitted. - `rating_filter` (string, optional): "positive" = 4-5 stars, "negative" = 1-3 stars, "all" = everything. Default "all".. one of `positive`, `negative`, `all`. - `force_refresh` (boolean, optional) - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "fetch_customer_reviews", "arguments": { "brand_name": "Nordlicht | Spring launch", "rating_filter": "positive" } } } ``` #### search_reddit https://www.apogee.ad/developers/mcp/search-reddit/ Search reddit. Reddit threads on a topic: the customers' own words and pain points. Search Reddit via web_search with site:reddit.com filter. Use for ICP pain point mining, customer language extraction, and community dialect. Construct queries from product keywords + pain signals ("hate", "alternative to", "better than", "problem with"). Cache 2 days. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `keywords` (string, required): Search keywords. Include product category + signal words (e.g. "vegan skincare hate", "best alternative to X") - `subreddit` (string, optional): Optional: restrict to a specific subreddit (e.g. "SkincareAddiction") - `force_refresh` (boolean, optional) - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_reddit", "arguments": { "keywords": "120200000000000001" } } } ``` #### search_meta_ad_library https://www.apogee.ad/developers/mcp/search-meta-ad-library/ Search meta ad library. Competitors' ads in the Meta Ad Library (texts only). Search the Meta Ad Library for competitor ads by brand/page or keyword. Returns TEXT metadata only (ad copy, headline, CTA, start date, is_running, countries) — NO images/videos to avoid token blowup. Use for Competitor Ad Analysis prompt. Ads running 90+ days = validated winners. Cache 7 days. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `page_id` (string, optional): Meta page ID of the competitor (preferred). Resolves to their ad library directly. - `search_terms` (string, optional): Keyword search across all pages (use when page_id unknown). - `countries` (array of strings, optional): ISO country codes (e.g. ["FR","BE"]). Default ["ALL"]. - `limit` (number, optional): Max ads to return (default 20, max 50). - `force_refresh` (boolean, optional) - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "search_meta_ad_library", "arguments": { "page_id": "100000000000001", "search_terms": "120200000000000001" } } } ``` #### load_reference https://www.apogee.ad/developers/mcp/load-reference/ Load reference. Loads one of Apogee's reference documents (audit frameworks, checklists, benchmarks). Load a deep-knowledge reference document into conversation context. Use when you need detailed frameworks, checklists, or benchmarks for a thorough analysis. Available references: "andromeda-audit" (full Meta Ads audit framework — 7-step process, 4 weighted categories: Pixel/CAPI 30%, Creative 30%, Structure 20%, Audience 20%), "meta-audit-checks" (50-point audit checklist), "meta-benchmarks" (CTR/CPM/ROAS/CPA benchmarks by vertical), "scoring-system" (how to score and weight each audit category), "audit-summary" (compact overview for quick reference). Only load what you need — each reference adds significant tokens to context. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `name` (string, required): The reference document to load.. one of `andromeda-audit`, `meta-audit-checks`, `meta-benchmarks`, `scoring-system`, `audit-summary`. - `ad_account_id` (string, optional): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "load_reference", "arguments": { "name": "andromeda-audit" } } } ``` ### Actions Each tool only prepares: it returns an `action_id` and what will change. Nothing changes until `execute_action`, after your explicit OK, within 24 hours. Created objects are paused unless you ask otherwise. #### pause_ad https://www.apogee.ad/developers/mcp/pause-ad/ Pause ad. Pauses ads. Pause one or more ads. Use when user asks to pause, cut, stop, or kill specific ads. Always confirm with the user before executing. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `ad_ids` (array of strings, required): Array of ad IDs to pause - `ad_names` (array of strings, required): Names of the ads for confirmation display - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "pause_ad", "arguments": { "ad_ids": [ "120200000000000003" ], "ad_names": [ "Nordlicht | Spring launch" ], "ad_account_id": "act_1234567890" } } } ``` #### pause_adset https://www.apogee.ad/developers/mcp/pause-adset/ Pause adset. Pauses ad sets. Pause one or more ad sets. Use when user asks to pause, cut, stop, or kill specific ad sets. Preferred over pause_ad for 1-ad-per-adset structures. When user wants to pause/cut a creative, use this tool with the adset_id. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `adset_ids` (array of strings, required): Array of ad set IDs to pause - `adset_names` (array of strings, required): Names of the ad sets for confirmation display - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "pause_adset", "arguments": { "adset_ids": [ "120200000000000002" ], "adset_names": [ "Nordlicht | Spring launch" ], "ad_account_id": "act_1234567890" } } } ``` #### activate_ad https://www.apogee.ad/developers/mcp/activate-ad/ Activate ad. Turns paused ads back on. Reactivate one or more paused ads to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ads. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `ad_ids` (array of strings, required): Array of ad IDs to activate - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `ad_names` (array of strings, optional): Names of the ads for confirmation display Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "activate_ad", "arguments": { "ad_ids": [ "120200000000000003" ], "ad_account_id": "act_1234567890" } } } ``` #### activate_adset https://www.apogee.ad/developers/mcp/activate-adset/ Activate adset. Turns paused ad sets back on. Reactivate one or more paused ad sets to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ad sets. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `adset_ids` (array of strings, required): Array of ad set IDs to activate - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `adset_names` (array of strings, optional): Names of the ad sets for confirmation display Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "activate_adset", "arguments": { "adset_ids": [ "120200000000000002" ], "ad_account_id": "act_1234567890" } } } ``` #### update_budget https://www.apogee.ad/developers/mcp/update-budget/ Update budget. Sets new daily budgets for ad sets. Update daily budget for one or more ad sets. Use when user asks to scale, increase, decrease, or change budget. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `updates` (array of objects, required): Array of budget updates - `adset_id` (string, optional) - `adset_name` (string, optional) - `current_budget` (number, optional): Current daily budget in account currency - `new_budget` (number, optional): New daily budget in account currency - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "update_budget", "arguments": { "updates": [ { "adset_id": "120200000000000002", "adset_name": "Nordlicht | Spring launch" } ], "ad_account_id": "act_1234567890" } } } ``` #### scale_budget https://www.apogee.ad/developers/mcp/scale-budget/ Scale budget. Scales ad set budgets by a percentage (+100% at most per change). Scale budgets of ad sets by a percentage (e.g. +30% or -20%). Fetches current budgets, calculates new ones, and shows confirmation with before/after. Max +100% per operation, min 1€/day. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `adset_ids` (array of strings, required): Array of ad set IDs to scale - `percentage` (number, required): Percentage to scale by (e.g. 30 for +30%, -20 for -20%). Max +100, cannot go below 1€/day. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `adset_names` (array of strings, optional): Names of the ad sets Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "scale_budget", "arguments": { "adset_ids": [ "120200000000000002" ], "percentage": 1, "ad_account_id": "act_1234567890" } } } ``` #### update_campaign_status https://www.apogee.ad/developers/mcp/update-campaign-status/ Update campaign status. Pauses or turns on a whole campaign. Change the status of an entire campaign (ACTIVE or PAUSED). Use when user asks to pause or activate a whole campaign. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `campaign_id` (string, required): Campaign ID - `status` (string, required): New status. one of `ACTIVE`, `PAUSED`. - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `campaign_name` (string, optional): Campaign name for confirmation display Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "update_campaign_status", "arguments": { "campaign_id": "120200000000000001", "status": "ACTIVE", "ad_account_id": "act_1234567890" } } } ``` #### update_adset_targeting https://www.apogee.ad/developers/mcp/update-adset-targeting/ Update adset targeting. Changes an ad set's targeting. Update the targeting of an existing ad set. Use when user asks to change audience, targeting, demographics, interests, or locations. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `adset_id` (string, required): Ad set ID to update - `targeting` (object, required): New targeting spec (geo_locations, age_min, age_max, genders, flexible_spec, custom_audiences, etc.) - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `adset_name` (string, optional): Ad set name for confirmation display Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "update_adset_targeting", "arguments": { "adset_id": "120200000000000002", "targeting": {}, "ad_account_id": "act_1234567890" } } } ``` #### duplicate_ad https://www.apogee.ad/developers/mcp/duplicate-ad/ Duplicate ad. Copies an ad into other ad sets of the same ad account, keeping the same post (likes, comments, shares). Duplicate a winning ad into one or more target ad sets of the same ad account, on the SAME Facebook post: its likes, comments and shares stay (a new ad on its post id, else on its creative). Instagram starts a new post for each ad, as with a post id in Ads Manager. Use when user asks to duplicate, replicate, scale a winning creative to other campaigns. Keeps the SAME Facebook post (its likes, comments and shares): a new ad on its post id, else on its creative (Meta's /copies made a new post). Instagram starts a new post for each ad, as with a post id in Ads Manager. Only inside the same ad account (a copy into another ad account makes a new post); changing a text or the URL afterwards makes a new post too. The result lists each new ad id and the method used (post_id: shown as an existing post in Ads Manager; shared_creative: the same post shown as a new ad). In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `source_ad_id` (string, required): ID of the ad to duplicate - `source_ad_name` (string, required): Name of the source ad (for confirmation display) - `target_adset_ids` (array of strings, required): Ad set IDs to duplicate into - `target_adset_names` (array of strings, required): Names of target ad sets (for confirmation display) - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `rename_suffix` (string, optional): Optional suffix to append to duplicated ad names (e.g. " - Copy") Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "duplicate_ad", "arguments": { "source_ad_id": "120200000000000003", "source_ad_name": "Nordlicht | Spring launch", "target_adset_ids": [ "120200000000000001" ], "target_adset_names": [ "Nordlicht | Spring launch" ], "ad_account_id": "act_1234567890" } } } ``` #### duplicate_adset https://www.apogee.ad/developers/mcp/duplicate-adset/ Duplicate adset. Copies an ad set with its ads, each keeping its post; created paused. Duplicate an ad set WITH its ads into the same campaign or a different one. Each copied ad keeps its post (likes, comments, shares keep adding up). The duplicate is created as PAUSED. Use when user asks to duplicate or copy an ad set. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `source_adset_id` (string, required): ID of the ad set to duplicate - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `target_campaign_id` (string, optional): Optional: campaign ID to duplicate into (defaults to same campaign) - `new_name` (string, optional): Optional: added after the source's name (" - "), e.g. a market or a version - `new_budget` (number, optional): Optional: new daily budget in account currency Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "duplicate_adset", "arguments": { "source_adset_id": "120200000000000001", "ad_account_id": "act_1234567890" } } } ``` #### duplicate_campaign https://www.apogee.ad/developers/mcp/duplicate-campaign/ Duplicate campaign. Copies a campaign with its ad sets and ads; created paused. Duplicate a full campaign with all its ad sets and ads (archived and deleted ones left out). Each copied ad keeps its post. The duplicate is created as PAUSED. Use when user asks to duplicate or copy a campaign. In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `source_campaign_id` (string, required): ID of the campaign to duplicate - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `new_name` (string, optional): Optional: added after the source's name (" - "), e.g. a market or a version - `status_option` (string, optional): Status for the new campaign (default PAUSED). one of `PAUSED`, `ACTIVE`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "duplicate_campaign", "arguments": { "source_campaign_id": "120200000000000001", "ad_account_id": "act_1234567890" } } } ``` #### create_adset https://www.apogee.ad/developers/mcp/create-adset/ Create adset. A new ad set in an existing campaign, from scratch or copying another ad set's targeting and conversion; created paused. Create a new ad set in an existing campaign. The ad set is created as PAUSED. Use when user asks to create a new ad set or audience. In a campaign budget (CBO) leave daily_budget out: the campaign's budget and bid strategy apply, each ad set gets its own bid_amount / roas_floor. In ABO, daily_budget is required. copy_from_adset_id copies the audience, optimization, conversion event, destination and attribution of an ad set of the same account (then targeting and optimization_goal can be left out). In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `campaign_id` (string, required): Campaign ID to create the ad set in - `name` (string, required): Ad set name - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `copy_from_adset_id` (string, optional): Optional: an ad set of the same account to copy targeting, optimization_goal, billing_event, promoted_object, destination_type and attribution_spec from (its budget, bid and schedule are not copied) - `daily_budget` (number, optional): ABO only: daily budget in cents (e.g. 2000 = 20€). Leave it out in a CBO campaign - `targeting` (object, optional): Targeting spec (geo_locations, age_min, age_max, genders, interests, custom_audiences, etc.). Required unless copy_from_adset_id - `optimization_goal` (string, optional): Optimization goal (e.g. OFFSITE_CONVERSIONS, LINK_CLICKS, IMPRESSIONS). Required unless copy_from_adset_id - `promoted_object` (object, optional): Conversion event, e.g. { pixel_id, custom_event_type: "PURCHASE" }. Required for OFFSITE_CONVERSIONS / VALUE / LEAD_GENERATION unless copied - `billing_event` (string, optional): Billing event (default IMPRESSIONS) - `bid_strategy` (string, optional): ABO: this ad set's bid strategy (default LOWEST_COST_WITHOUT_CAP). CBO: the campaign's (leave it out). one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. - `bid_amount` (number, optional): Cost cap / bid cap in cents (e.g. 2500 = 25€): required with COST_CAP and LOWEST_COST_WITH_BID_CAP - `roas_floor` (number, optional): Minimum ROAS strategy: the ROAS floor, e.g. 2.5 (optimization becomes VALUE) - `start_time` (string, optional): Optional: start time in ISO format Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_adset", "arguments": { "campaign_id": "120200000000000001", "name": "Nordlicht | Spring launch", "ad_account_id": "act_1234567890" } } } ``` #### create_ad https://www.apogee.ad/developers/mcp/create-ad/ Create ad. A new ad on an existing post or creative; created paused. Create a new ad in an existing ad set. Use an object_story_id (page post ID) to preserve social proof (likes, comments, shares), or a creative_id to reuse an existing creative. The ad is created as PAUSED. Reuses an existing post (object_story_id) or creative (creative_id); Advantage+ enhancements are sent OFF on a post. For an Instagram post or explicit Advantage+ choices, use create_ad_from_post. For several ads or ad sets, use prepare_launch with ads[].source_ad_id (likes and comments kept). In Apogee this only PREPARES the action: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `adset_id` (string, required): Ad set ID to create the ad in - `name` (string, required): Ad name - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `object_story_id` (string, optional): Optional: existing post ID (format pageId_postId) to preserve social proof - `creative_id` (string, optional): Optional: existing creative ID to reuse (used if no object_story_id) - `status` (string, optional): Ad status (default PAUSED). one of `PAUSED`, `ACTIVE`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_ad", "arguments": { "adset_id": "120200000000000002", "name": "Nordlicht | Spring launch", "ad_account_id": "act_1234567890" } } } ``` #### create_ad_from_post https://www.apogee.ad/developers/mcp/create-ad-from-post/ Prepare an ad from an existing post. A new ad on an existing Facebook or Instagram post, which keeps its likes and comments, with every Advantage+ enhancement explicit; paused by default. Prepare a new ad on an EXISTING post, which keeps its likes, comments and shares (list_posts finds them): a Facebook post (object_story_id) or an Instagram post (instagram_media_id + instagram_user_id + page_id). A Facebook post keeps its own text, link and call to action; an Instagram post may get a call to action and its link. url_parameters adds UTMs. advantage_plus.opt_in is required: every enhancement not listed is sent OFF. Returns an action_id and what will be created; nothing happens until execute_action, after the user's explicit OK. New ads with new creatives go through prepare_launch instead. - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `ad_account_id` (string, required): Meta ad account id (act_…), from list_ad_accounts. matches `^(act_)?\d{1,32}$`. - `adset_id` (string, required): The ad set the new ad goes in (list_adsets). matches `^\d{1,40}$`. - `name` (string, required): The new ad's name. 1 to 255 characters. - `advantage_plus` (object, required): Required: the Advantage+ creative enhancements, all explicit - `opt_in` (array of strings, required): Enhancements turned ON; every other one is sent OFF. [] = all off. one of `enhance_cta`, `inline_comment`, `reveal_details_over_time`, `show_destination_blurbs`, `show_summary`, `text_optimizations`, `product_tags`, `optimize_website_destination`, `shop`, `product_browsing`, `browser_addons`, `image_brightness_and_contrast`, `image_templates`, `image_touchups`, `video_auto_crop`, `video_filtering`. - `object_story_id` (string, optional): A Facebook post: _ (list_posts). matches `^\d{1,40}_\d{1,40}$`. - `instagram_media_id` (string, optional): An Instagram post (list_posts source instagram or ads). matches `^\d{1,40}$`. - `instagram_user_id` (string, optional): Instagram post: the account that published it. matches `^\d{1,40}$`. - `page_id` (string, optional): Instagram post: the Facebook page linked to that account. matches `^\d{1,40}$`. - `call_to_action` (string, optional): Instagram post only: e.g. SHOP_NOW, LEARN_MORE. up to 40 characters. - `website_url` (string, optional): Instagram post only, with call_to_action. up to 2,048 characters. - `url_parameters` (string, optional): UTM parameters added to the post's link. up to 1,000 characters. - `status` (string, optional): Default PAUSED. one of `PAUSED`, `ACTIVE`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_ad_from_post", "arguments": { "ad_account_id": "act_1234567890", "adset_id": "120200000000000002", "name": "Nordlicht | Spring launch", "advantage_plus": { "opt_in": [ "enhance_cta" ] } } } } ``` #### execute_action https://www.apogee.ad/developers/mcp/execute-action/ Run a prepared action. Runs a prepared action (or a prepared campaign), once, after your explicit OK. Returns what was created. Run an action prepared by a pause / activate / budget / duplicate / create / targeting tool, create_campaign, create_ad_from_post, or (TikTok / Snapchat) set_platform_status / set_platform_daily_budget (its action_id), only after the user explicitly approved what it will do. An action runs once, within 24 hours. The answer lists what was created or changed (details: new ids, and for a duplicated ad the method that kept its post). - Effect: Changes the ad account or acts as the Page, once, after the user's explicit yes. - Scope: `launch or actions` - Plans: Pro and Scale - Credits: 2 credits per change - REST API: Confirm action (`POST /actions/{id}/confirm`) Parameters: - `action_id` (string, required). up to 64 characters. - `confirm` (constant, required): true = the user approved this action. always `true`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "execute_action", "arguments": { "action_id": "1c9e5a27-8b34-4f0d-a6e2-5d7c3b9f4a18", "confirm": true } } } ``` ### TikTok and Snapchat `list_ad_accounts`, `list_campaigns`, `list_adsets`, `list_ads`, `get_launch_questions` and `prepare_launch` take `platform: "tiktok"` or `"snapchat"` (default `"meta"`); `launch` and `launch_status` follow the plan whatever its platform: the same interview, the same recap, the same explicit yes. The tools below read their numbers and change their status and budgets, one platform at a time: money in the ad account's currency, days in its time zone, TikTok and Snapchat never added up. #### get_platform_overview https://www.apogee.ad/developers/mcp/get-platform-overview/ TikTok / Snapchat account overview. One TikTok or Snapchat ad account's totals over a period: spend, impressions, clicks, CTR, CPC, CPM, reach, purchases, revenue, ROAS, CPA, add to carts. For Meta: `get_account_overview`. TikTok or Snapchat: one ad account's totals over a date range (spend, impressions, clicks, CTR, CPC, CPM, reach, purchases, revenue, ROAS, CPA, add to cart). Money in the ad account's currency, days in its time zone; one platform at a time (never add TikTok and Snapchat up). For Meta: get_account_overview. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `date_preset` (string, optional): Default last_7d (the 7 days before today, today left out), in the ad account's time zone. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `since` (string, optional): YYYY-MM-DD, with until (both included), instead of date_preset. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, optional): YYYY-MM-DD, with since. matches `^\d{4}-\d{2}-\d{2}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_platform_overview", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001" } } } ``` #### get_platform_performance https://www.apogee.ad/developers/mcp/get-platform-performance/ TikTok / Snapchat performance by campaign, ad set or ad. One row per campaign, ad set (TikTok ad group, Snapchat ad squad) or ad, with its status, daily budget and metrics, sorted by spend by default. TikTok's Smart+ ads included. TikTok or Snapchat: one row per campaign, ad set (TikTok ad group / Snapchat ad squad) or ad of an ad account over a date range, with its status, its daily budget and its metrics (spend, impressions, clicks, CTR, CPC, CPM, purchases, revenue, ROAS, CPA…), sorted (default by spend). Filters: campaign_id, adset_id, status, min_spend. TikTok ads: the standard ads and the Smart+ ads (smart_plus: true), not the variations TikTok generates. Money in the ad account's currency, days in its time zone; one platform at a time (never add TikTok and Snapchat up). For Meta: get_campaigns / get_adsets / get_ads. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `level` (string, required): campaign, adset (a TikTok ad group / a Snapchat ad squad) or ad. one of `campaign`, `adset`, `ad`. - `date_preset` (string, optional): Default last_7d (the 7 days before today, today left out), in the ad account's time zone. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `since` (string, optional): YYYY-MM-DD, with until (both included), instead of date_preset. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, optional): YYYY-MM-DD, with since. matches `^\d{4}-\d{2}-\d{2}$`. - `campaign_id` (string, optional): Only this campaign (level campaign), or its ad groups / ad squads / ads. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `adset_id` (string, optional): Only this ad group / ad squad (level adset), or its ads (level ad). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `status` (string, optional): Only the active or only the paused ones. one of `active`, `paused`. - `sort` (string, optional): Default spend. cpa, cpc and cpm: the lowest first; the others: the highest first. one of `spend`, `revenue`, `roas`, `purchases`, `ctr`, `clicks`, `impressions`, `cpa`, `cpc`, `cpm`. - `min_spend` (number, optional): Only the rows that spent at least this much (account currency). ≥ 0. - `limit` (integer, optional): Default 50. 1 to 200. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_platform_performance", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "level": "campaign" } } } ``` #### get_platform_daily_breakdown https://www.apogee.ad/developers/mcp/get-platform-daily-breakdown/ TikTok / Snapchat daily breakdown. Day by day (92 days at most, every day listed) for the ad account or one campaign, ad set or ad, with the period's totals. For Meta: `get_daily_breakdown`. TikTok or Snapchat: day by day over a date range (92 days at most, every day listed, zeros when nothing delivered), for the whole ad account or ONE campaign_id / adset_id / ad_id; with the range's totals. Money in the ad account's currency, days in its time zone; one platform at a time (never add TikTok and Snapchat up). For Meta: get_daily_breakdown. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get insights (`GET /ad-accounts/{acc}/insights`) Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `date_preset` (string, optional): Default last_7d (the 7 days before today, today left out), in the ad account's time zone. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `since` (string, optional): YYYY-MM-DD, with until (both included), instead of date_preset. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, optional): YYYY-MM-DD, with since. matches `^\d{4}-\d{2}-\d{2}$`. - `campaign_id` (string, optional): One campaign's days instead of the whole ad account. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `adset_id` (string, optional): One ad group / ad squad's days. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `ad_id` (string, optional): One ad's days. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_platform_daily_breakdown", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001" } } } ``` #### get_platform_top_performers https://www.apogee.ad/developers/mcp/get-platform-top-performers/ TikTok / Snapchat top and worst performers. The best and the worst ads, ad sets or campaigns by a metric, among those that spent: what to scale, what to cut. For Meta: `get_top_performers`. TikTok or Snapchat: the best and the worst ads (or campaigns / ad sets: level) by a metric over a date range, among those that spent: "what is working", "what should I scale or cut". Money in the ad account's currency, days in its time zone; one platform at a time (never add TikTok and Snapchat up). For Meta: get_top_performers. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `metric` (string, required): What ranks them: roas, cpa, ctr, spend, purchases, revenue, cpc, cpm, clicks or impressions (cpa, cpc, cpm: the lowest is the best). one of `spend`, `revenue`, `roas`, `purchases`, `ctr`, `clicks`, `impressions`, `cpa`, `cpc`, `cpm`. - `level` (string, optional): Default ad. one of `campaign`, `adset`, `ad`. - `date_preset` (string, optional): Default last_7d (the 7 days before today, today left out), in the ad account's time zone. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `since` (string, optional): YYYY-MM-DD, with until (both included), instead of date_preset. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, optional): YYYY-MM-DD, with since. matches `^\d{4}-\d{2}-\d{2}$`. - `campaign_id` (string, optional): Only this campaign's. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `adset_id` (string, optional): Only this ad group / ad squad's ads. matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `min_spend` (number, optional): Only those that spent at least this much (account currency); those that spent nothing are always left out. ≥ 0. - `limit` (integer, optional): How many best and worst, default 10. 1 to 50. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_platform_top_performers", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "metric": "spend" } } } ``` #### list_targeting https://www.apogee.ad/developers/mcp/list-targeting/ List TikTok / Snapchat targeting. TikTok interests, behaviors and languages, Snapchat interests and countries, with the launch field their ids go in. Meta's targeting: `search_targeting`. TikTok and Snapchat targeting choices, as the app's ad group / ad squad drawers list them, with the plan field their ids go in (use): TikTok interests (tiktok.adgroup_settings.interest_category_ids), behaviors (action_category_ids), languages (languages); Snapchat interests (snapchat.adsquad_settings.interest_ids), countries (countries). Meta's targeting: search_targeting. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List targeting options (`GET /ad-accounts/{acc}/targeting-options`) Parameters: - `platform` (string, required). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): Ad account id (list_ad_accounts). matches `^[\w-]{1,64}$`. - `type` (string, required): TikTok: interests, behaviors, languages. Snapchat: interests, countries. one of `interests`, `behaviors`, `languages`, `countries`. - `country` (string, optional): Snapchat interests: the targeted country, as the app reads them (its first targeted country). matches `^[A-Za-z]{2}$`. - `search` (string, optional): Filter on the name. up to 100 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_targeting", "arguments": { "platform": "tiktok", "ad_account_id": "act_1234567890", "type": "interests" } } } ``` #### list_tiktok_posts https://www.apogee.ad/developers/mcp/list-tiktok-posts/ List TikTok posts (Spark Ads). TikTok Spark Ads: the existing posts a launch can run as ads, keeping their likes and comments. `source: "account"` (default): the video posts of one of the brand's TikTok accounts (the first one, or `identity_id` from the launch interview), a page at a time; `source: "authorized"`: the posts creators authorized for this ad account. Each post says whether it can run and gives its `spark_post` for `prepare_launch`. TikTok Spark Ads: existing TikTok posts a launch can run as ads, keeping their likes and comments (the post's caption is the ad text). source "account" (default): the video posts of one of the brand's TikTok accounts (identity_id / identity_type from get_launch_questions identity.choices; default: the first one), a page at a time (next_cursor). source "authorized": the posts creators authorized for this ad account with a code (bound in the Apogee app). Each post says whether it can run (usable, refusals, warnings) and gives its spark_post, to put in prepare_launch ads[].spark_post instead of files. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List TikTok posts (`GET /ad-accounts/{acc}/tiktok-posts`) Parameters: - `ad_account_id` (string, required): A TikTok advertiser id (list_ad_accounts platform tiktok). matches `^[\w-]{1,64}$`. - `source` (string, optional). one of `account`, `authorized`. - `identity_id` (string, optional): source account: the TikTok account (get_launch_questions identity.choices.identities). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `identity_type` (string, optional). one of `TT_USER`, `BC_AUTH_TT`. - `bc_id` (string, optional): BC_AUTH_TT: its Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `cursor` (string, optional): source account: next_cursor of the previous page. up to 200 characters. - `page` (integer, optional): source authorized: the page. 1 to 100. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_tiktok_posts", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_tiktok_music https://www.apogee.ad/developers/mcp/list-tiktok-music/ List TikTok music (carousels). TikTok carousels (image ads run with a music): the music of TikTok's carousel library a launch can use. `q`: a keyword (e.g. "upbeat"); without one, the music this ad account used. Each gives its `music_id` for `prepare_launch` (`tiktok.ad_settings.music_id`); none given, TikTok picks one for each carousel at the launch. TikTok carousels (image ads run with a music): the music of TikTok's carousel library a launch can use. q: a keyword (e.g. "upbeat"); without one, the musics this ad account used. Each gives its music_id for prepare_launch tiktok.ad_settings.music_id; none given, TikTok picks one for each carousel at the launch. Previews play from TikTok. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List TikTok music (`GET /ad-accounts/{acc}/tiktok-music`) Parameters: - `ad_account_id` (string, required): A TikTok advertiser id (list_ad_accounts platform tiktok). matches `^[\w-]{1,64}$`. - `q` (string, optional): A keyword. 2 to 100 characters. - `page` (integer, optional). 1 to 50. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_tiktok_music", "arguments": { "ad_account_id": "act_1234567890" } } } ``` #### list_platform_templates https://www.apogee.ad/developers/mcp/list-platform-templates/ List Quick Bulk Edit templates. TikTok and Snapchat: the Quick Bulk Edit templates saved in the Apogee app ("Save as template" in the ad group / ad squad and ad params), per ad account. `level: "adgroup"`: an ad group's (TikTok) or an ad squad's (Snapchat) settings; `level: "ad"`: an ad's (TikTok account, texts, URL, call to action…). `prepare_launch` uses one as its base: `tiktok.adgroup_template_id` / `tiktok.ad_template_id`, `snapchat.adsquad_template_id` / `snapchat.ad_template_id`. Saving and deleting templates stay in the app. TikTok / Snapchat: the Quick Bulk Edit templates saved in the Apogee app ("Save as template" in the ad group / ad squad and ad params), per ad account. level adgroup = an ad group's (TikTok) or ad squad's (Snapchat) settings, ad = an ad's (TikTok account, texts, URL, CTA…). Use one in prepare_launch: tiktok.adgroup_template_id / tiktok.ad_template_id, snapchat.adsquad_template_id / snapchat.ad_template_id (the template's settings as the base, the plan's settings over them). Saving and deleting stay in the app. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List platform templates (`GET /ad-accounts/{acc}/platform-templates`) Parameters: - `platform` (string, required). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): Ad account id (list_ad_accounts). matches `^[\w-]{1,64}$`. - `level` (string, required): adgroup (ad group / ad squad) or ad. one of `adgroup`, `ad`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_platform_templates", "arguments": { "platform": "tiktok", "ad_account_id": "act_1234567890", "level": "adgroup" } } } ``` #### set_platform_status https://www.apogee.ad/developers/mcp/set-platform-status/ Prepare a TikTok / Snapchat pause or activation. Prepares pausing or turning on TikTok or Snapchat campaigns, ad sets or ads; `execute_action` runs it after your OK. TikTok GMV Max and legacy Smart+ campaigns, and the ads TikTok generates inside Smart+ ad groups, are changed in TikTok Ads Manager. TikTok or Snapchat: prepare pausing or activating campaigns, ad sets (TikTok ad groups / Snapchat ad squads) or ads of one ad account (ids from get_platform_performance). In Apogee this only PREPARES the change: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). TikTok GMV Max and legacy Smart+ campaigns, and the ads TikTok generates inside Smart+ ad groups, are changed in TikTok Ads Manager. - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `level` (string, required): campaign, adset (a TikTok ad group / a Snapchat ad squad) or ad. one of `campaign`, `adset`, `ad`. - `ids` (array of strings, required): 1 to 50 campaigns, ad sets or ads of that level. 1 to 50 items; matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `status` (string, required): paused (stops delivering) or active (delivers again). one of `paused`, `active`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "set_platform_status", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "level": "campaign", "ids": [ "120200000000000001" ], "status": "paused" } } } ``` #### set_platform_daily_budget https://www.apogee.ad/developers/mcp/set-platform-daily-budget/ Prepare a TikTok / Snapchat daily budget change. Prepares a new daily budget for a TikTok or Snapchat campaign or ad set, in the ad account's currency; `execute_action` runs it after your OK. Refused above the safety cap, on a lifetime budget, on an ad set whose campaign holds the budget, and on TikTok Smart+ budgets. TikTok or Snapchat: prepare a new daily budget for a campaign (its campaign budget) or an ad set (TikTok ad group / Snapchat ad squad), in the ad account's currency. Refused above the safety cap, on a lifetime budget, on an ad set whose campaign holds the budget, and on TikTok Smart+ budgets. In Apogee this only PREPARES the change: it returns an action_id and what will change. Show it to the user and call execute_action only after their explicit OK (valid 24 hours). - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `actions` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create action (`POST /actions`) Parameters: - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `level` (string, required): campaign (its campaign budget) or adset (a TikTok ad group / a Snapchat ad squad). one of `campaign`, `adset`. - `id` (string, required): The campaign or ad set (get_platform_performance shows its daily_budget). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `daily_budget` (number, required): The new daily budget, in the ad account's currency (not cents or micro). > 0. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "set_platform_daily_budget", "arguments": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "level": "campaign", "id": "120200000000000001", "daily_budget": 50 } } } ``` ### Bridge Ads of one platform launched on another (Meta → TikTok or Snapchat, and back), or a Meta → Meta copy between two ad accounts. Prepared, shown in full, then run after your explicit yes; a plan runs once, within 24 hours. #### prepare_bridge_transfer https://www.apogee.ad/developers/mcp/prepare-bridge-transfer/ Prepare a Bridge transfer. Prepares a transfer without transferring anything: the source ads (or a Meta ad set's or campaign's ads), the destination campaign and its existing ad set / ad group / ad squad or a new one copied from a template. Returns the recap and a `plan_id`; `ready: false` lists what to ask (a TikTok identity, a Snapchat public profile, a website…), then prepare again with `extra_fields`. Incompatibilities are listed: your yes accepts them. For a Meta → Meta copy, the destination's fields go in `mapping`. Prepare a Bridge transfer WITHOUT transferring anything: the plan and its recap, plan_id valid 24 hours. Cross-platform: ads of one platform launched on another — e.g. Meta ads (source.ad_ids, or a Meta ad set / campaign = its ads: source.adset_ids / campaign_ids) into a TikTok or Snapchat campaign (destination.campaign_id), in an existing ad group / ad squad (destination.adset_id) or a new one copied from a template (destination.new_adset). The destination's identity / public profile is inherited from the ads already in that ad group when it can be; what is still missing is listed (ready:false + recap.missing): ask the user, then prepare again with extra_fields. Incompatibilities (a CTA remapped, a non-vertical source…) are listed: the user's yes acknowledges them. Meta → Meta (both platforms meta, two different ad accounts): ads into destination.adset_id, ad sets into destination.campaign_id, or whole campaigns; recap.items shows what gets created (each ad on its source's post or not); recap.fields lists what the destination needs (pixel, page, audience, budget…): give values in mapping ("drop" where allow_drop), then prepare again. Use list_ad_accounts / list_campaigns / list_adsets / list_ads (with platform) to find the ids. - Effect: Stores a plan or an action and says what it will do. Nothing changes on the ad account until it is confirmed. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Create bridge transfer (`POST /bridge-transfers`) Parameters: - `source` (object, required): Where the ads come from: one of ad_ids, adset_ids, campaign_ids - `platform` (string, required). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `ad_ids` (array of strings, optional): The ads to transfer (cross-platform: 50 max per run). 1 to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_ids` (array of strings, optional): Meta: ad sets (cross-platform: their ads; Meta → Meta: the ad sets themselves, 20 max). 1 to 20 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `campaign_ids` (array of strings, optional): Meta: campaigns (cross-platform: their ads; Meta → Meta: the campaigns themselves, 5 max). 1 to 5 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `destination` (object, required) - `platform` (string, required). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `campaign_id` (string, optional): Required, except for a Meta → Meta copy of campaigns. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_id` (string, optional): An existing destination ad set / TikTok ad group / Snapchat ad squad (list_adsets). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `new_adset` (object, optional): Cross-platform: a new ad set copied from a template, instead of adset_id - `template_adset_id` (string, required): An ad set / ad group / ad squad of the destination campaign whose settings the new one copies. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `name` (string, required). 1 to 255 characters. - `daily_budget` (number, optional): Account currency; not for a TikTok CBO campaign. > 0. - `launch_status` (string, optional): Cross-platform. Default CREATED_PAUSED; SCHEDULED_* only with new_adset (+ schedule). one of `CREATED_PAUSED`, `LIVE_NOW`, `SCHEDULED_PAUSED`, `SCHEDULED_LIVE`. - `schedule` (object, optional): A new ad set's schedule, in the destination account's time zone - `date` (string, required). matches `^\d{4}-\d{2}-\d{2}$`. - `time` (string, required). matches `^\d{2}:\d{2}$`. - `end_date` (string, optional). matches `^\d{4}-\d{2}-\d{2}$`. - `end_time` (string, optional). matches `^\d{2}:\d{2}$`. - `extra_fields` (object, optional): What the plan asked for (recap.missing): the destination's identity / profile / page, URL, CTA, headline - `identity_id` (string, optional): TikTok: the identity the ads run under. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `identity_type` (string, optional): TikTok: one of the brand's TikTok accounts, TT_USER or BC_AUTH_TT through its Business Center (TikTok refuses a custom identity on these ads). one of `TT_USER`, `BC_AUTH_TT`. - `identity_bc_id` (string, optional): TikTok BC_AUTH_TT: the Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `profile_id` (string, optional): Snapchat: the public profile. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `page_id` (string, optional): Meta: the Facebook page. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `website_url` (string, optional). up to 2,048 characters. - `call_to_action` (string, optional). up to 40 characters. - `headline` (string, optional): Snapchat: 34 characters. up to 255 characters. - `meta_texts` (map, optional): Destination Meta: each source ad's texts (by source ad id), instead of the ones the plan suggested (recap.meta_texts). keys match `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `primary_text` (string, optional). up to 2,200 characters. - `headline` (string, optional). up to 255 characters. - `description` (string, optional). up to 255 characters. - `options` (object, optional): Meta → Meta copy only - `keep_social_proof` (boolean, optional): Default true: reuse the source post (likes, comments) when Meta allows it - `status` (string, optional): After the copy: PAUSED (default), ACTIVE, or INHERIT (on only what is on in the source). one of `PAUSED`, `ACTIVE`, `INHERIT`. - `name_suffix` (string, optional). up to 100 characters. - `mapping` (map, optional): Meta → Meta copy: values for the plan's fields (recap.fields: pixel, page, audience, budget…), by key Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "prepare_bridge_transfer", "arguments": { "source": { "platform": "meta", "ad_account_id": "act_1234567890", "ad_ids": [ "120200000000000003" ] }, "destination": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "campaign_id": "1800000000000000001", "adset_id": "1800000000000000002" } } } } ``` #### bridge_transfer https://www.apogee.ad/developers/mcp/bridge-transfer/ Run a prepared Bridge transfer. Runs a prepared transfer, once, after your explicit yes; the plan is checked again first. Answers with the outcome (completed, partial, failed…) within about 25 seconds, or `running`. A Meta → Meta copy reuses each ad's post (likes, comments) when Meta lets the destination use it, and copies it as a new post otherwise. Run a Bridge transfer prepared by prepare_bridge_transfer, only after the user explicitly said yes to its full recap. The plan is checked again first (if anything changed, nothing is transferred: prepare it again). Answers within ~25 s with the outcome, or status running: then follow it with bridge_transfer_status. A plan runs once. - Effect: Changes the ad account or acts as the Page, once, after the user's explicit yes. - Scope: `launch` - Plans: Pro and Scale - Credits: 15 credits per ad created, counted at the end (an ad that failed is free), plus 120 for a transfer to Meta (texts written by AI) - REST API: Confirm bridge transfer (`POST /bridge-transfers/{id}/confirm`) Parameters: - `plan_id` (string, required). up to 64 characters. - `confirm` (constant, required): true = the user said yes to the recap. always `true`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "bridge_transfer", "arguments": { "plan_id": "7b0f3c52-4d1e-4c8a-9f61-2a5e8d3c1b90", "confirm": true } } } ``` #### bridge_transfer_status https://www.apogee.ad/developers/mcp/bridge-transfer-status/ Follow a Bridge transfer. Where a transfer is: running, completed, partial (with the failures), failed, not run, or unknown (check the destination before running it again). Where a Bridge transfer is, by its plan_id: prepared, running, completed, partial (some ads failed: failures), failed, not_run (nothing transferred) or unknown (no outcome recorded: check the destination before running it again). Once finished, the ids it created: ad_ids, and adsets (cross-platform) or campaign_ids / adset_ids (Meta → Meta). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get bridge transfer (`GET /bridge-transfers/{id}`) Parameters: - `plan_id` (string, required). up to 64 characters. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "bridge_transfer_status", "arguments": { "plan_id": "7b0f3c52-4d1e-4c8a-9f61-2a5e8d3c1b90" } } } ``` #### list_bridge_transfers https://www.apogee.ad/developers/mcp/list-bridge-transfers/ List the ads already bridged. The ads already bridged from a source ad account, newest first, with where each one went. The ads already bridged from a source ad account (the app's Bridged tab), newest first: each source ad with where it went (platform, ad account, ad, ad set). Yours, and another Apogee user's from the same source account when the account they went to is yours too. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: List bridged ads (`GET /bridged-ads`) Parameters: - `source_ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `source_platform` (string, optional): Default meta. one of `meta`, `tiktok`, `snapchat`. - `limit` (integer, optional): Default 50. 1 to 200. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "list_bridge_transfers", "arguments": { "source_ad_account_id": "act_1234567890" } } } ``` #### get_bridge_stats https://www.apogee.ad/developers/mcp/get-bridge-stats/ Bridge stats per creative. The Bridge's Stats tab: each creative bridged from a Meta ad account, its Meta ad next to its TikTok and Snapchat copies, with their spend and results over the period (7-day click attribution everywhere), each platform in its own currency. The Bridge's Stats tab: each creative bridged from a Meta ad account (yours, and others' sent to accounts you hold too), newest first: its source ad on Meta next to its copies on TikTok and Snapchat over since → until (7-day click attribution on every platform). Per platform: status (ok, no_target = never bridged there, not_connected = that account is not connected to this Apogee account, error), the ads, inputs (raw sums) and kpis (spend, revenue, ROAS, CPM, CTR, CPC, cost per purchase, hook and hold rates…), each in its own ad account's currency: never add platforms together. - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get bridge stats (`GET /bridged-ads/stats`) Parameters: - `source_ad_account_id` (string, required): The Meta ad account the ads were bridged from (act_…). matches `^(act_)?\d{1,32}$`. - `since` (string, required): First day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, required): Last day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_bridge_stats", "arguments": { "source_ad_account_id": "act_1234567890", "since": "2026-10-01", "until": "2026-10-21" } } } ``` #### get_bridge_daily_stats https://www.apogee.ad/developers/mcp/get-bridge-daily-stats/ Bridge stats per day. The Bridge's Daily tab: day by day, per platform, the spend, revenue, ROAS, impressions, link clicks and purchases of every creative bridged from a Meta ad account (366 days at most). Platforms are never added up. The Bridge's Daily tab: day by day over since → until (366 days max), per platform (Meta source ads, their TikTok and Snapchat copies), the spend, revenue, ROAS, impressions, link clicks and purchases summed over every creative bridged from a Meta ad account, with each platform's totals and currency. Platforms are never summed together (each in its own ad account's currency). - Effect: Reads only: nothing is changed, in Apogee or on the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - REST API: Get bridge daily stats (`GET /bridged-ads/daily-stats`) Parameters: - `source_ad_account_id` (string, required): The Meta ad account the ads were bridged from (act_…). matches `^(act_)?\d{1,32}$`. - `since` (string, required): First day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, required): Last day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. Example (MCP tools/call): ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_bridge_daily_stats", "arguments": { "source_ad_account_id": "act_1234567890", "since": "2026-10-01", "until": "2026-10-21" } } } ``` ## REST API operations 48 operations in 8 groups, base URL https://api.apogee.ad/api/v1. Each one: its page, scope, plans, credits, rate limit, parameters, body, response fields, errors and a curl example. ### Account Who am I, the API itself #### Get the account and key: GET /me https://www.apogee.ad/developers/api/get-me/ The Apogee account and the API key in use. Any valid key may call it, whatever its scopes. - Scope: `any valid key` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `whoami` Response 200 - `user` (object) - `id` (string) - `email` (string or null) - `name` (string or null) - `key` (object) - `id` (string) - `scopes` (array of strings). one of `read`, `launch`, `actions`. - `name` (string or null) - `expires_at` (string (date-time) or null) - `api_version` (string) - `platforms` (array of strings) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/me' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Ad accounts Ad accounts and what a launch picks from #### List ad accounts: GET /ad-accounts https://www.apogee.ad/developers/api/list-ad-accounts/ The ad accounts connected to Apogee. platform: meta (default), tiktok or snapchat: the ids a launch plan, the launch questions and the lists below take with the same platform. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_ad_accounts` Parameters: - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. - `platform` (string, optional, in query): meta (default), tiktok or snapchat. one of `meta`, `tiktok`, `snapchat`. Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `currency` (string or null) - `timezone` (string or null) - `timezone_offset_hours_utc` (number or null) - `business` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List pages: GET /ad-accounts/{acc}/pages https://www.apogee.ad/developers/api/list-pages/ Facebook pages usable with the ad account, with their linked Instagram account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_pages` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `instagram` (object or null) - `id` (string) - `username` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/pages' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List pixels: GET /ad-accounts/{acc}/pixels https://www.apogee.ad/developers/api/list-pixels/ Meta pixels of the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_pixels` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/pixels' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get pixel events: GET /ad-accounts/{acc}/pixels/{pixel}/events https://www.apogee.ad/developers/api/get-pixel-events/ A pixel's events: standard events (with their volume), custom events and the account's custom conversions. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `pixel` (string, required, in path). matches `^\d{1,40}$`. Response 200 - `standard` (array of objects) - `event` (string) - `name` (string or null) - `count` (number or null) - `never_fired` (array of strings) - `custom_events` (array of objects) - `custom_event_str` (string) - `count` (number or null) - `custom_conversions` (array of objects) - `custom_conversion_id` (string) - `name` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/pixels/200000000000001/events' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List catalogs: GET /ad-accounts/{acc}/catalogs https://www.apogee.ad/developers/api/list-catalogs/ Product catalogs of the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `product_count` (integer) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/catalogs' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List product sets: GET /catalogs/{id}/product-sets https://www.apogee.ad/developers/api/list-product-sets/ Product sets of a catalog. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` Parameters: - `id` (string, required, in path): Catalog id. matches `^\d{1,40}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `product_count` (integer) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/catalogs/120200000000000001/product-sets' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List custom audiences: GET /ad-accounts/{acc}/custom-audiences https://www.apogee.ad/developers/api/list-custom-audiences/ Custom and lookalike audiences of the ad account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_custom_audiences` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `subtype` (string or null) - `delivery_status` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/custom-audiences' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List templates: GET /ad-accounts/{acc}/templates https://www.apogee.ad/developers/api/list-templates/ The ad set or ad templates saved in Apogee for this account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `kind` (string, required, in query): adset or ad. one of `adset`, `ad`. Response 200 - `data` (array of objects) - `id` (string) - `kind` (string). one of `adset`, `ad`. - `name` (string or null) - `objective` (string or null) - `created_at` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/templates?kind=adset' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List naming conventions: GET /ad-accounts/{acc}/naming-conventions https://www.apogee.ad/developers/api/list-naming-conventions/ The naming conventions saved in Apogee for this account (they read the concepts, hooks and formats in file names). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/naming-conventions' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List instagram accounts: GET /pages/{page}/instagram-accounts https://www.apogee.ad/developers/api/list-instagram-accounts/ The Instagram accounts an ad of a Facebook page can run with. With ad_account_id: the page's own first, then the ones assigned to that ad account in Business Settings, the one a launch uses marked recommended (a page without its own Instagram account runs with the ad account's). Without it: the page's linked account only. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_instagram_accounts` Parameters: - `page` (string, required, in path): Facebook page id. matches `^\d{1,40}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `ad_account_id` (string, optional, in query): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. Response 200 - `data` (array of objects) - `id` (string) - `username` (string or null) - `name` (string or null) - `linked` (boolean) - `recommended` (boolean) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/pages/100000000000001/instagram-accounts' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List lead forms: GET /pages/{page}/lead-forms https://www.apogee.ad/developers/api/list-lead-forms/ Instant forms (lead forms) of a Facebook page. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_lead_forms` Parameters: - `page` (string, required, in path): Facebook page id. matches `^\d{1,40}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `status` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/pages/100000000000001/lead-forms' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List partnership creators: GET /pages/{page}/partnership-creators https://www.apogee.ad/developers/api/list-partnership-creators/ Creators with an approved partnership with the page (partnership ads): Instagram creators who approved its Instagram account, Facebook creators whose Page accepted it. source instagram: give creator_ig_id in the launch plan's ad_settings.partnership; source facebook: creator_fb_page_id alone. A Facebook creator is invited from the app (Ad params → Identity → Partnership ad). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` Parameters: - `page` (string, required, in path): Facebook page id. matches `^\d{1,40}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `source` (string). one of `instagram`, `facebook`. - `creator_ig_id` (string or null) - `creator_username` (string or null) - `creator_fb_page_id` (string or null) - `creator_name` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/pages/100000000000001/partnership-creators' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List ad posts: GET /ad-accounts/{acc}/posts https://www.apogee.ad/developers/api/list-ad-posts/ The posts behind the account's ads (dark posts included): reuse one with a create_ad_from_post action. One row per post, with the ads using it. A new ad on the same post keeps its likes, comments and shares. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_posts` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `campaign_id` (string, optional, in query). matches `^\d{1,40}$`. Response 200 - `data` (array of objects) - `object_story_id` (string or null): A Facebook post: _ - `instagram_media_id` (string or null) - `instagram_user_id` (string or null) - `page_id` (string or null) - `instagram_permalink` (string or null) - `permalink` (string or null) - `message` (string or null) - `caption` (string or null) - `created_time` (string or null) - `thumbnail_url` (string or null) - `media_type` (string or null) - `likes` (number or null) - `comments` (number or null) - `ads` (array of objects): /ad-accounts/{acc}/posts: the ads using this post - `id` (string) - `name` (string or null) - `status` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/posts' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List page posts: GET /pages/{page}/posts https://www.apogee.ad/developers/api/list-page-posts/ A page's published Facebook posts, or the posts of its Instagram account. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_posts` Parameters: - `page` (string, required, in path): Facebook page id. matches `^\d{1,40}$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `source` (string, optional, in query): Default facebook. one of `facebook`, `instagram`. - `instagram_account_id` (string, optional, in query): source instagram. matches `^\d{1,40}$`. Response 200 - `data` (array of objects) - `object_story_id` (string or null): A Facebook post: _ - `instagram_media_id` (string or null) - `instagram_user_id` (string or null) - `page_id` (string or null) - `instagram_permalink` (string or null) - `permalink` (string or null) - `message` (string or null) - `caption` (string or null) - `created_time` (string or null) - `thumbnail_url` (string or null) - `media_type` (string or null) - `likes` (number or null) - `comments` (number or null) - `ads` (array of objects): /ad-accounts/{acc}/posts: the ads using this post - `id` (string) - `name` (string or null) - `status` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/pages/100000000000001/posts' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Search targeting: GET /targeting/search https://www.apogee.ad/developers/api/search-targeting/ Search Meta targeting: countries, locations (regions, cities), interests, behaviors, demographics, locales. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `search_targeting` Parameters: - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `type` (string, required, in query). one of `countries`, `locations`, `interests`, `locales`, `behaviors`, `demographics`. - `q` (string, optional, in query): Required except for behaviors and demographics. up to 100 characters. Response 200 - `data` (array of objects) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/targeting/search?type=countries' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List targeting options: GET /ad-accounts/{acc}/targeting-options https://www.apogee.ad/developers/api/list-targeting-options/ TikTok and Snapchat targeting choices (interests, behaviors, languages, countries), each with the launch plan field its id goes in. The app's ad group / ad squad drawer pickers. platform tiktok, type interests (tiktok.adgroup_settings.interest_category_ids), behaviors (action_category_ids) or languages (languages); platform snapchat, type interests (snapchat.adsquad_settings.interest_ids) or countries (countries). The account must be one of yours on that platform (404 otherwise). Meta's targeting: GET /targeting/search. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_targeting` Parameters: - `acc` (string, required, in path): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. - `platform` (string, required, in query): tiktok or snapchat. one of `tiktok`, `snapchat`. - `type` (string, required, in query): TikTok: interests, behaviors, languages. Snapchat: interests, countries. one of `interests`, `behaviors`, `languages`, `countries`. - `country` (string, optional, in query): Snapchat interests: the targeted country, as the app reads them (its first targeted country). matches `^[A-Za-z]{2}$`. Response 200 - `data` (array of objects) - `id` (string): The id or code to put in the launch plan field `use` - `use` (string): The launch plan field it goes in, e.g. tiktok.adgroup_settings.interest_category_ids - `name` (string or null) - `level` (number or null) - `description` (string or null) - `scene` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/targeting-options?platform=tiktok&type=interests' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Structure Campaigns, ad sets, ads #### List campaigns: GET /ad-accounts/{acc}/campaigns https://www.apogee.ad/developers/api/list-campaigns/ Campaigns of the ad account (id, name, objective, CBO budget, status). platform tiktok / snapchat: that platform's campaigns, with its own fields (TikTok: family, budget_mode, can_create_adgroup). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_campaigns` Parameters: - `acc` (string, required, in path): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. - `include_archived` (string, optional, in query). one of `true`, `false`. - `platform` (string, optional, in query): meta (default), tiktok or snapchat. one of `meta`, `tiktok`, `snapchat`. Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `objective` (string or null) - `status` (string or null) - `cbo` (boolean) - `daily_budget` (number or null) - `lifetime_budget` (number or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/campaigns' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List ad sets: GET /ad-accounts/{acc}/adsets https://www.apogee.ad/developers/api/list-adsets/ Ad sets of the ad account, optionally of one campaign. platform tiktok: its ad groups; snapchat: its ad squads (insertable false: a Lens / special ad squad Apogee can't fill). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_adsets` Parameters: - `acc` (string, required, in path): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. - `campaign_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `platform` (string, optional, in query): meta (default), tiktok or snapchat. one of `meta`, `tiktok`, `snapchat`. Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `status` (string or null) - `campaign_id` (string or null) - `daily_budget` (number or null) - `optimization_goal` (string or null) - `countries` (array of strings or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/adsets' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List ads: GET /ad-accounts/{acc}/ads https://www.apogee.ad/developers/api/list-ads/ Ads of the ad account, optionally of one campaign or ad set. platform tiktok / snapchat: that platform's ads (adset_id = a TikTok ad group / a Snapchat ad squad). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_ads` Parameters: - `acc` (string, required, in path): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `search` (string, optional, in query): Filter on the name (contains, case-insensitive). up to 100 characters. - `campaign_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `platform` (string, optional, in query): meta (default), tiktok or snapchat. one of `meta`, `tiktok`, `snapchat`. Response 200 - `data` (array of objects) - `id` (string) - `name` (string or null) - `status` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/ads' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get latest in campaign: GET /ad-accounts/{acc}/campaigns/{id}/latest https://www.apogee.ad/developers/api/get-latest-in-campaign/ The most recently created ad set and ad of a campaign ("copy the latest"). Their ids, names and creation times. A launch plan copies them with copy_adset_from / copy_ad_from (GET /adsets/{id}/template and /ads/{id}/template show what each one holds). null when the campaign has none. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_latest_in_campaign` Parameters: - `acc` (string, required, in path): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `id` (string, required, in path): Campaign id. matches `^\d{1,40}$`. Response 200 - `campaign_id` (string) - `adset` (object or null) - `id` (string) - `name` (string or null) - `status` (string or null) - `created_time` (string or null) - `ad` (object or null) - `id` (string) - `name` (string or null) - `status` (string or null) - `adset_id` (string or null) - `created_time` (string or null) - `use` (object) - `copy_adset_from` (string) - `copy_ad_from` (string) - `note` (string) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/campaigns/120200000000000001/latest' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get an ad set template: GET /adsets/{id}/template https://www.apogee.ad/developers/api/get-adset-template/ What a launch copying this ad set would take (targeting, event, bid, budget). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_templates` Parameters: - `id` (string, required, in path): Ad set id. matches `^\d{1,40}$`. Response 200 Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/adsets/120200000000000001/template' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get ad template: GET /ads/{id}/template https://www.apogee.ad/developers/api/get-ad-template/ What a launch copying this ad would take (page, Instagram, texts, URL, CTA, Advantage+ state). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_templates` Parameters: - `id` (string, required, in path): Ad id. matches `^\d{1,40}$`. Response 200 Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ads/120200000000000001/template' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Insights Performance #### Get insights: GET /ad-accounts/{acc}/insights https://www.apogee.ad/developers/api/get-insights/ Performance of one ad account: by level, or broken down by day, placement, age and gender, or device. Meta (default): the numbers of Apogee's Autopilot (spend, impressions, clicks, CTR, CPC, CPM, purchases, revenue, ROAS, CPA, add to cart…), in the account currency. level=account answers one row; the other levels and the breakdowns answer at most 200 rows in all (the pages stop there). Filters: level campaign takes status; level adset and breakdown daily campaign_id; level ad and the other breakdowns campaign_id and adset_id; level account none (any other filter is refused). Dates: Meta's presets. TikTok and Snapchat: platform=tiktok or snapchat with that platform's ad account id (GET /ad-accounts?platform=…), an account the key's user connected. level account / campaign / adset (TikTok ad groups, Snapchat ad squads) / ad, or breakdown=daily (every day, zeros included; the account or one campaign_id / adset_id / ad_id; 92 days at most). Dates: the same presets, or since + until (YYYY-MM-DD, both included, 366 days at most), in the ad account's time zone. Levels take campaign_id, adset_id, status (active / paused), min_spend and sort (a metric: cpa, cpc, cpm the lowest first, the others the highest first; default spend), 200 rows at most. Rows carry platform, since, until and currency; the campaign / ad set rows their status and daily_budget. Purchases are TikTok's Complete Payment (website) / Snapchat's conversion_purchases (7-day swipe, no view), revenue their value; clicks = TikTok clicks / Snapchat swipes. These reads spend the user's TikTok / Snapchat budget, never Meta's. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `get_account_overview`, `get_campaigns`, `get_adsets`, `get_ads`, `get_daily_breakdown`, `get_ad_performance_by_placement`, `get_ad_performance_by_age_gender`, `get_ad_performance_by_device`, `get_platform_overview`, `get_platform_performance`, `get_platform_daily_breakdown` Parameters: - `acc` (string, required, in path): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `platform` (string, optional, in query): meta (default), tiktok or snapchat. one of `meta`, `tiktok`, `snapchat`. - `level` (string, optional, in query): Default account. one of `account`, `campaign`, `adset`, `ad`. - `date_preset` (string, optional, in query): Default last_7d. one of `today`, `yesterday`, `last_7d`, `last_14d`, `last_30d`, `last_90d`, `this_month`, `last_month`. - `since` (string, optional, in query): TikTok / Snapchat: YYYY-MM-DD, with until (both included), instead of date_preset. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, optional, in query): TikTok / Snapchat: YYYY-MM-DD, with since. matches `^\d{4}-\d{2}-\d{2}$`. - `breakdown` (string, optional, in query): TikTok / Snapchat: daily only. one of `daily`, `placement`, `age_gender`, `device`. - `campaign_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `ad_id` (string, optional, in query): TikTok / Snapchat, breakdown daily: one ad. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `status` (string, optional, in query): Meta: level campaign only; TikTok / Snapchat: any level. one of `active`, `paused`. - `sort` (string, optional, in query): TikTok / Snapchat levels: the metric the rows are sorted by. one of `spend`, `revenue`, `roas`, `purchases`, `ctr`, `clicks`, `impressions`, `cpa`, `cpc`, `cpm`. - `min_spend` (string, optional, in query): TikTok / Snapchat levels: only rows that spent at least this much. matches `^\d{1,9}(\.\d{1,2})?$`. Response 200 - `data` (array of objects) - `level` (string). one of `account`, `campaign`, `adset`, `ad`, `daily`, `placement`, `age_gender`, `device`. - `metrics` (object) - `spend` (number or null) - `impressions` (number or null) - `clicks` (number or null) - `ctr` (number or null) - `cpc` (number or null) - `cpm` (number or null) - `reach` (number or null) - `frequency` (number or null) - `purchases` (number or null) - `revenue` (number or null) - `roas` (number or null) - `cpa` (number or null) - `add_to_cart` (number or null) - `cost_per_atc` (number or null) - `date_preset` (string or null) - `since` (string or null) - `until` (string or null) - `currency` (string or null) - `campaign_id` (string or null) - `adset_id` (string or null) - `daily_budget` (number or null) - `id` (string or null) - `name` (string or null) - `status` (string or null) - `date` (string or null) - `platform` (string or null) - `position` (string or null) - `age` (string or null) - `gender` (string or null) - `device` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/insights' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Creatives Imports, uploads, Drive folders #### Create import: POST /imports https://www.apogee.ad/developers/api/create-import/ Import the creatives behind a link into Apogee storage. Frame.io (project, folder or file; a public share link works without a Frame.io account, 20 to 30 s to read), Dropbox (shared file or folder) or Google Drive (file or folder; Frame.io / Drive / Dropbox accounts are the ones connected in the app). Answers 202 with an import id: follow it with GET /imports/{id} until done. The first 200 files of a link at most, counted in the day's upload budget (429 rate_limited past it). - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - Idempotency-Key: required - MCP: `import_link` Body (application/json): - `url` (string (uri), required): The link. up to 2,048 characters. Response 202 - `id` (string) - `source` (string). one of `frameio`, `google_drive`, `dropbox`. - `jobs` (array of objects) - `id` (string) - `name` (string or null) - `status` (string). one of `PENDING`, `PROCESSING`, `COMPLETED`, `FAILED`, `MISSING`. - `progress` (number or null) - `size_bytes` (number or null) - `ref` (string or null) - `error` (string or null) - `share_name` (string or null) - `done` (boolean) - `created_at` (string or null) - `note` (string): Why an import has no file (done at once) - `truncated` (boolean) - `skipped_too_large` (array of values) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/imports' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "url": "https://www.dropbox.com/scl/fo/example0000/nordlicht-creatives" }' ``` #### Get import: GET /imports/{id} https://www.apogee.ad/developers/api/get-import/ Where an import is; each COMPLETED file has its ref. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `import_status` Parameters: - `id` (string, required, in path): Import id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `id` (string) - `source` (string). one of `frameio`, `google_drive`, `dropbox`. - `jobs` (array of objects) - `id` (string) - `name` (string or null) - `status` (string). one of `PENDING`, `PROCESSING`, `COMPLETED`, `FAILED`, `MISSING`. - `progress` (number or null) - `size_bytes` (number or null) - `ref` (string or null) - `error` (string or null) - `share_name` (string or null) - `done` (boolean) - `created_at` (string or null) - `note` (string): Why an import has no file (done at once) - `truncated` (boolean) - `skipped_too_large` (array of values) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/imports/120200000000000001' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List drive folder: GET /drive-folders https://www.apogee.ad/developers/api/list-drive-folder/ The videos and images of a Google Drive folder, with their format (through the Drive connected to Apogee). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_drive_folder` Parameters: - `url` (string, required, in query): Drive folder link or id. 1 to 2,048 characters. - `recursive` (string, optional, in query): Default true. one of `true`, `false`. - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page Response 200 - `data` (array of objects) - `ref` (string): drive: - `name` (string) - `folder` (string or null) - `kind` (string). one of `video`, `image`. - `mimeType` (string or null) - `width` (number or null) - `height` (number or null) - `format` (string) - `sizeMB` (number or null) - `durationS` (number or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/drive-folders?url=https%3A%2F%2Fwww.dropbox.com%2Fscl%2Ffo%2Fexample0000%2Fnordlicht-creatives' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Create uploads: POST /uploads https://www.apogee.ad/developers/api/create-uploads/ Signed upload URLs for files on the caller's machine (PUT each file, then POST /uploads/confirm). For a Meta, TikTok or Snapchat launch: ad_account_id is the account the files are for (GET /ad-accounts?platform=…). Up to 50 files per call, 500 MB per file; the URLs are valid 1 hour and never stored (an Idempotency-Key is accepted, nothing is replayed). Each file's size is required: an Apogee account may ask for 2000 files and 100 GB a day through the API and the MCP (429 rate_limited past it). - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - Idempotency-Key: accepted - MCP: `get_upload_urls` Body (application/json): - `ad_account_id` (string, required): Ad account id: Meta act_…, a TikTok advertiser id or a Snapchat ad account id (GET /ad-accounts?platform=…). matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `files` (array of objects, required). 1 to 50 items. - `name` (string, required): File name with its extension. 1 to 255 characters. - `size` (integer, required): Bytes (the daily upload budget counts them). > 0; ≤ 524,288,000. - `mime_type` (string, optional). up to 100 characters. Response 201 - `files` (array of objects) - `name` (string) - `upload_url` (string (uri)) - `method` (string). one of `PUT`. - `headers` (map) - `path` (string) - `ref` (string) - `expires_at` (string (date-time)) - `error` (string) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/uploads' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ad_account_id": "act_1234567890", "files": [ { "name": "NORDLICHT_RAIN_HOOK1_9x16.mp4", "size": 48213112, "mime_type": "video/mp4" } ] }' ``` #### Confirm uploads: POST /uploads/confirm https://www.apogee.ad/developers/api/confirm-uploads/ Check uploaded files: present and not empty; each one's ref for the launch plans. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - MCP: `confirm_uploads` Body (application/json): - `paths` (array of strings, required): The paths POST /uploads returned. 1 to 50 items; each: 1 to 2,048 characters. Response 200 - `files` (array of objects) - `path` (string) - `ref` (string) - `size_bytes` (number or null) - `error` (string) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/uploads/confirm' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "paths": [ "120200000000000001" ] }' ``` #### List TikTok posts: GET /ad-accounts/{acc}/tiktok-posts https://www.apogee.ad/developers/api/list-tiktok-posts/ TikTok Spark Ads: the existing posts a launch plan can run as ads (their likes and comments stay). source account (default): the video posts of one of the brand's TikTok accounts (identity_id / identity_type from the launch questions' identity choices; default: the first one), a page at a time (next_cursor). source authorized: the posts creators authorized for this ad account with a code (bound in the Apogee app). Each post says whether it can run (usable, refusals) and gives its spark_post for POST /launch-plans ads[].spark_post. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `list_tiktok_posts` Parameters: - `acc` (string, required, in path): TikTok advertiser id (GET /ad-accounts?platform=tiktok). matches `^\d{1,32}$`. - `source` (string, optional, in query). one of `account`, `authorized`. - `identity_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `identity_type` (string, optional, in query). one of `TT_USER`, `BC_AUTH_TT`. - `bc_id` (string, optional, in query). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `cursor` (string, optional, in query): next_cursor of the previous page - `page` (string, optional, in query). matches `^\d{1,3}$`. Response 200 - `source` (string). one of `account`, `authorized`. - `posts` (array of objects) - `item_id` (string or null) - `spark_post` (object): What goes in a launch plan's ads[].spark_post - `caption` (string or null) - `visibility` (string or null) - `account` (object) - `id` (string or null) - `type` (string or null) - `name` (string or null) - `duration_sec` (number or null) - `preview_url` (string or null) - `authorization` (object): A creator's post: its authorization (status, end, days left) - `usable` (boolean): false: TikTok would refuse it now (refusals) - `refusals` (array of strings) - `warnings` (array of strings) - `available` (boolean) - `account` (object) - `id` (string or null) - `type` (string or null) - `name` (string or null) - `next_cursor` (string or null): source account: the next page - `page` (number or null) - `total_pages` (number or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/7000000000000000001/tiktok-posts' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List TikTok music: GET /ad-accounts/{acc}/tiktok-music https://www.apogee.ad/developers/api/list-tiktok-music/ TikTok carousels: the music a launch plan can give its image ads. TikTok runs images as carousels with a music, from its carousel library. q: a keyword; without one, the musics this ad account used. Each gives its music_id for POST /launch-plans tiktok.ad_settings.music_id; none given, TikTok picks one for each carousel at the launch. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `list_tiktok_music` Parameters: - `acc` (string, required, in path): TikTok advertiser id (GET /ad-accounts?platform=tiktok). matches `^\d{1,32}$`. - `q` (string, optional, in query). 2 to 100 characters. - `page` (string, optional, in query). matches `^\d{1,2}$`. Response 200 - `musics` (array of objects) - `music_id` (string or null) - `title` (string or null) - `author` (string or null) - `duration_sec` (number or null) - `preview_url` (string or null) - `available` (boolean) - `query` (string or null) - `page` (number or null) - `use` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/7000000000000000001/tiktok-music' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List platform templates: GET /ad-accounts/{acc}/platform-templates https://www.apogee.ad/developers/api/list-platform-templates/ TikTok / Snapchat: the Quick Bulk Edit templates saved in the app, for a launch plan. The templates saved in the Apogee app ("Save as template" in the TikTok ad group / Snapchat ad squad and ad params) for this ad account. level adgroup = an ad group's / ad squad's settings, ad = an ad's. A launch plan uses one as its base: tiktok.adgroup_template_id / tiktok.ad_template_id, snapchat.adsquad_template_id / snapchat.ad_template_id. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `list_platform_templates` Parameters: - `acc` (string, required, in path): TikTok advertiser id or Snapchat ad account id (GET /ad-accounts?platform=…). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `platform` (string, required, in query). one of `tiktok`, `snapchat`. - `level` (string, required, in query). one of `adgroup`, `ad`. Response 200 - `templates` (array of objects) - `id` (string or null) - `name` (string or null) - `updated_at` (string or null) - `params` (object): The drawer's params as the app saved them - `platform` (string). one of `tiktok`, `snapchat`. - `level` (string). one of `adgroup`, `ad`. - `available` (boolean): false: templates are not set up on this server Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/ad-accounts/7000000000000000001/platform-templates?platform=tiktok&level=adgroup' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Launches The interview, launch plans, launches #### Get launch questions: POST /ad-accounts/{acc}/launch-questions https://www.apogee.ad/developers/api/get-launch-questions/ The launch interview: every question the app asks before a launch, with the choices read from the account. Send files (names, refs, formats) once the creatives are known: the account's naming convention reads them and the ads are proposed (one creative in 9:16 / 4:5 / 1:1 = one ad). Send the answers so far to get the choices that depend on them. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_launch_questions` Parameters: - `acc` (string, required, in path): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. Body (application/json): - `platform` (string, optional): Default meta. tiktok / snapchat: the same interview and arguments where they mean the same, their own settings under `tiktok` / `snapchat`, groups made by hand under `adgroups` / `adsquads`. one of `meta`, `tiktok`, `snapchat`. - `files` (array of objects, optional). up to 500 items. - `name` (string, required): File name with its extension. 1 to 255 characters. - `ref` (string, optional): drive: or the URL of the file in Apogee storage. up to 2,048 characters. - `mimeType` (string, optional). up to 100 characters. - `kind` (string, optional). one of `video`, `image`. - `format` (string, optional): Measured format (list_drive_folder, ffprobe): 9:16, 4:5, 1:1, 16:9. up to 10 characters. - `folder` (string, optional): Subfolder (list_drive_folder). up to 500 characters. - `durationS` (number, optional): Length in seconds (list_drive_folder durationS, ffprobe): files of one length in different formats are flagged as maybe one creative. 0 to 36,000. - `answers` (one of, optional): The answers so far, with prepare_launch's argument names - Shape `option 1`: - `campaign_id` (string or integer or null, optional): Question "campaign". matches `^\d{1,40}$`. - `existing_adset_ids` (array of strings or integers or null, optional). matches `^\d{1,40}$`. - `copy_adset_from` (string or integer or null, optional): An ad set to duplicate. matches `^\d{1,40}$`. - `copy_ad_from` (string or integer or null, optional): An ad to duplicate. matches `^\d{1,40}$`. - `adset_settings` (object or null, optional) - `pixel_id` (string or integer or null, optional): A pixel. matches `^\d{1,40}$`. - `custom_conversion_id` (string or integer or null, optional): A custom conversion. matches `^\d{1,40}$`. - `application_id` (string or integer or null, optional): app_installs. matches `^\d{1,40}$`. - `ad_settings` (object or null, optional) - `page_id` (string or integer or null, optional): A Facebook page. matches `^\d{1,40}$`. - `instagram_id` (string or integer or null, optional): An Instagram account. matches `^\d{1,40}$`. - `lead_form_id` (string or integer or null, optional): An instant form. matches `^\d{1,40}$`. - `partnership` (object or null, optional) - `creator_ig_id` (string or integer or null, optional): An approved creator. matches `^\d{1,40}$`. - `creator_fb_page_id` (string or integer or null, optional): The creator's Facebook page (alone: a Facebook creator). matches `^\d{1,40}$`. - `products` (object or null, optional) - `catalog_id` (string or integer or null, optional): A catalog. matches `^\d{1,40}$`. - `product_set_id` (string or integer or null, optional): A product set. matches `^\d{1,40}$`. - `ads` (array of objects or null, optional) - `source_ad_id` (string or integer or null, optional): An existing ad to reuse with its post. matches `^\d{1,40}$`. - `adset_variants` (array of objects or null, optional) - `source_campaign_id` (string or integer or null, optional): Lists the existing ads of this campaign only. matches `^\d{1,40}$`. - `nomenclature` (object or null, optional) - `config_id` (string or null, optional). up to 64 characters. - `grouping_mode` (string, optional): The app's switch: "names" reads the file names with a saved convention, "by_hand" groups the files by hand (formats of one creative linked by name, every other file its own ad). Default: names only when a convention reads every file, else by hand. one of `by_hand`, `names`. - Shape `option 2`: - `campaign_id` (string or null, optional): Question "campaign". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `copy_adset_from` (string or null, optional): An ad group / ad squad to copy, or "latest". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `copy_ad_from` (string or null, optional): An ad to copy, or "latest". matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `existing_adset_ids` (array of strings or null, optional). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. Response 200 - `questions` (array of objects) - `id` (string) - `topic` (string) - `status` (string). one of `to_ask`, `answered`, `not_needed`, `later`, `last`. - `ask` (string) - `choices` (any) - `recommended` (any) - `answer_with` (string) - `read_errors` (array of strings) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/ad-accounts/act_1234567890/launch-questions' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "meta", "files": [ { "name": "Nordlicht | Spring launch" } ] }' ``` #### Create launch plan: POST /launch-plans https://www.apogee.ad/developers/api/create-launch-plan/ Prepare a launch from the interview's answers, WITHOUT creating anything. Answers 201 with the full recap (show it to the user) and the plan id, valid 24 hours; or 422 missing_answers with the open questions (nothing saved). Only the app's documented defaults are applied, and listed. Files: drive:, or your own files in Apogee storage (each checked present). Advantage+ enhancements: only those in advantage_plus.opt_in are ON. platform tiktok / snapchat: the same answers where they mean the same, their own settings under tiktok / snapchat, groups made by hand under adgroups / adsquads; their files must be in Apogee storage (a Drive file: POST /imports first). - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - Idempotency-Key: required - MCP: `prepare_launch` Body (application/json): - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `ads` (array of objects, required): The confirmed ads (get_launch_questions proposed_ads, corrected by the user), or the existing ads to reuse (each either files or source_ad_id, all the same kind; 50 existing ads max). 1 to 200 items. - `source_ad_id` (string, optional): An ad of this account to reuse with its post (likes and comments stay). Instead of files (get_launch_questions → creatives.choices.existing_ads). matches `^\d{1,40}$`. - `files` (array of objects, optional): New files: the formats of ONE creative, linked by hand or as proposed (one file per format: 9:16, 4:5, 1:1, 16:9; never a video with an image). Leave out with source_ad_id. 1 to 10 items. - `ref` (string, required): drive: (list_drive_folder), or the Apogee storage URL returned by get_upload_urls / confirm_uploads / import_status (your own files only). 1 to 2,048 characters. - `name` (string, optional). up to 255 characters. - `mimeType` (string, optional). up to 100 characters. - `format` (string, optional): 9:16, 4:5, 1:1, 16:9 or auto. up to 10 characters. - `concept` (string, optional): Concept = ad set name in 1 Concept 1 Adset. Files without it: the ad's own (its name), or "Group 1" once other ads have one (the app's Group by hand). 1 to 255 characters. - `ad_name` (string, optional): Ad name = ad set name in 1 Adset 1 Ad. Required with files; existing ads: default = the source ad's name + " - Copy with post ID". 1 to 255 characters. - `copy` (object, optional): This ad's own texts (question "copy"). Not with source_ad_id: an existing ad keeps its post - `primary_texts` (array of strings, optional). up to 5 items; each: up to 5,000 characters. - `headlines` (array of strings, optional). up to 5 items; each: up to 255 characters. - `description` (string, optional). up to 255 characters. - `website_url` (string, optional). up to 2,048 characters. - `call_to_action` (string, optional). up to 40 characters. - `spark_post` (object, optional): TikTok Spark Ads: an existing TikTok post run as this ad instead of files (its caption is the ad text, its likes and comments stay), as list_tiktok_posts gives it - `tiktok_item_id` (string, required): The TikTok post (list_tiktok_posts: its spark_post). matches `^\d{1,40}$`. - `identity` (object, required) - `id` (string, required): The account that posted it. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `type` (string, required): TT_USER / BC_AUTH_TT: one of the brand's TikTok accounts; AUTH_CODE: a creator who authorized the post for this ad account. one of `TT_USER`, `BC_AUTH_TT`, `AUTH_CODE`. - `bc_id` (string or null, optional): BC_AUTH_TT: its Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `platform` (string, optional): Default meta. tiktok / snapchat: the same interview and arguments where they mean the same, their own settings under `tiktok` / `snapchat`, groups made by hand under `adgroups` / `adsquads`. one of `meta`, `tiktok`, `snapchat`. - `campaign_id` (string, optional): Question "campaign": an existing campaign. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `structure` (string, optional): Question "test_structure": 1 Adset 1 Ad, 1 Concept 1 Adset, All in One, Insert in Adset. one of `one_adset_one_ad`, `one_concept_one_adset`, `all_in_one`, `insert_in_adset`. - `all_in_one_name` (string, optional): All in One: the ad set name (required, unless adset_variants). up to 255 characters. - `existing_adset_ids` (array of strings, optional): Insert: the existing ad sets (Meta), ad groups (TikTok) or ad squads (Snapchat). up to 50 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_variants` (array of objects, optional): all_in_one: the same ads in 1-5 new ad sets, each with its own bid / budget (e.g. two cost caps), or each with its own ads (ads). 1 to 5 items. - `name` (string, required): The new ad set's name. 1 to 255 characters. - `bid_amount` (number, optional): Cost cap / bid cap, in the account currency. > 0. - `roas_floor` (number, optional): Minimum ROAS strategy, e.g. 2.5. > 0. - `daily_budget` (number, optional): ABO only, account currency. > 0. - `ads` (array of strings, optional): Existing ads (source_ad_id) only: the ads that go into this new ad set, each by its ad_name or its source_ad_id. Left out on every ad set: every ad in each. Given on one: every ad set needs its own, and every ad must be in one at least. up to 50 items; each: 1 to 255 characters. - `adsets` (array of objects, optional): New files: the ads arranged into new ad sets by hand, on top of structure one_adset_one_ad, one_concept_one_adset or all_in_one. Launched as 1 Concept 1 Adset (each ad set = a concept), the ad names unchanged; every ad set takes the plan's ad set settings. Not with insert_in_adset, adset_variants or existing ads. 1 to 200 items. - `name` (string, required): The new ad set's name. 1 to 255 characters. - `ads` (array of strings, required): The ads that go into it, by their ad_name (each ad in exactly one ad set). up to 200 items; each: 1 to 255 characters. - `nomenclature` (object, optional): Question "nomenclature", when a saved convention reads the names: named in the recap. Files grouped by hand: leave it out - `confirmed` (constant, required): true = the user validated the parsed concepts / iterations / formats. always `true`. - `config_id` (string or null, optional): The saved naming convention used, null when none. up to 64 characters. - `name` (string, optional). up to 255 characters. - `grouping_confirmed` (constant, optional): Question "grouping": true = the user validated the ads (their names and files), read by a convention or grouped by hand. always `true`. - `copy_adset_from` (string, optional): Question "starting_point": the ad set / ad group / ad squad whose settings are copied ("latest" = TikTok: the latest ad group of campaign_id). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_template_id` (string, optional): Question "starting_point": a saved Apogee ad set template. up to 64 characters. - `adset_settings` (object, optional): A new ad set (questions "audience", "tracking"), or the changes to a copied ad set / template - `goal` (string, optional): New ad set only. Default: from the campaign objective. one of `sales`, `leads_website`, `leads_instant_form`, `traffic`, `engagement`, `awareness`, `app_installs`. - `countries` (array of strings, optional): ISO codes, e.g. ["FR"] (a new ad set needs countries, regions or cities). up to 250 items; matches `^[A-Za-z]{2}$`. - `regions` (array of objects, optional): Region keys from search_targeting(type: locations). up to 200 items. - `key` (string, required). 1 to 32 characters. - `cities` (array of objects, optional): City keys from search_targeting(type: locations). up to 250 items. - `key` (string, required). 1 to 32 characters. - `radius` (number, optional). 1 to 80. - `distance_unit` (string, optional). one of `kilometer`, `mile`. - `age_min` (integer, optional). 13 to 65. - `age_max` (integer, optional). 13 to 65. - `genders` (string, optional): Default all. one of `all`, `men`, `women`. - `locales` (array of integers, optional): Language keys from search_targeting(type: locales). up to 50 items; each: ≥ -9,007,199,254,740,991. - `advantage_audience` (boolean, optional): Default true (broad) - `custom_audience_ids` (array of strings, optional): list_custom_audiences. up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional): list_custom_audiences. up to 100 items; matches `^\d{1,40}$`. - `interests` (array of objects, optional): search_targeting(type: interests). up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `behaviors` (array of objects, optional): search_targeting(type: behaviors). up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `life_events` (array of objects, optional): search_targeting(type: demographics): the app's demographics / life events. up to 100 items. - `id` (string, required). 1 to 64 characters. - `name` (string, optional). up to 255 characters. - `placements` (one of, optional): "advantage" (Advantage+ placements, the default) or the platforms to keep - Shape `option 1`: (no fields) - Shape `option 2`: - `publisher_platforms` (array of strings, required). one of `facebook`, `instagram`, `messenger`, `audience_network`; at least 1 item. - `pixel_id` (string, optional): A pixel (list_pixels). matches `^\d{1,40}$`. - `event` (string, optional): Meta enum: PURCHASE, LEAD, CONTENT_VIEW, INITIATED_CHECKOUT, ADD_TO_CART, COMPLETE_REGISTRATION…. up to 64 characters. - `custom_event_str` (string, optional): A pixel custom event name (sent as OTHER + this name). up to 100 characters. - `custom_conversion_id` (string, optional): A custom conversion (sent alone). matches `^\d{1,40}$`. - `optimization_goal` (string, optional): New ad set: overrides the default optimization (e.g. LINK_CLICKS for traffic). up to 64 characters. - `bid_strategy` (string, optional): ABO: the new ad sets' strategy (default Lowest cost, the highest volume). CBO: must be the campaign's (or leave it out). one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. - `bid_amount` (number, optional): COST_CAP / LOWEST_COST_WITH_BID_CAP: the cost per result goal (cost cap) or bid cap of every new ad set, account currency (adset_variants: one each). > 0. - `roas_floor` (number, optional): LOWEST_COST_WITH_MIN_ROAS: the minimum ROAS of every new ad set, e.g. 2.5 (adset_variants: one each). > 0. - `attribution` (object, optional): Default 7-day click, 1-day view, 1-day engaged view - `click` (string, required). one of `1_day`, `7_days`. - `view` (string, optional). one of `none`, `1_day`, `7_days`. - `engaged_view` (string, optional). one of `none`, `1_day`, `7_days`. - `application_id` (string, optional): app_installs. matches `^\d{1,40}$`. - `object_store_url` (string, optional): app_installs: App Store / Play Store link. up to 2,048 characters. - `copy_ad_from` (string, optional): Question "starting_point": the ad whose settings are copied ("latest" = TikTok: the latest ad). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `ad_template_id` (string, optional): Question "starting_point": a saved Apogee ad template. up to 64 characters. - `ad_settings` (object, optional): A new ad (questions "identity", "destination"), or the changes to a copied ad / template - `page_id` (string, optional): A Facebook page (list_pages). matches `^\d{1,40}$`. - `instagram_id` (string or null, optional): Default: the page's linked Instagram. matches `^\d{1,40}$`. - `partnership` (object or null, optional): A partnership ad with a creator who approved the brand's Instagram account, or whose Facebook Page accepted the brand's Page; null = none (default) - `creator_ig_id` (string, optional): An Instagram creator (get_launch_questions → identity.choices.partnership_creators, source instagram). matches `^\d{1,40}$`. - `creator_username` (string, optional). up to 100 characters. - `creator_fb_page_id` (string or null, optional): An Instagram creator's Page (default: read from the approved creators; null: none), or alone a Facebook creator (source facebook). matches `^\d{1,40}$`. - `creator_name` (string, optional): A Facebook creator's Page name (as listed). up to 200 characters. - `ad_code` (string, optional): Not used: a partnership ad code boosts the creator's own post, Apogee launches your files. Use creator_ig_id. up to 200 characters. - `identity_mode` (string, optional): advertiser_primary = your brand first (default); creator_primary = the creator first (on Instagram only when the creator has no Facebook Page). one of `advertiser_primary`, `creator_primary`. - `header_display` (string, optional): dynamic (default, Meta picks; optimizes on Instagram only), both identities, or the first identity only. one of `dynamic`, `both`, `primary_only`. - `website_url` (string, optional): Required except for instant forms. up to 2,048 characters. - `url_parameters` (string, optional): UTM parameters, "" = none (required for a new ad). up to 1,000 characters. - `call_to_action` (string, optional): Required for a new ad, e.g. SHOP_NOW, LEARN_MORE, SIGN_UP. up to 40 characters. - `lead_form_id` (string, optional): Instant form leads (list_lead_forms). matches `^\d{1,40}$`. - `display_link` (string, optional). up to 255 characters. - `advantage_plus` (object, optional): Question "advantage_plus" (required) - `opt_in` (array of strings, required): The enhancements the user turned ON; every other one is sent OFF. [] = all off. one of `enhance_cta`, `inline_comment`, `reveal_details_over_time`, `show_destination_blurbs`, `show_summary`, `text_optimizations`, `product_tags`, `optimize_website_destination`, `shop`, `product_browsing`, `browser_addons`, `image_brightness_and_contrast`, `image_templates`, `image_touchups`, `video_auto_crop`, `video_filtering`. - `products` (object, optional): Question "products" (required when the account has a catalog) - `show` (boolean, required): "Show relevant products" from the catalog - `catalog_id` (string, optional): A catalog (get_launch_questions → products). matches `^\d{1,40}$`. - `product_set_id` (string, optional): A product set of that catalog. matches `^\d{1,40}$`. - `daily_budget` (number, optional): ABO: daily budget of each new ad set, account currency (required). > 0. - `launch_status` (string, optional): Created Paused (default), Live Now, Scheduled & Paused, Scheduled & Live. one of `CREATED_PAUSED`, `LIVE_NOW`, `SCHEDULED_PAUSED`, `SCHEDULED_LIVE`. - `schedule` (object, optional): Scheduled & Paused / Scheduled & Live - `date` (string, required): YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `time` (string, required): HH:MM, account time zone. matches `^\d{2}:\d{2}$`. - `ad_overrides` (object, optional): Advanced: raw ad template fields (description, display_link, url_parameters, website_url, call_to_action, instagram_account_id, lead_gen_form_id, primary_texts, headlines) - `description` (any, optional) - `display_link` (any, optional) - `url_parameters` (any, optional) - `website_url` (any, optional) - `call_to_action` (any, optional) - `instagram_account_id` (any, optional) - `lead_gen_form_id` (any, optional) - `primary_texts` (any, optional) - `headlines` (any, optional) - `adset_overrides` (object, optional): Advanced: raw ad set template fields (name, billing_event, optimization_goal, destination_type, daily_min_spend_target, daily_spend_cap, start_time, end_time, bid_strategy, bid_amount, bid_constraints, attribution_spec, attribution_setting, targeting), Meta's units - `name` (any, optional) - `billing_event` (any, optional) - `optimization_goal` (any, optional) - `destination_type` (any, optional) - `daily_min_spend_target` (any, optional) - `daily_spend_cap` (any, optional) - `start_time` (any, optional) - `end_time` (any, optional) - `bid_strategy` (any, optional) - `bid_amount` (any, optional) - `bid_constraints` (any, optional) - `attribution_spec` (any, optional) - `attribution_setting` (any, optional) - `targeting` (any, optional) - `adgroups` (array of objects, optional): TikTok: ad groups made by hand, instead of structure. 1 to 100 items. - `ads` (array of strings, required): The ad_name of each ad in it (every ad in exactly one group). 1 to 200 items; each: 1 to 255 characters. - `name` (string, optional): A new ad group's name. 1 to 255 characters. - `existing_adgroup_id` (string, optional): An existing ad group to fill instead (all groups existing, or all new). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `settings` (object, optional): This ad group's own settings, over the shared ones - `pixel_id` (string, optional): A pixel (get_launch_questions → tracking.choices.pixels). matches `^\d{1,40}$`. - `optimization_event` (string, optional): TikTok's optimization_event: SHOPPING (purchase), INITIATE_ORDER, ON_WEB_CART, ON_WEB_DETAIL, ON_WEB_REGISTER, FORM…. up to 64 characters. - `bid_type` (string, optional): Default BID_TYPE_NO_BID (maximum delivery); BID_TYPE_CUSTOM = cost cap (bid). one of `BID_TYPE_NO_BID`, `BID_TYPE_CUSTOM`. - `bid` (number, optional): Cost cap per conversion (BID_TYPE_CUSTOM), account currency. > 0. - `pacing` (string, optional). one of `PACING_MODE_SMOOTH`, `PACING_MODE_FAST`. - `countries` (array of strings, optional): ISO codes, read into TikTok location ids (or location_ids). up to 250 items; matches `^[A-Za-z]{2}$`. - `location_ids` (array of strings, optional). up to 500 items; matches `^\d{1,40}$`. - `age_groups` (array of strings, optional): [] or absent = every age. one of `AGE_13_17`, `AGE_18_24`, `AGE_25_34`, `AGE_35_44`, `AGE_45_54`, `AGE_55_100`; up to 6 items. - `gender` (string, optional). one of `GENDER_UNLIMITED`, `GENDER_MALE`, `GENDER_FEMALE`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `action_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `placements` (array of strings, optional): Default TikTok only. one of `PLACEMENT_TIKTOK`, `PLACEMENT_PANGLE`, `PLACEMENT_GLOBAL_APP_BUNDLE`; 1 to 3 items. - `daily_budget` (number, optional): ABO: this one's budget (default daily_budget). > 0. - `adsquads` (array of objects, optional): Snapchat: ad squads made by hand, instead of structure. 1 to 100 items. - `ads` (array of strings, required): The ad_name of each ad in it (every ad in exactly one group). 1 to 200 items; each: 1 to 255 characters. - `name` (string, optional): A new ad squad's name. 1 to 255 characters. - `existing_adsquad_id` (string, optional): An existing ad squad to fill instead (all groups existing, or all new). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `settings` (object, optional): This ad squad's own settings, over the shared ones - `optimization_goal` (string, optional): Default PIXEL_PURCHASE. The PIXEL_ goals need pixel_id (and enough of that event on the pixel lately: get_launch_questions tracking.choices.optimization_goals); SWIPES and LANDING_PAGE_VIEW need none. one of `PIXEL_PURCHASE`, `PIXEL_ADD_TO_CART`, `PIXEL_SIGNUP`, `PIXEL_PAGE_VIEW`, `SWIPES`, `LANDING_PAGE_VIEW`. - `pixel_id` (string, optional): A Snap pixel (get_launch_questions → tracking.choices.pixels). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `bid_strategy` (string, optional): Default AUTO_BID. one of `AUTO_BID`, `LOWEST_COST_WITH_MAX_BID`, `TARGET_COST`. - `bid` (number, optional): LOWEST_COST_WITH_MAX_BID / TARGET_COST: the bid, account currency. > 0. - `pacing_type` (string, optional): Default STANDARD. one of `STANDARD`, `ACCELERATED`. - `conversion_window` (string, optional): Default Snapchat's SWIPE_28DAY_VIEW_1DAY. one of `SWIPE_28DAY_VIEW_1DAY`, `SWIPE_7DAY`. - `countries` (array of strings, optional): ISO codes (at least one for a new ad squad). 1 to 250 items; matches `^[A-Za-z]{2}$`. - `region_ids` (array of strings, optional). up to 200 items; each: up to 32 characters. - `min_age` (integer, optional): Default 18 for a new ad squad: Snapchat refuses its goal for under-18s in the EU, the EEA, Switzerland and the UK. 13 to 65. - `max_age` (integer, optional). 13 to 65. - `genders` (string, optional). one of `all`, `men`, `women`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_ids` (array of strings, optional). up to 200 items; each: up to 64 characters. - `segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `excluded_segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `daily_budget` (number, optional): ABO: this one's budget (default daily_budget). > 0. - `tiktok` (object, optional): TikTok's own settings (platform tiktok) - `new_campaign` (object, optional): A new Sales campaign (website conversions; Standard, or Upgraded Smart+), created by the launch (instead of campaign_id) - `name` (string, required). 1 to 255 characters. - `budget_type` (string, required): abo = a budget per ad group (daily_budget); cbo = the campaign's budget. one of `abo`, `cbo`. - `budget` (number, optional): CBO: the campaign budget, account currency. > 0. - `budget_mode` (string, optional): Default daily. one of `daily`, `lifetime`. - `smart_plus` (boolean, optional): true = an Upgraded Smart+ campaign (website Sales), as the app's Create Campaign: up to 30 ad groups of 30 ads, one ad per creative (its texts rotate inside), TikTok's dynamic CTA (ad_settings.cta_language / cta_texts), no Spark post. Default: a Standard campaign - `adgroup_settings` (object, optional): A new ad group's settings, or the changes to a copied one (copy_adset_from) - `pixel_id` (string, optional): A pixel (get_launch_questions → tracking.choices.pixels). matches `^\d{1,40}$`. - `optimization_event` (string, optional): TikTok's optimization_event: SHOPPING (purchase), INITIATE_ORDER, ON_WEB_CART, ON_WEB_DETAIL, ON_WEB_REGISTER, FORM…. up to 64 characters. - `bid_type` (string, optional): Default BID_TYPE_NO_BID (maximum delivery); BID_TYPE_CUSTOM = cost cap (bid). one of `BID_TYPE_NO_BID`, `BID_TYPE_CUSTOM`. - `bid` (number, optional): Cost cap per conversion (BID_TYPE_CUSTOM), account currency. > 0. - `pacing` (string, optional). one of `PACING_MODE_SMOOTH`, `PACING_MODE_FAST`. - `countries` (array of strings, optional): ISO codes, read into TikTok location ids (or location_ids). up to 250 items; matches `^[A-Za-z]{2}$`. - `location_ids` (array of strings, optional). up to 500 items; matches `^\d{1,40}$`. - `age_groups` (array of strings, optional): [] or absent = every age. one of `AGE_13_17`, `AGE_18_24`, `AGE_25_34`, `AGE_35_44`, `AGE_45_54`, `AGE_55_100`; up to 6 items. - `gender` (string, optional). one of `GENDER_UNLIMITED`, `GENDER_MALE`, `GENDER_FEMALE`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `action_category_ids` (array of strings, optional). up to 200 items; matches `^\d{1,40}$`. - `audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `excluded_audience_ids` (array of strings, optional). up to 100 items; matches `^\d{1,40}$`. - `placements` (array of strings, optional): Default TikTok only. one of `PLACEMENT_TIKTOK`, `PLACEMENT_PANGLE`, `PLACEMENT_GLOBAL_APP_BUNDLE`; 1 to 3 items. - `ad_settings` (object, optional): The ads' TikTok account (and whether their videos show on its profile), destination and CTA (new, or the changes to a copied ad); Smart+ and carousel settings - `identity` (object, optional) - `id` (string, required): The identity id (get_launch_questions → identity.choices.identities). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `type` (string, required): One of the brand's TikTok accounts: TT_USER, or BC_AUTH_TT through its Business Center (TikTok refuses a custom identity on these ads). one of `TT_USER`, `BC_AUTH_TT`. - `bc_id` (string or null, optional): BC_AUTH_TT: the Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `show_on_profile` (boolean, optional): The app's "Also show the video on the TikTok profile": true = the video is also posted on the TikTok account's profile; default: it only runs as an ad (TikTok's default). Not where TikTok keeps the videos ads-only (get_launch_questions identity.show_on_profile) - `website_url` (string, optional): Apogee's TikTok tracking replaces its own utm_ parameters (no UTM option on TikTok: the app's final URL). up to 2,048 characters. - `call_to_action` (string, optional): Default SHOP_NOW (a Standard ad's; a Smart+ ad runs TikTok's dynamic CTA: cta_language, cta_texts). one of `SHOP_NOW`, `BUY_NOW`, `ORDER_NOW`, `LEARN_MORE`, `SIGN_UP`, `DOWNLOAD_NOW`, `CONTACT_US`, `BOOK_NOW`. - `cta_language` (string, optional): Smart+: the language of TikTok's dynamic call to action when the ad group lends none (default en). one of `en`, `fr`, `de`, `es`, `it`, `nl`, `pt`, `pl`, `sv`. - `cta_texts` (array of strings, optional): Smart+: the calls to action kept among the ones TikTok offers in cta_language (default: TikTok's whole list). up to 20 items; each: 1 to 40 characters. - `automations` (object, optional): Smart+: TikTok's automations of the ads (as Meta's Advantage+ creative enhancements), each off unless turned on - `auto_add` (boolean, optional): TikTok adds assets of its own - `enhancements` (array of strings, optional). one of `TRANSLATE_AND_DUB`, `MUSIC_REFRESH`, `VIDEO_QUALITY`, `IMAGE_QUALITY`, `IMAGE_RESIZE`; up to 5 items. - `music_id` (string, optional): Carousels (image ads): the music, one of list_tiktok_music; default TikTok's pick at the launch. matches `^\d{1,40}$`. - `carousel_mode` (string, optional): Images run as carousels (TikTok's image ads), as the app's Configure switch: single (default) = each image ad a carousel of its image; by_concept = a concept's images one carousel ad named after the concept (35 images at most, its first ad's settings and texts). one of `single`, `by_concept`. - `adgroup_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad group template (list_platform_templates level adgroup), the base of the new ad groups' settings (instead of copy_adset_from); adgroup_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `ad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad template (list_platform_templates level ad), the base of the ads' settings (instead of copy_ad_from); ad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `time_zone` (string, optional): The IANA zone of schedule (only when the advertiser's time zone is unknown). up to 64 characters. - `end_date` (string, optional): A lifetime campaign budget (CBO, budget_mode lifetime, a new campaign or an existing one): the date it runs to, YYYY-MM-DD in the schedule's time zone, after the launch (a scheduled launch: TikTok spreads the budget until then). Ignored for a daily budget. matches `^\d{4}-\d{2}-\d{2}$`. - `end_time` (string, optional): HH:MM of end_date, default the start's time. matches `^\d{2}:\d{2}$`. - `snapchat` (object, optional): Snapchat's own settings (platform snapchat) - `new_campaign` (object, optional): A new Sales campaign, created by the launch (instead of campaign_id) - `name` (string, required). 1 to 255 characters. - `budget_type` (string, required): abo = a budget per ad squad (daily_budget, 5 minimum); cbo = the campaign's (20 minimum per day), Snapchat's Smart Budget: its ad squads share their optimisation goal, pixel, bid strategy and conversion window (get_launch_questions tracking.choices); a lower budget is raised to it. one of `abo`, `cbo`. - `budget` (number, optional): CBO: the campaign budget per day, account currency. > 0. - `adsquad_settings` (object, optional): A new ad squad's settings, or the changes to a copied one (copy_adset_from) - `optimization_goal` (string, optional): Default PIXEL_PURCHASE. The PIXEL_ goals need pixel_id (and enough of that event on the pixel lately: get_launch_questions tracking.choices.optimization_goals); SWIPES and LANDING_PAGE_VIEW need none. one of `PIXEL_PURCHASE`, `PIXEL_ADD_TO_CART`, `PIXEL_SIGNUP`, `PIXEL_PAGE_VIEW`, `SWIPES`, `LANDING_PAGE_VIEW`. - `pixel_id` (string, optional): A Snap pixel (get_launch_questions → tracking.choices.pixels). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `bid_strategy` (string, optional): Default AUTO_BID. one of `AUTO_BID`, `LOWEST_COST_WITH_MAX_BID`, `TARGET_COST`. - `bid` (number, optional): LOWEST_COST_WITH_MAX_BID / TARGET_COST: the bid, account currency. > 0. - `pacing_type` (string, optional): Default STANDARD. one of `STANDARD`, `ACCELERATED`. - `conversion_window` (string, optional): Default Snapchat's SWIPE_28DAY_VIEW_1DAY. one of `SWIPE_28DAY_VIEW_1DAY`, `SWIPE_7DAY`. - `countries` (array of strings, optional): ISO codes (at least one for a new ad squad). 1 to 250 items; matches `^[A-Za-z]{2}$`. - `region_ids` (array of strings, optional). up to 200 items; each: up to 32 characters. - `min_age` (integer, optional): Default 18 for a new ad squad: Snapchat refuses its goal for under-18s in the EU, the EEA, Switzerland and the UK. 13 to 65. - `max_age` (integer, optional). 13 to 65. - `genders` (string, optional). one of `all`, `men`, `women`. - `languages` (array of strings, optional). up to 50 items; each: up to 16 characters. - `interest_ids` (array of strings, optional). up to 200 items; each: up to 64 characters. - `segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `excluded_segment_ids` (array of strings, optional). up to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `ad_settings` (object, optional): The ads' profile, destination and CTA (new, or the changes to a copied ad) - `profile_id` (string, optional): A public profile (get_launch_questions → identity.choices.profiles). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `website_url` (string, optional): https only. up to 2,048 characters. - `utm` (object, optional): UTM parameters appended to the URL (the app's UTM toggle): given = on, the app's defaults for the fields left out - `source` (string, optional). up to 200 characters. - `medium` (string, optional). up to 200 characters. - `campaign` (string, optional). up to 200 characters. - `id` (string, optional). up to 200 characters. - `content` (string, optional). up to 200 characters. - `call_to_action` (string, optional): Default SHOP_NOW. one of `SHOP_NOW`, `ORDER_NOW`, `SIGN_UP`, `GET_NOW`, `BOOK_NOW`, `VIEW`, `MORE`, `READ`, `WATCH`, `DOWNLOAD`, `APPLY_NOW`, `BUY_TICKETS`, `PLAY_GAME`, `LISTEN`. - `brand_name` (string, optional). up to 32 characters. - `adsquad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad squad template (list_platform_templates level adgroup), the base of the new ad squads' settings (instead of copy_adset_from); adsquad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `ad_template_id` (string (uuid), optional): Quick Bulk Edit: a saved ad template (list_platform_templates level ad), the base of the ads' settings (instead of copy_ad_from); ad_settings over it. matches `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`. - `time_zone` (string, optional): The IANA zone of schedule (only when the ad account's time zone is unknown). up to 64 characters. Response 201 - `id` (string) - `status` (string). one of `prepared`, `launched`, `expired`. - `recap` (object) - `defaults_applied` (array of strings) - `warnings` (array of strings) - `created_at` (string or null) - `expires_at` (string (date-time)) - `launched_at` (string or null) - `launch_id` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/launch-plans' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "ad_account_id": "act_1234567890", "campaign_id": "120200000000000001", "structure": "one_concept_one_adset", "copy_adset_from": "120200000000000002", "copy_ad_from": "120200000000000003", "ads": [ { "ad_name": "NORDLICHT_RAIN_HOOK1", "concept": "Rain test", "files": [ { "ref": "drive:EXAMPLE_FILE_916", "name": "NORDLICHT_RAIN_HOOK1_9x16.mp4", "format": "9:16" }, { "ref": "drive:EXAMPLE_FILE_45", "name": "NORDLICHT_RAIN_HOOK1_4x5.mp4", "format": "4:5" } ], "copy": { "primary_texts": [ "Rain-proof, wind-proof, and light enough to forget you are wearing it." ], "headlines": [ "The Nordlicht shell jacket" ], "description": "Free returns within 30 days" } } ], "grouping_confirmed": true, "advantage_plus": { "opt_in": [] }, "daily_budget": 50, "launch_status": "CREATED_PAUSED" }' ``` #### Get launch plan: GET /launch-plans/{id} https://www.apogee.ad/developers/api/get-launch-plan/ A prepared plan: its recap and whether it was launched. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) Parameters: - `id` (string, required, in path): Launch plan id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `id` (string) - `status` (string). one of `prepared`, `launched`, `expired`. - `recap` (object) - `defaults_applied` (array of strings) - `warnings` (array of strings) - `created_at` (string or null) - `expires_at` (string (date-time)) - `launched_at` (string or null) - `launch_id` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/launch-plans/120200000000000001' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Confirm launch plan: POST /launch-plans/{id}/confirm https://www.apogee.ad/developers/api/confirm-launch-plan/ Launch a prepared plan, after the user said yes to its recap. A plan launches once (409 plan_already_launched), within 24 hours of being prepared (410 plan_expired). Answers 202 as soon as the launch started: follow it with GET /launches/{launch_id}. - Scope: `launch` - Plans: Pro and Scale - Credits: 15 credits per ad created, counted at the end (an ad that failed is free) - Rate limit: confirmation (30 per 15 minutes on Pro, 60 on Scale) - Idempotency-Key: required - MCP: `launch` Parameters: - `id` (string, required, in path): Launch plan id. 1 to 64 characters; matches `^[\w-]+$`. Body (application/json): - `confirm` (constant, required): true = the user said yes to the recap. always `true`. Response 202 - `plan_id` (string) - `status` (string). one of `started`. - `launch_id` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 410 (action_expired, plan_expired); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/launch-plans/120200000000000001/confirm' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "confirm": true }' ``` #### Get launch plan status: GET /launch-plans/{id}/status https://www.apogee.ad/developers/api/get-launch-plan-status/ Where a launch is, by its plan id (useful in the first seconds, before its launch id is known). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `launch_status` Parameters: - `id` (string, required, in path): Launch plan id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `finished` (boolean) - `status` (string). one of `not launched`, `starting`, `processing`, `completed`, `failed`, `interrupted`. - `id` (string or null) - `plan_id` (string or null) - `files` (object) - `total` (number or null) - `completed` (number or null) - `failed` (number or null) - `result` (object or null) - `success` (boolean) - `summary` (object) - `adsets` (array of objects) - `id` (string or null) - `name` (string or null) - `ads_ok` (array of strings) - `errors` (any) - `interrupted_at` (string or null) - `note` (string or null) - `error` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/launch-plans/120200000000000001/status' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List launches: GET /launches https://www.apogee.ad/developers/api/list-launches/ The launches (the app's History), newest first: processing, completed, failed or interrupted. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_launches` Parameters: - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `ad_account_id` (string, optional, in query): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `status` (string, optional, in query). one of `processing`, `completed`, `failed`, `interrupted`. Response 200 - `data` (array of objects) - `id` (string) - `status` (string). one of `processing`, `completed`, `failed`, `interrupted`. - `name` (string or null) - `ad_account_id` (string or null) - `files` (object) - `total` (number or null) - `completed` (number or null) - `failed` (number or null) - `created_at` (string or null) - `completed_at` (string or null) - `interrupted_at` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/launches' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get launch: GET /launches/{id} https://www.apogee.ad/developers/api/get-launch/ Where a launch is: processing, completed, failed or interrupted (a deploy cut it: check Ads Manager). - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) Parameters: - `id` (string, required, in path): Launch id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `finished` (boolean) - `status` (string). one of `not launched`, `starting`, `processing`, `completed`, `failed`, `interrupted`. - `id` (string or null) - `plan_id` (string or null) - `files` (object) - `total` (number or null) - `completed` (number or null) - `failed` (number or null) - `result` (object or null) - `success` (boolean) - `summary` (object) - `adsets` (array of objects) - `id` (string or null) - `name` (string or null) - `ads_ok` (array of strings) - `errors` (any) - `interrupted_at` (string or null) - `note` (string or null) - `error` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/launches/120200000000000001' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ### Actions Prepared then confirmed changes #### Create action: POST /actions https://www.apogee.ad/developers/api/create-action/ Prepare an action: what will change is answered, nothing changes until it is confirmed. Meta types: pause_ad, pause_adset, activate_ad, activate_adset, update_budget, scale_budget, duplicate_ad, duplicate_adset, duplicate_campaign, create_adset, create_ad, update_adset_targeting, update_campaign_status, create_campaign, create_ad_from_post. TikTok / Snapchat types (with platform and that platform's ad account id): set_platform_status, set_platform_daily_budget. Budgets above the safety cap are refused. duplicate_ad: Keeps the SAME Facebook post (its likes, comments and shares): a new ad on its post id, else on its creative (Meta's /copies made a new post). Instagram starts a new post for each ad, as with a post id in Ads Manager. Only inside the same ad account (a copy into another ad account makes a new post); changing a text or the URL afterwards makes a new post too. The result lists each new ad id and the method used (post_id: shown as an existing post in Ads Manager; shared_creative: the same post shown as a new ad). - Scope: `actions (launch for create_campaign)` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - Idempotency-Key: required - MCP: `create_campaign`, `create_ad_from_post`, `pause_ad`, `pause_adset`, `update_budget`, `duplicate_ad`, `activate_ad`, `activate_adset`, `duplicate_adset`, `duplicate_campaign`, `create_adset`, `create_ad`, `update_adset_targeting`, `update_campaign_status`, `scale_budget`, `set_platform_status`, `set_platform_daily_budget` Body (application/json): - Shape `type: "pause_ad"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `pause_ad`. - `input` (object, required): Pause one or more ads. Use when user asks to pause, cut, stop, or kill specific ads. Always confirm with the user before executing. - `ad_ids` (array of strings, required): Array of ad IDs to pause - `ad_names` (array of strings, required): Names of the ads for confirmation display - Shape `type: "pause_adset"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `pause_adset`. - `input` (object, required): Pause one or more ad sets. Use when user asks to pause, cut, stop, or kill specific ad sets. Preferred over pause_ad for 1-ad-per-adset structures. When user wants to pause/cut a creative, use this tool with the adset_id. - `adset_ids` (array of strings, required): Array of ad set IDs to pause - `adset_names` (array of strings, required): Names of the ad sets for confirmation display - Shape `type: "activate_ad"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `activate_ad`. - `input` (object, required): Reactivate one or more paused ads to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ads. - `ad_ids` (array of strings, required): Array of ad IDs to activate - `ad_names` (array of strings, optional): Names of the ads for confirmation display - Shape `type: "activate_adset"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `activate_adset`. - `input` (object, required): Reactivate one or more paused ad sets to ACTIVE status. Use when user asks to reactivate, resume, restart, turn on specific ad sets. - `adset_ids` (array of strings, required): Array of ad set IDs to activate - `adset_names` (array of strings, optional): Names of the ad sets for confirmation display - Shape `type: "update_budget"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `update_budget`. - `input` (object, required): Update daily budget for one or more ad sets. Use when user asks to scale, increase, decrease, or change budget. - `updates` (array of objects, required): Array of budget updates - `adset_id` (string, optional) - `adset_name` (string, optional) - `current_budget` (number, optional): Current daily budget in account currency - `new_budget` (number, optional): New daily budget in account currency - Shape `type: "scale_budget"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `scale_budget`. - `input` (object, required): Scale budgets of ad sets by a percentage (e.g. +30% or -20%). Fetches current budgets, calculates new ones, and shows confirmation with before/after. Max +100% per operation, min 1€/day. - `adset_ids` (array of strings, required): Array of ad set IDs to scale - `percentage` (number, required): Percentage to scale by (e.g. 30 for +30%, -20 for -20%). Max +100, cannot go below 1€/day. - `adset_names` (array of strings, optional): Names of the ad sets - Shape `type: "duplicate_ad"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `duplicate_ad`. - `input` (object, required): Duplicate a winning ad into one or more target ad sets of the same ad account, on the SAME Facebook post: its likes, comments and shares stay (a new ad on its post id, else on its creative). Instagram starts a new post for each ad, as with a post id in Ads Manager. Use when user asks to duplicate, replicate, scale a winning creative to other campaigns. Keeps the SAME Facebook post (its likes, comments and shares): a new ad on its post id, else on its creative (Meta's /copies made a new post). Instagram starts a new post for each ad, as with a post id in Ads Manager. Only inside the same ad account (a copy into another ad account makes a new post); changing a text or the URL afterwards makes a new post too. The result lists each new ad id and the method used (post_id: shown as an existing post in Ads Manager; shared_creative: the same post shown as a new ad). - `source_ad_id` (string, required): ID of the ad to duplicate - `source_ad_name` (string, required): Name of the source ad (for confirmation display) - `target_adset_ids` (array of strings, required): Ad set IDs to duplicate into - `target_adset_names` (array of strings, required): Names of target ad sets (for confirmation display) - `rename_suffix` (string, optional): Optional suffix to append to duplicated ad names (e.g. " - Copy") - Shape `type: "duplicate_adset"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `duplicate_adset`. - `input` (object, required): Duplicate an ad set WITH its ads into the same campaign or a different one. Each copied ad keeps its post (likes, comments, shares keep adding up). The duplicate is created as PAUSED. Use when user asks to duplicate or copy an ad set. - `source_adset_id` (string, required): ID of the ad set to duplicate - `target_campaign_id` (string, optional): Optional: campaign ID to duplicate into (defaults to same campaign) - `new_name` (string, optional): Optional: added after the source's name (" - "), e.g. a market or a version - `new_budget` (number, optional): Optional: new daily budget in account currency - Shape `type: "duplicate_campaign"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `duplicate_campaign`. - `input` (object, required): Duplicate a full campaign with all its ad sets and ads (archived and deleted ones left out). Each copied ad keeps its post. The duplicate is created as PAUSED. Use when user asks to duplicate or copy a campaign. - `source_campaign_id` (string, required): ID of the campaign to duplicate - `new_name` (string, optional): Optional: added after the source's name (" - "), e.g. a market or a version - `status_option` (string, optional): Status for the new campaign (default PAUSED). one of `PAUSED`, `ACTIVE`. - Shape `type: "create_adset"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `create_adset`. - `input` (object, required): Create a new ad set in an existing campaign. The ad set is created as PAUSED. Use when user asks to create a new ad set or audience. In a campaign budget (CBO) leave daily_budget out: the campaign's budget and bid strategy apply, each ad set gets its own bid_amount / roas_floor. In ABO, daily_budget is required. copy_from_adset_id copies the audience, optimization, conversion event, destination and attribution of an ad set of the same account (then targeting and optimization_goal can be left out). - `campaign_id` (string, required): Campaign ID to create the ad set in - `name` (string, required): Ad set name - `copy_from_adset_id` (string, optional): Optional: an ad set of the same account to copy targeting, optimization_goal, billing_event, promoted_object, destination_type and attribution_spec from (its budget, bid and schedule are not copied) - `daily_budget` (number, optional): ABO only: daily budget in cents (e.g. 2000 = 20€). Leave it out in a CBO campaign - `targeting` (object, optional): Targeting spec (geo_locations, age_min, age_max, genders, interests, custom_audiences, etc.). Required unless copy_from_adset_id - `optimization_goal` (string, optional): Optimization goal (e.g. OFFSITE_CONVERSIONS, LINK_CLICKS, IMPRESSIONS). Required unless copy_from_adset_id - `promoted_object` (object, optional): Conversion event, e.g. { pixel_id, custom_event_type: "PURCHASE" }. Required for OFFSITE_CONVERSIONS / VALUE / LEAD_GENERATION unless copied - `billing_event` (string, optional): Billing event (default IMPRESSIONS) - `bid_strategy` (string, optional): ABO: this ad set's bid strategy (default LOWEST_COST_WITHOUT_CAP). CBO: the campaign's (leave it out). one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. - `bid_amount` (number, optional): Cost cap / bid cap in cents (e.g. 2500 = 25€): required with COST_CAP and LOWEST_COST_WITH_BID_CAP - `roas_floor` (number, optional): Minimum ROAS strategy: the ROAS floor, e.g. 2.5 (optimization becomes VALUE) - `start_time` (string, optional): Optional: start time in ISO format - Shape `type: "create_ad"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `create_ad`. - `input` (object, required): Create a new ad in an existing ad set. Use an object_story_id (page post ID) to preserve social proof (likes, comments, shares), or a creative_id to reuse an existing creative. The ad is created as PAUSED. - `adset_id` (string, required): Ad set ID to create the ad in - `name` (string, required): Ad name - `object_story_id` (string, optional): Optional: existing post ID (format pageId_postId) to preserve social proof - `creative_id` (string, optional): Optional: existing creative ID to reuse (used if no object_story_id) - `status` (string, optional): Ad status (default PAUSED). one of `PAUSED`, `ACTIVE`. - Shape `type: "update_adset_targeting"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `update_adset_targeting`. - `input` (object, required): Update the targeting of an existing ad set. Use when user asks to change audience, targeting, demographics, interests, or locations. - `adset_id` (string, required): Ad set ID to update - `targeting` (object, required): New targeting spec (geo_locations, age_min, age_max, genders, flexible_spec, custom_audiences, etc.) - `adset_name` (string, optional): Ad set name for confirmation display - Shape `type: "update_campaign_status"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `update_campaign_status`. - `input` (object, required): Change the status of an entire campaign (ACTIVE or PAUSED). Use when user asks to pause or activate a whole campaign. - `campaign_id` (string, required): Campaign ID - `status` (string, required): New status. one of `ACTIVE`, `PAUSED`. - `campaign_name` (string, optional): Campaign name for confirmation display - Shape `type: "create_campaign"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `create_campaign`. - `input` (object, required): A new campaign (the app's Create Campaign), always created PAUSED. Scope launch. - `name` (string, required). 1 to 255 characters. - `objective` (string, required). one of `OUTCOME_SALES`, `OUTCOME_LEADS`, `OUTCOME_TRAFFIC`, `OUTCOME_ENGAGEMENT`, `OUTCOME_AWARENESS`, `OUTCOME_APP_PROMOTION`. - `budget_type` (string, required): abo = budget per ad set; cbo = campaign budget. one of `abo`, `cbo`. - `budget` (number, optional): CBO only: daily campaign budget, in the account currency. > 0. - `bid_strategy` (string, optional): CBO: the bid strategy shared by all ad sets; each new ad set then gets its own bid_amount / roas_floor (prepare_launch adset_variants). ABO: leave it out and use adset_settings.bid_strategy.. one of `LOWEST_COST_WITHOUT_CAP`, `COST_CAP`, `LOWEST_COST_WITH_BID_CAP`, `LOWEST_COST_WITH_MIN_ROAS`. - Shape `type: "create_ad_from_post"`: - `ad_account_id` (string, required): Meta ad account id (act_…). matches `^(act_)?\d{1,32}$`. - `type` (constant, required). always `create_ad_from_post`. - `input` (object, required): A new ad on an EXISTING post (Facebook or Instagram): its likes, comments and shares are kept. Advantage+ choices required, every other enhancement OFF. - `adset_id` (string, required): The ad set the new ad goes in (list_adsets). matches `^\d{1,40}$`. - `name` (string, required): The new ad's name. 1 to 255 characters. - `advantage_plus` (object, required): Required: the Advantage+ creative enhancements, all explicit - `opt_in` (array of strings, required): Enhancements turned ON; every other one is sent OFF. [] = all off. one of `enhance_cta`, `inline_comment`, `reveal_details_over_time`, `show_destination_blurbs`, `show_summary`, `text_optimizations`, `product_tags`, `optimize_website_destination`, `shop`, `product_browsing`, `browser_addons`, `image_brightness_and_contrast`, `image_templates`, `image_touchups`, `video_auto_crop`, `video_filtering`. - `object_story_id` (string, optional): A Facebook post: _ (list_posts). matches `^\d{1,40}_\d{1,40}$`. - `instagram_media_id` (string, optional): An Instagram post (list_posts source instagram or ads). matches `^\d{1,40}$`. - `instagram_user_id` (string, optional): Instagram post: the account that published it. matches `^\d{1,40}$`. - `page_id` (string, optional): Instagram post: the Facebook page linked to that account. matches `^\d{1,40}$`. - `call_to_action` (string, optional): Instagram post only: e.g. SHOP_NOW, LEARN_MORE. up to 40 characters. - `website_url` (string, optional): Instagram post only, with call_to_action. up to 2,048 characters. - `url_parameters` (string, optional): UTM parameters added to the post's link. up to 1,000 characters. - `status` (string, optional): Default PAUSED. one of `PAUSED`, `ACTIVE`. - Shape `type: "set_platform_status"`: - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `type` (constant, required). always `set_platform_status`. - `input` (object, required): TikTok / Snapchat: pause or activate campaigns, ad sets (TikTok ad groups, Snapchat ad squads) or ads of the ad account (platform and its own ad account id at the top). TikTok GMV Max and legacy Smart+ campaigns, and the ads TikTok generates inside Smart+ ad groups, are refused. - `level` (string, required): campaign, adset (a TikTok ad group / a Snapchat ad squad) or ad. one of `campaign`, `adset`, `ad`. - `ids` (array of strings, required): 1 to 50 campaigns, ad sets or ads of that level. 1 to 50 items; matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `status` (string, required): paused (stops delivering) or active (delivers again). one of `paused`, `active`. - Shape `type: "set_platform_daily_budget"`: - `ad_account_id` (string, required): The ad account, from list_ad_accounts with the same platform: a TikTok advertiser id (digits), a Snapchat ad account id (a UUID). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `platform` (string, required): tiktok or snapchat (Meta has its own reads and actions). one of `tiktok`, `snapchat`. - `type` (constant, required). always `set_platform_daily_budget`. - `input` (object, required): TikTok / Snapchat: a campaign's or an ad set's new daily budget, in the ad account's currency. Refused above the safety cap (converted to the account's currency), on a lifetime budget, on an ad set whose campaign holds the budget, and on TikTok Smart+ budgets. - `level` (string, required): campaign (its campaign budget) or adset (a TikTok ad group / a Snapchat ad squad). one of `campaign`, `adset`. - `id` (string, required): The campaign or ad set (get_platform_performance shows its daily_budget). matches `^[A-Za-z0-9][A-Za-z0-9-]{0,63}$`. - `daily_budget` (number, required): The new daily budget, in the ad account's currency (not cents or micro). > 0. Response 201 - `id` (string) - `status` (string). one of `prepared`, `done`, `expired`. - `type` (string or null) - `platform` (string). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string or null) - `will_do` (string or null) - `created_at` (string or null) - `expires_at` (string (date-time)) - `executed_at` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/actions' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "ad_account_id": "act_1234567890", "type": "pause_ad", "input": { "ad_ids": [ "120200000000000003" ], "ad_names": [ "Nordlicht | Spring launch" ] } }' ``` #### Get action: GET /actions/{id} https://www.apogee.ad/developers/api/get-action/ A prepared action: what it will do, and whether it ran. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) Parameters: - `id` (string, required, in path): Action id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `id` (string) - `status` (string). one of `prepared`, `done`, `expired`. - `type` (string or null) - `platform` (string). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string or null) - `will_do` (string or null) - `created_at` (string or null) - `expires_at` (string (date-time)) - `executed_at` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/actions/120200000000000001' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Confirm action: POST /actions/{id}/confirm https://www.apogee.ad/developers/api/confirm-action/ Run a prepared action, after the user approved what it will do. An action runs once (409 action_already_ran), within 24 hours (410 action_expired). The answer lists what was created (details: new ids; for a duplicated ad, the method that kept its post). - Scope: `the action's own: actions (launch for create_campaign)` - Plans: Pro and Scale - Credits: 2 credits per change - Rate limit: confirmation (30 per 15 minutes on Pro, 60 on Scale) - Idempotency-Key: required - MCP: `execute_action` Parameters: - `id` (string, required, in path): Action id. 1 to 64 characters; matches `^[\w-]+$`. Body (application/json): - `confirm` (constant, required): true = the user approved this action. always `true`. Response 200 - `id` (string) - `done` (boolean) - `message` (string) - `type` (string or null) - `details` (array of objects) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 410 (action_expired, plan_expired); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/actions/120200000000000001/confirm' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "confirm": true }' ``` ### Bridge Ads of one platform launched on another, Meta → Meta copies across ad accounts #### Create bridge transfer: POST /bridge-transfers https://www.apogee.ad/developers/api/create-bridge-transfer/ Prepare a Bridge transfer WITHOUT transferring anything: the recap and the transfer id. Cross-platform: source ads (or a Meta ad set / campaign = its ads, 50 ads max) into a destination campaign, in an existing ad set / TikTok ad group / Snapchat ad squad (destination.adset_id) or a new one copied from a template (destination.new_adset). Meta → Meta (both platforms meta, two ad accounts): ads, ad sets or campaigns. ready false = what to give (recap.missing → extra_fields; recap.fields → mapping), then prepare it again. Show the recap, confirm after the yes. - Scope: `launch` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: write (120 per 15 minutes on Pro, 240 on Scale) - Idempotency-Key: required - MCP: `prepare_bridge_transfer` Body (application/json): - `source` (object, required): Where the ads come from: one of ad_ids, adset_ids, campaign_ids - `platform` (string, required). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `ad_ids` (array of strings, optional): The ads to transfer (cross-platform: 50 max per run). 1 to 100 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_ids` (array of strings, optional): Meta: ad sets (cross-platform: their ads; Meta → Meta: the ad sets themselves, 20 max). 1 to 20 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `campaign_ids` (array of strings, optional): Meta: campaigns (cross-platform: their ads; Meta → Meta: the campaigns themselves, 5 max). 1 to 5 items; matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `destination` (object, required) - `platform` (string, required). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string, required): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. - `campaign_id` (string, optional): Required, except for a Meta → Meta copy of campaigns. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `adset_id` (string, optional): An existing destination ad set / TikTok ad group / Snapchat ad squad (list_adsets). matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `new_adset` (object, optional): Cross-platform: a new ad set copied from a template, instead of adset_id - `template_adset_id` (string, required): An ad set / ad group / ad squad of the destination campaign whose settings the new one copies. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `name` (string, required). 1 to 255 characters. - `daily_budget` (number, optional): Account currency; not for a TikTok CBO campaign. > 0. - `launch_status` (string, optional): Cross-platform. Default CREATED_PAUSED; SCHEDULED_* only with new_adset (+ schedule). one of `CREATED_PAUSED`, `LIVE_NOW`, `SCHEDULED_PAUSED`, `SCHEDULED_LIVE`. - `schedule` (object, optional): A new ad set's schedule, in the destination account's time zone - `date` (string, required). matches `^\d{4}-\d{2}-\d{2}$`. - `time` (string, required). matches `^\d{2}:\d{2}$`. - `end_date` (string, optional). matches `^\d{4}-\d{2}-\d{2}$`. - `end_time` (string, optional). matches `^\d{2}:\d{2}$`. - `extra_fields` (object, optional): What the plan asked for (recap.missing): the destination's identity / profile / page, URL, CTA, headline - `identity_id` (string, optional): TikTok: the identity the ads run under. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `identity_type` (string, optional): TikTok: one of the brand's TikTok accounts, TT_USER or BC_AUTH_TT through its Business Center (TikTok refuses a custom identity on these ads). one of `TT_USER`, `BC_AUTH_TT`. - `identity_bc_id` (string, optional): TikTok BC_AUTH_TT: the Business Center. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `profile_id` (string, optional): Snapchat: the public profile. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `page_id` (string, optional): Meta: the Facebook page. matches `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `website_url` (string, optional). up to 2,048 characters. - `call_to_action` (string, optional). up to 40 characters. - `headline` (string, optional): Snapchat: 34 characters. up to 255 characters. - `meta_texts` (map, optional): Destination Meta: each source ad's texts (by source ad id), instead of the ones the plan suggested (recap.meta_texts). keys match `^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`. - `primary_text` (string, optional). up to 2,200 characters. - `headline` (string, optional). up to 255 characters. - `description` (string, optional). up to 255 characters. - `options` (object, optional): Meta → Meta copy only - `keep_social_proof` (boolean, optional): Default true: reuse the source post (likes, comments) when Meta allows it - `status` (string, optional): After the copy: PAUSED (default), ACTIVE, or INHERIT (on only what is on in the source). one of `PAUSED`, `ACTIVE`, `INHERIT`. - `name_suffix` (string, optional). up to 100 characters. - `mapping` (map, optional): Meta → Meta copy: values for the plan's fields (recap.fields: pixel, page, audience, budget…), by key Response 201 - `id` (string) - `status` (string). one of `prepared`. - `ready` (boolean) - `recap` (object) - `ask` (string): When not ready: what to give, then prepare it again - `expires_at` (string (date-time)) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/bridge-transfers' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "source": { "platform": "meta", "ad_account_id": "act_1234567890", "ad_ids": [ "120200000000000003" ] }, "destination": { "platform": "tiktok", "ad_account_id": "7000000000000000001", "campaign_id": "1800000000000000001", "adset_id": "1800000000000000002" } }' ``` #### Confirm bridge transfer: POST /bridge-transfers/{id}/confirm https://www.apogee.ad/developers/api/confirm-bridge-transfer/ Run a prepared Bridge transfer, after the user said yes to its recap. The plan is checked again first (changed: 422, nothing transferred). Answers 202 with the outcome when the run ended within ~25 s (finished true), else status running: follow it with GET /bridge-transfers/{id}. A transfer runs once (409), within 24 hours (410). - Scope: `launch` - Plans: Pro and Scale - Credits: 15 credits per ad created, counted at the end (an ad that failed is free), plus 120 for a transfer to Meta (texts written by AI) - Rate limit: confirmation (30 per 15 minutes on Pro, 60 on Scale) - Idempotency-Key: required - MCP: `bridge_transfer` Parameters: - `id` (string, required, in path): Bridge transfer id. 1 to 64 characters; matches `^[\w-]+$`. Body (application/json): - `confirm` (constant, required): true = the user said yes to the recap. always `true`. Response 202 - `id` (string) - `status` (string). one of `prepared`, `running`, `completed`, `partial`, `failed`, `not_run`, `unknown`, `expired`. - `finished` (boolean) - `ads_created` (number or null) - `adsets` (any) - `campaigns` (number or null) - `failures` (array of strings) - `error` (string or null) - `may_have_written` (boolean) - `started_at` (string or null) - `at` (string or null) - `ready` (boolean) - `expires_at` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 409 (action_already_ran, idempotency_in_progress, plan_already_launched); 410 (action_expired, plan_expired); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 422 (budget_above_cap, idempotency_key_reused, launch_refused, missing_answers); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl -X POST 'https://api.apogee.ad/api/v1/bridge-transfers/120200000000000001/confirm' \ -H "Authorization: Bearer $APOGEE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "confirm": true }' ``` #### Get bridge transfer: GET /bridge-transfers/{id} https://www.apogee.ad/developers/api/get-bridge-transfer/ Where a Bridge transfer is: prepared, running, completed, partial, failed, not_run or unknown. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `bridge_transfer_status` Parameters: - `id` (string, required, in path): Bridge transfer id. 1 to 64 characters; matches `^[\w-]+$`. Response 200 - `id` (string) - `status` (string). one of `prepared`, `running`, `completed`, `partial`, `failed`, `not_run`, `unknown`, `expired`. - `finished` (boolean) - `ads_created` (number or null) - `adsets` (any) - `campaigns` (number or null) - `failures` (array of strings) - `error` (string or null) - `may_have_written` (boolean) - `started_at` (string or null) - `at` (string or null) - `ready` (boolean) - `expires_at` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/bridge-transfers/120200000000000001' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### List bridged ads: GET /bridged-ads https://www.apogee.ad/developers/api/list-bridged-ads/ The ads already bridged from a source ad account (the app's Bridged tab), newest first. Your own transfers, and another Apogee user's from the same source account when their destination account is yours too. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - Paginated: `limit` and `cursor`, answers `{ data, has_more, next_cursor }` - MCP: `list_bridge_transfers` Parameters: - `limit` (integer, optional, in query): Page size. default `50`; 1 to 200. - `cursor` (string, optional, in query): next_cursor of the previous page - `source_platform` (string, optional, in query): Default meta. one of `meta`, `tiktok`, `snapchat`. - `source_ad_account_id` (string, required, in query): The ad account (list_ad_accounts): Meta act_…, a TikTok advertiser id, a Snapchat ad account id. matches `^(act_\d{1,32}|[A-Za-z0-9][A-Za-z0-9-]{0,63})$`. Response 200 - `data` (array of objects) - `source_ad_id` (string) - `name` (string or null) - `last_at` (string or null) - `count` (number or null) - `targets` (array of objects) - `platform` (string). one of `meta`, `tiktok`, `snapchat`. - `ad_account_id` (string or null) - `ad_id` (string or null) - `adset_id` (string or null) - `smart_plus` (boolean) - `at` (string or null) - `has_more` (boolean) - `next_cursor` (string or null) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/bridged-ads?source_ad_account_id=act_1234567890' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get bridge stats: GET /bridged-ads/stats https://www.apogee.ad/developers/api/get-bridge-stats/ The Bridge's Stats tab: each creative bridged from a Meta ad account, its source ad next to its TikTok and Snapchat copies. Newest bridge first, over since → until (7-day click attribution on every platform). Per platform: status (ok, no_target = never bridged there, not_connected = that destination account is not one of yours, error), the ads, inputs (raw sums: spend, revenue, impressions, reach, link clicks, landing page views, adds to cart, purchases, 3 s and 15 s plays) and kpis (the app's columns: ROAS, CPM, CTR, CPC, cost per purchase, hook and hold rates…), each in its own ad account's currency: platforms are never added together. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_bridge_stats` Parameters: - `source_ad_account_id` (string, required, in query): The Meta ad account the ads were bridged from (act_…). matches `^(act_)?\d{1,32}$`. - `since` (string, required, in query): First day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, required, in query): Last day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. Response 200 - `range` (object) - `since` (string) - `until` (string) - `creatives` (array of objects) - `source_ad_id` (string) - `platforms` (object) - `meta` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `ads` (array of objects) - `id` (string or null) - `ad_account_id` (string or null) - `adset_id` (string or null) - `smart_plus` (boolean) - `at` (string or null) - `inputs` (object or null): Raw sums over the platform's ads, in its ad account currency (null = the platform can't report it) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `reach` (number or null) - `link_clicks` (number or null) - `landing_page_views` (number or null) - `add_to_cart` (number or null) - `purchases` (number or null) - `hook_views` (number or null) - `hold_views` (number or null) - `kpis` (object or null): The app's columns from the inputs: spend, revenue, roas, atc_rate (+ atc_rate_basis), cpm, cpmr, reach, impressions, cpc, cpatc, cpp, link_clicks, add_to_cart, purchases, hook_rate, hold_rate, ctr - `hint` (string or null) - `error` (string or null) - `tiktok` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `ads` (array of objects) - `id` (string or null) - `ad_account_id` (string or null) - `adset_id` (string or null) - `smart_plus` (boolean) - `at` (string or null) - `inputs` (object or null): Raw sums over the platform's ads, in its ad account currency (null = the platform can't report it) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `reach` (number or null) - `link_clicks` (number or null) - `landing_page_views` (number or null) - `add_to_cart` (number or null) - `purchases` (number or null) - `hook_views` (number or null) - `hold_views` (number or null) - `kpis` (object or null): The app's columns from the inputs: spend, revenue, roas, atc_rate (+ atc_rate_basis), cpm, cpmr, reach, impressions, cpc, cpatc, cpp, link_clicks, add_to_cart, purchases, hook_rate, hold_rate, ctr - `hint` (string or null) - `error` (string or null) - `snapchat` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `ads` (array of objects) - `id` (string or null) - `ad_account_id` (string or null) - `adset_id` (string or null) - `smart_plus` (boolean) - `at` (string or null) - `inputs` (object or null): Raw sums over the platform's ads, in its ad account currency (null = the platform can't report it) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `reach` (number or null) - `link_clicks` (number or null) - `landing_page_views` (number or null) - `add_to_cart` (number or null) - `purchases` (number or null) - `hook_views` (number or null) - `hold_views` (number or null) - `kpis` (object or null): The app's columns from the inputs: spend, revenue, roas, atc_rate (+ atc_rate_basis), cpm, cpmr, reach, impressions, cpc, cpatc, cpp, link_clicks, add_to_cart, purchases, hook_rate, hold_rate, ctr - `hint` (string or null) - `error` (string or null) - `name` (string or null) - `last_bridged_at` (string or null) - `count` (number or null) - `source_ad_account_id` (string) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/bridged-ads/stats?source_ad_account_id=act_1234567890&since=2026-10-01&until=2026-10-21' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` #### Get bridge daily stats: GET /bridged-ads/daily-stats https://www.apogee.ad/developers/api/get-bridge-daily-stats/ The Bridge's Daily tab: per platform and per day, the numbers of every creative bridged from a Meta ad account. Day by day over since → until (366 days at most): spend, revenue, ROAS, impressions, link clicks and purchases summed over the Meta source ads and, separately, over their TikTok and Snapchat copies, with each platform's totals, status and currency. Platforms are never summed together. - Scope: `read` - Plans: Pro and Scale - Credits: Free up to the daily free reads (5,000 a day on Pro, 10,000 on Scale), then 1 credit per read - Rate limit: read (600 per 15 minutes on Pro, 1,200 on Scale) - MCP: `get_bridge_daily_stats` Parameters: - `source_ad_account_id` (string, required, in query): The Meta ad account the ads were bridged from (act_…). matches `^(act_)?\d{1,32}$`. - `since` (string, required, in query): First day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. - `until` (string, required, in query): Last day, YYYY-MM-DD. matches `^\d{4}-\d{2}-\d{2}$`. Response 200 - `range` (object) - `since` (string) - `until` (string) - `platforms` (object) - `meta` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `currency` (string or null) - `totals` (object or null) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `days` (array of objects or null): Every day of the range (zeros when nothing delivered); null when status is not ok - `date` (string) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `hint` (string or null) - `error` (string or null) - `tiktok` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `currency` (string or null) - `totals` (object or null) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `days` (array of objects or null): Every day of the range (zeros when nothing delivered); null when status is not ok - `date` (string) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `hint` (string or null) - `error` (string or null) - `snapchat` (object) - `status` (string). one of `ok`, `no_target`, `not_connected`, `error`. - `currency` (string or null) - `totals` (object or null) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `days` (array of objects or null): Every day of the range (zeros when nothing delivered); null when status is not ok - `date` (string) - `spend` (number or null) - `revenue` (number or null) - `impressions` (number or null) - `link_clicks` (number or null) - `purchases` (number or null) - `roas` (number or null) - `hint` (string or null) - `error` (string or null) - `source_ad_account_id` (string) Errors (problem+json, https://www.apogee.ad/developers/errors/): 400 (idempotency_key_required, not_supported, request_refused, validation_failed); 401 (invalid_api_key); 403 (account_not_connected, forbidden, insufficient_scope, meta_permission_denied, platform_permission_denied, upgrade_required); 404 (not_found); 412 (drive_not_connected, meta_not_connected, platform_not_connected); 429 (daily_read_cap, meta_rate_limited, platform_rate_limited, quota_exceeded, rate_limited); 500 (internal_error); 502 (drive_error, launch_failed, meta_error, platform_error, upstream_error). Example: ```bash curl 'https://api.apogee.ad/api/v1/bridged-ads/daily-stats?source_ad_account_id=act_1234567890&since=2026-10-01&until=2026-10-21' \ -H "Authorization: Bearer $APOGEE_API_KEY" ``` ## Changelog ### 2026-10-24: The MCP server and the REST API, in the Pro and Scale plans - The MCP server (`https://app.apogee.ad/mcp`, 80 tools) and the REST API v1 (`https://api.apogee.ad/api/v1`, 48 operations, contract 2026-10-06) are part of the Pro and Scale plans. - Credits: each plan includes credits every month, and what changes something on an ad platform, creates ads or runs Apogee's AI spends them. Reads are free up to a daily cap. Credit packs top them up. - Rate limits per plan: Scale has twice the limits of Pro. On these plans, every key and assistant connection of an account shares one counter.