Xylo API Documentation
Base URL: https://api.xylomcp.com (production) or http://localhost:3001 (dev)
Connect via MCP
Most users drive Xylo through its Model Context Protocol (MCP) connector rather than raw REST. One connector, https://xylomcp.com/api/mcp, carries 300+ ad operations across Meta (Facebook & Instagram), Google, TikTok, and X, approved TikTok Shop commerce workflows, Klaviyo email and SMS, plus Google Analytics (GA4) reporting through a compact set of tools. Install the Xylo plugin in Codex, or add the connector in Claude, ChatGPT, Cursor, or Claude Code, then authorize once through OAuth. Meta, Google, and TikTok Ads entity routes use the channel parameter; TikTok Shop uses commerce; X, Klaviyo, and Google Analytics use their dedicated dispatchers. Pick your app below for the current setup steps.
Steps
Add Xylo to ClaudeOpens Claude with Xylo's name and connector URL pre-filled. Review the details, confirm, then connect with OAuth.- 1Use the button above to open Claude's custom connector form with Xylo pre-filled.
- 2Review the name and URL, click "Add", then click "Connect" to authenticate through OAuth.
- 3For Team or Enterprise workspaces, an Owner may need to add Xylo from Organization settings → Connectors first.
Upload creative from Claude
Optional. Add this domain in Claude's Additional allowed domains so it can send uploaded creative to Xylo. Open allowed domains
api.xylomcp.comOne URL for everything: 300+ ad operations across Meta, Google, TikTok & X, plus Klaviyo email and SMS workflows, through tools that load instantly in any AI client.
https://xylomcp.com/api/mcpOnce connected, have your AI learn your accounts one brand at a time. Swap in your brand name and website below; it studies your winners, copy, budgets and conventions, then saves it all to Xylo so future requests need no hand-holding.
Use Xylo. Learn my [brand name] accounts. Set up the brand and get fully up to speed on it, the website is [yourwebsite.com]. When you're done, I should be able to ask you to launch new ads in a later conversation using all the information you store in Xylo, without needing to ask me any setup questions.
Most of the 29 tools are dispatchers: the agent picks a route with top-level discriminators (channel, resource, action, mode, lens, topic) and nests every route argument under a single params object. Listing your active Meta campaigns looks like this:
query({ channel: "meta", resource: "campaign", mode: "list", params: { status: "ACTIVE" } })No schema memorization needed: describe returns any route's exact input schema from its discriminators, search_tools finds routes by intent, and wrong guesses are cheap because errors return the route's schema plus a valid example. The surface also includes cross-platform reporting and competitor intelligence: ad-library research that pulls any brand's public ads from Meta, Google, and TikTok (ad copy, creatives, CTAs, and run dates) with no connected ad account required. MCP connections use OAuth 2.1 (PKCE), so you approve account access once without copying an API key. The REST tab uses a Xylo API key (xy_sk_...) for direct HTTP calls. The rest of this page documents that underlying REST API.
The agent can also generate new ad creative directly, using several AI models picked for the job. Save a generated image and Xylo pushes it to Meta permanently, so it never expires. For catalog products, the agent can pull a product's own photos out of your Meta catalog as generation references instead of a text description, so the output matches your real product. It also builds its own account context on connect: it studies your site, then saves a brand profile, reference assets (logo, product shots), and a library of winning ads deduped by creative, each with performance stats for its source window and an AI description. On paid plans, Xylo selects proven Meta winners during onboarding and refreshes them automatically every week; Free accounts keep the manual inspect-and-save flow. Both generated creative and recommendations stay on-brand and grounded in what already works. Generation is billed in credits included with paid plans, with prepaid top-up packs available.
Quick Start for AI Agents
Xylo is a unified REST API for four ad platforms, TikTok Shop commerce, Klaviyo as an owned email and SMS channel, and Google Analytics (GA4) as a measurement source. Meta uses the core /v1/ routes, Google Ads lives under /v1/google/, TikTok Ads under /v1/tiktok/, TikTok Shop under /v1/tiktok-shop/, X Ads under /v1/x/, Klaviyo under /v1/klaviyo/, and Google Analytics under /v1/ga4/. Each integration has its own section below.
- Budgets in dollars — Meta uses cents internally; Xylo accepts and returns dollars (e.g.
daily_budget: 50.00). - Flat insights — Meta returns conversions as nested
actionsarrays. Xylo flattens them to top-levelconversions,cost_per_conversion, androasfields. - Single-call ad creation — Pass
image_urlin the creative object and Xylo auto-uploads the image, creates the creative, and attaches it to the ad. - Default PAUSED on writes — All create endpoints default to
status: "PAUSED"as a safety measure. You must explicitly setstatus: "ACTIVE"to go live. - Clean targeting — Simplified targeting format compared to Meta's deeply nested structure.
3-Step Getting Started
Step 1: List connected ad accounts
curl https://api.xylomcp.com/v1/accounts \ -H "x-api-key: xy_sk_your_key"
Step 2: Get campaigns for an account
curl https://api.xylomcp.com/v1/campaigns \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Step 3: Get performance insights
curl "https://api.xylomcp.com/v1/insights?level=campaign&date_from=2026-03-01&date_to=2026-03-15" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Full Agent Workflow Example
A typical AI agent lifecycle: list accounts, pick one, read campaigns, check performance, create a new campaign.
# 1. List accounts (no x-ad-account header needed)
GET /v1/accounts
# Response: array of accounts with id, name, currency, timezone
# 2. Pick an account, set x-ad-account header for all subsequent requests
# x-ad-account: act_123456789
# 3. List campaigns
GET /v1/campaigns?status=ACTIVE
# 4. Get insights for the last 7 days
GET /v1/insights?level=campaign&date_from=2026-03-01&date_to=2026-03-15
# 5. Create a new campaign (defaults to PAUSED)
POST /v1/campaigns
{ "name": "Brand Awareness Q2", "objective": "OUTCOME_SALES", "daily_budget": 50.00 }
# 6. Create an ad set under the campaign
POST /v1/campaigns/:campaign_id/adsets
{ "name": "Broad - Interests", "billing_event": "IMPRESSIONS", "optimization_goal": "VALUE",
"targeting": { "age_min": 18, "age_max": 65, "geo_locations": { "countries": ["US"] } } }
# 7. Create an ad (image_url auto-uploaded)
POST /v1/adsets/:adset_id/ads
{ "name": "Video Ad - 15s", "creative": { "page_id": "112233445566778",
"headline": "Shop Now", "body": "Best deals today", "link": "https://example.com",
"call_to_action": "SHOP_NOW", "image_url": "https://example.com/hero.jpg" } }Authentication
All API requests require two headers. The x-ad-account header is NOT required for GET /v1/accounts and POST /v1/connect-sessions.
| Parameter | Type | Required | Description |
|---|---|---|---|
| x-api-key | string | required | Your Xylo API key (starts with xy_sk_) |
| x-ad-account | string | required | Meta ad account ID (e.g. act_123456789). NOT required for GET /v1/accounts and POST /v1/connect-sessions. |
curl https://api.xylomcp.com/v1/campaigns \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Connect Sessions
Before making API calls, users must connect their Meta ad account via OAuth. Create a connect session, then redirect the user to the returned connect URL. This endpoint does NOT require the x-ad-account header.
/v1/connect-sessions| Parameter | Type | Required | Description |
|---|---|---|---|
| redirect_url | string | optional | URL to redirect user to after OAuth (e.g. https://yourapp.com/callback) |
{
"redirect_url": "https://yourapp.com/callback"
}{
"data": {
"connect_url": "https://xylomcp.com/connect?session=..."
}
}Redirect your user to the connect_url. After they authorize, they'll be sent to your redirect_url.
Response Format
All responses follow a consistent envelope format with data and meta fields on success, or an error object on failure.
Success (2xx)
{
"data": { ... },
"meta": {
"cached": true,
"cache_age_seconds": 45,
"meta_api_version": "v25.0"
}
}Paginated Success (2xx)
{
"data": [ ... ],
"meta": {
"cached": false,
"cache_age_seconds": null,
"meta_api_version": "v25.0"
},
"paging": {
"next_cursor": "abc123",
"has_more": true
}
}Error (4xx/5xx)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The 'daily_budget' field must be at least 1.00 (dollars).",
"meta_error": null
}
}Meta API Error (502)
{
"error": {
"code": "META_API_ERROR",
"message": "Meta rejected the request.",
"meta_error": {
"message": "(#100) Daily budget is too low.",
"type": "OAuthException",
"code": 100
}
}
}The meta object on success always includes cached, cache_age_seconds, and meta_api_version. Paginated responses include a paging object with next_cursor and has_more. Use after=next_cursor to fetch the next page.
Caching
All GET endpoints are cached. Add ?refresh=true to bypass the cache and fetch fresh data from Meta.
| Data Type | Cache TTL |
|---|---|
| Account structure (campaigns, ad sets, ads) | 5 minutes |
| Performance metrics (insights) | 15 minutes |
| Ad creatives | 30 minutes |
| Account info | 1 hour |
curl "https://api.xylomcp.com/v1/campaigns?refresh=true" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Rate Limits
Rate limits are per API key, measured in requests per minute (rpm). Every response includes rate limit headers.
| Plan | Rate Limit |
|---|---|
| Free | 60 rpm |
| Brand | 120 rpm |
| Agency | 600 rpm |
Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Accounts
List Accounts
/v1/accountsReturns all connected Meta ad accounts. This endpoint does NOT require the x-ad-account header.
curl https://api.xylomcp.com/v1/accounts \ -H "x-api-key: xy_sk_your_key"
{
"data": [
{
"id": "act_123456789",
"name": "Acme Corp Ad Account",
"currency": "USD",
"timezone": "America/Los_Angeles",
"status": "ACTIVE",
"spend_cap": 0,
"amount_spent": 1234.56,
"connected_at": "2026-03-01T10:00:00.000+00:00"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Campaigns
Campaigns — List
/v1/campaignsReturns all campaigns for the specified ad account. Supports filtering and pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | optional | Filter: ACTIVE, PAUSED, DELETED, ARCHIVED |
| limit | integer | optional | Max results (default 25, max 100) |
| after | string | optional | Pagination cursor from paging.next_cursor |
| refresh | boolean | optional | Set to true to bypass cache |
curl "https://api.xylomcp.com/v1/campaigns?status=ACTIVE&limit=10" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "120211234567890",
"name": "Summer Sale 2026",
"status": "ACTIVE",
"objective": "OUTCOME_SALES",
"daily_budget": 50.00,
"lifetime_budget": null,
"budget_remaining": 50.00,
"spend_today": null,
"start_time": "2026-03-01T00:00:00-0800",
"stop_time": null,
"created_time": "2026-03-01T09:00:00-0800",
"updated_time": "2026-03-15T12:00:00-0800"
}
],
"meta": { "cached": true, "cache_age_seconds": 45, "meta_api_version": "v25.0" },
"paging": { "next_cursor": "abc123", "has_more": true }
}Campaigns — Read
/v1/campaigns/:idReturns a single campaign by its Meta campaign ID. Same data shape as list.
curl https://api.xylomcp.com/v1/campaigns/120211234567890 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": {
"id": "120211234567890",
"name": "Summer Sale 2026",
"status": "ACTIVE",
"objective": "OUTCOME_SALES",
"daily_budget": 50.00,
"lifetime_budget": null,
"budget_remaining": 50.00,
"spend_today": null,
"start_time": "2026-03-01T00:00:00-0800",
"stop_time": null,
"created_time": "2026-03-01T09:00:00-0800",
"updated_time": "2026-03-15T12:00:00-0800"
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Campaigns — Create
/v1/campaignsCreates a new campaign. Defaults to status: "PAUSED" as a safety measure. Budget values are in dollars.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Campaign name |
| objective | string | required | OUTCOME_SALES, OUTCOME_TRAFFIC, OUTCOME_AWARENESS, OUTCOME_LEADS, OUTCOME_ENGAGEMENT |
| status | string | optional | ACTIVE or PAUSED (default PAUSED) |
| daily_budget | number | optional | Daily budget in dollars (e.g. 50.00) |
| lifetime_budget | number | optional | Lifetime budget in dollars |
| start_time | string | optional | ISO 8601 start time |
| stop_time | string | optional | ISO 8601 stop time |
| special_ad_categories | array | optional | Special ad categories if applicable |
| buying_type | string | optional | Buying type (e.g. AUCTION) |
curl -X POST https://api.xylomcp.com/v1/campaigns \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Campaign",
"objective": "OUTCOME_SALES",
"daily_budget": 75.00
}'{
"data": {
"id": "120219876543210",
"name": "Summer Campaign",
"status": "PAUSED",
"objective": "OUTCOME_SALES",
"daily_budget": 75.00,
"lifetime_budget": null,
"budget_remaining": 75.00,
"spend_today": null,
"start_time": null,
"stop_time": null,
"created_time": "2026-03-15T10:00:00-0700",
"updated_time": "2026-03-15T10:00:00-0700"
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Campaigns — Update
/v1/campaigns/:idPartial update. Only include the fields you want to change.
curl -X PATCH https://api.xylomcp.com/v1/campaigns/120219876543210 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "status": "ACTIVE", "daily_budget": 100.00 }'{
"data": {
"id": "120219876543210",
"name": "Summer Campaign",
"status": "ACTIVE",
"objective": "OUTCOME_SALES",
"daily_budget": 100.00,
"lifetime_budget": null,
"budget_remaining": 100.00,
"spend_today": null,
"start_time": null,
"stop_time": null,
"created_time": "2026-03-15T10:00:00-0700",
"updated_time": "2026-03-15T12:00:00-0700"
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Campaigns — Delete
/v1/campaigns/:idSets the campaign status to DELETED in Meta. This is not a hard delete.
curl -X DELETE https://api.xylomcp.com/v1/campaigns/120219876543210 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Campaigns — Duplicate
/v1/campaigns/:id/duplicateDuplicate a campaign. By default this is a shallow copy (campaign shell only). Pass include_children: true to also clone every ad set, and (unless include_ads: false) the ads inside them. The new campaign is always created in PAUSED status.
| Parameter | Type | Required | Description |
|---|---|---|---|
| new_name | string | optional | Name for the new campaign (defaults to `{source name} (Copy)`) |
| include_children | boolean | optional | Deep duplicate — also copy all ad sets under this campaign |
| include_ads | boolean | optional | When include_children is true, also copy ads (default true) |
curl -X POST https://api.xylomcp.com/v1/campaigns/120211234567890/duplicate \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "new_name": "Summer Sale 2026 (Copy)", "include_children": true }'{
"data": { "campaign": { "from": "120211234567890", "to": "120219876543299" } },
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Ad Sets
Ad Sets — List
/v1/campaigns/:campaign_id/adsetsReturns all ad sets within a campaign.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results (default 25, max 100) |
| after | string | optional | Pagination cursor from paging.next_cursor |
| refresh | boolean | optional | Set to true to bypass cache |
curl https://api.xylomcp.com/v1/campaigns/120211234567890/adsets \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "120215556667770",
"name": "US Women 25-45",
"status": "ACTIVE",
"campaign_id": "120211234567890",
"daily_budget": null,
"lifetime_budget": null,
"billing_event": "IMPRESSIONS",
"optimization_goal": "VALUE",
"bid_strategy": null,
"promoted_object": { "pixel_id": "1234567890", "custom_event_type": "PURCHASE" },
"targeting": {
"age_min": 18,
"age_max": 65,
"geo_locations": {
"countries": ["US"],
"location_types": ["home", "recent"]
}
},
"start_time": "2026-03-01T00:00:00-0800",
"end_time": null,
"created_time": "2026-03-01T09:00:00-0800",
"updated_time": "2026-03-01T09:05:00-0800"
}
],
"meta": { "cached": true, "cache_age_seconds": 120, "meta_api_version": "v25.0" },
"paging": { "next_cursor": null, "has_more": false }
}Ad Sets — Read
/v1/adsets/:idReturns a single ad set by ID. Same data shape as the list response.
curl https://api.xylomcp.com/v1/adsets/120215556667770 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": {
"id": "120215556667770",
"name": "US Women 25-45",
"status": "ACTIVE",
"campaign_id": "120211234567890",
"daily_budget": null,
"lifetime_budget": null,
"billing_event": "IMPRESSIONS",
"optimization_goal": "VALUE",
"bid_strategy": null,
"promoted_object": { "pixel_id": "1234567890", "custom_event_type": "PURCHASE" },
"targeting": {
"age_min": 18,
"age_max": 65,
"geo_locations": {
"countries": ["US"],
"location_types": ["home", "recent"]
}
},
"start_time": "2026-03-01T00:00:00-0800",
"end_time": null,
"created_time": "2026-03-01T09:00:00-0800",
"updated_time": "2026-03-01T09:05:00-0800"
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Ad Sets — Create
/v1/campaigns/:campaign_id/adsetsCreates a new ad set under a campaign. Defaults to status: "PAUSED".
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Ad set name |
| billing_event | string | required | e.g. IMPRESSIONS |
| optimization_goal | string | required | e.g. VALUE, CONVERSIONS, LINK_CLICKS, REACH |
| targeting | object | required | Targeting spec: { age_min, age_max, geo_locations: { countries: [...] } } |
| status | string | optional | ACTIVE or PAUSED (default PAUSED) |
| daily_budget | number | optional | Daily budget in dollars |
| lifetime_budget | number | optional | Lifetime budget in dollars |
| bid_strategy | string | optional | Bid strategy |
| promoted_object | object | optional | What the ad set optimizes for. Required for conversion optimization: { pixel_id, custom_event_type } — or custom_event_type: "OTHER" + custom_event_str for a custom pixel event, or custom_conversion_id for a Custom Conversion |
| start_time | string | optional | ISO 8601 start time |
| end_time | string | optional | ISO 8601 end time |
curl -X POST https://api.xylomcp.com/v1/campaigns/120211234567890/adsets \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"name": "Broad - Interests",
"billing_event": "IMPRESSIONS",
"optimization_goal": "VALUE",
"targeting": {
"age_min": 18,
"age_max": 65,
"geo_locations": { "countries": ["US"] }
}
}'Ad Sets — Update
/v1/adsets/:idPartial update. Only include the fields you want to change.
curl -X PATCH https://api.xylomcp.com/v1/adsets/120215556667770 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "daily_budget": 50.00, "status": "ACTIVE" }'Ad Sets — Delete
/v1/adsets/:idDeletes the ad set in Meta.
curl -X DELETE https://api.xylomcp.com/v1/adsets/120215556667770 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Ad Sets — Duplicate
/v1/adsets/:id/duplicateDuplicate an ad set. Shallow copy by default (ad-set shell only — preserves promoted_object, targeting, bid settings, etc.). Pass include_children: true to also clone ads. Optionally move the copy into a different campaign via target_campaign_id. The new ad set is always created in PAUSED.
| Parameter | Type | Required | Description |
|---|---|---|---|
| new_name | string | optional | Name for the new ad set (defaults to `{source name} (Copy)`) |
| include_children | boolean | optional | Deep duplicate — also copy ads in this ad set |
| include_ads | boolean | optional | When include_children is true, also copy ads (default true) |
| target_campaign_id | string | optional | Move the copy into this campaign instead of the source campaign |
curl -X POST https://api.xylomcp.com/v1/adsets/120215556667770/duplicate \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "new_name": "US Women 25-45 (Copy)", "include_children": true }'{
"data": { "adset": { "from": "120215556667770", "to": "120215556668899" } },
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Budget Schedules
Temporarily boost the budget of a campaign or ad set during a sale, launch, or peak window. A schedule applies an absolute budget override or a multiplier (e.g. 2x daily budget) during the time range, then reverts. Meta supports this natively on campaigns and ad sets.
budget_value_type is ABSOLUTE or MULTIPLIER. ABSOLUTE is in cents (Meta's native unit — 5000 = $50.00). MULTIPLIER is an integer between 100 and 1000 where 100 = 1.0x, 200 = 2.0x, and 1000 = 10x.
Budget Schedules — List
/v1/budget-schedulesList all schedules attached to a campaign or ad set.
| Parameter | Type | Required | Description |
|---|---|---|---|
| entity_id | string | required | Campaign or ad set ID |
curl "https://api.xylomcp.com/v1/budget-schedules?entity_id=120211234567890" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "99887766554433",
"time_start": "2026-04-20T00:00:00-0700",
"time_end": "2026-04-22T23:59:59-0700",
"budget_value": 200,
"budget_value_type": "MULTIPLIER"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Budget Schedules — Create
/v1/budget-schedulesCreate a schedule. time_start must be before time_end.
| Parameter | Type | Required | Description |
|---|---|---|---|
| entity_id | string | required | Campaign or ad set ID to apply the schedule to |
| time_start | string | required | ISO 8601 start time of the boost window |
| time_end | string | required | ISO 8601 end time of the boost window |
| budget_value | number | required | Amount in cents (ABSOLUTE) or 100–1000 integer (MULTIPLIER, where 200 = 2.0x) |
| budget_value_type | string | required | ABSOLUTE or MULTIPLIER |
curl -X POST https://api.xylomcp.com/v1/budget-schedules \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"entity_id": "120211234567890",
"time_start": "2026-04-20T00:00:00-0700",
"time_end": "2026-04-22T23:59:59-0700",
"budget_value": 200,
"budget_value_type": "MULTIPLIER"
}'{
"data": { "id": "99887766554433" },
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Budget Schedules — Delete
/v1/budget-schedules/:idRemove a schedule. Does not affect the underlying campaign/ad-set budget.
curl -X DELETE https://api.xylomcp.com/v1/budget-schedules/99887766554433 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Ads
Ads — List
/v1/adsets/:adset_id/adsReturns all ads within an ad set.
curl https://api.xylomcp.com/v1/adsets/120215556667770/ads \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "120218889990001",
"name": "Creative A - Carousel",
"status": "ACTIVE",
"adset_id": "120215556667770",
"creative": {
"page_id": "112233445566778",
"headline": "",
"body": "",
"link": "",
"call_to_action": ""
},
"created_time": "2026-03-01T09:10:00-0800",
"updated_time": "2026-03-10T14:30:00-0800"
}
],
"meta": { "cached": true, "cache_age_seconds": 60, "meta_api_version": "v25.0" },
"paging": { "next_cursor": null, "has_more": false }
}Ads — Read
/v1/ads/:idReturns a single ad by ID. Same data shape as the list response.
curl https://api.xylomcp.com/v1/ads/120218889990001 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": {
"id": "120218889990001",
"name": "Creative A - Carousel",
"status": "ACTIVE",
"adset_id": "120215556667770",
"creative": {
"page_id": "112233445566778",
"headline": "",
"body": "",
"link": "",
"call_to_action": ""
},
"created_time": "2026-03-01T09:10:00-0800",
"updated_time": "2026-03-10T14:30:00-0800"
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Ads — Create
/v1/adsets/:adset_id/adsCreates a new ad under an ad set. Defaults to status: "PAUSED". Pass image_url in the creative object and Xylo will auto-upload the image.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Ad name |
| creative | object | required | Creative object with page_id, headline, body, link, call_to_action. Optionally image_url (Xylo auto-uploads). |
| status | string | optional | ACTIVE or PAUSED (default PAUSED) |
Creative object fields: page_id (required), headline, body, link, call_to_action, image_url (optional, auto-uploaded).
curl -X POST https://api.xylomcp.com/v1/adsets/120215556667770/ads \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"name": "Static Image - Blue",
"creative": {
"page_id": "112233445566778",
"headline": "Shop Now",
"body": "Great deals on top products",
"link": "https://example.com/summer",
"call_to_action": "SHOP_NOW",
"image_url": "https://example.com/hero.jpg"
}
}'Ads — Update
/v1/ads/:idPartial update. Only include the fields you want to change.
curl -X PATCH https://api.xylomcp.com/v1/ads/120218889990001 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "name": "Updated Ad Name", "status": "ACTIVE" }'Ads — Delete
/v1/ads/:idDeletes the ad in Meta.
curl -X DELETE https://api.xylomcp.com/v1/ads/120218889990001 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Insights
/v1/insightsRetrieve performance metrics. Xylo flattens Meta's nested actions arrays into top-level conversions, cost_per_conversion, and roas fields. All monetary values are in dollars (Meta uses cents internally).
| Parameter | Type | Required | Description |
|---|---|---|---|
| level | string | required | account, campaign, adset, or ad |
| date_from | string | optional | Start date YYYY-MM-DD (default: 7 days ago) |
| date_to | string | optional | End date YYYY-MM-DD (default: yesterday) |
| campaign_id | string | optional | Filter by campaign ID |
| adset_id | string | optional | Filter by ad set ID |
| ad_id | string | optional | Filter by ad ID |
| time_granularity | string | optional | all (default, aggregated), daily, weekly, monthly |
| fields | string | optional | Comma-separated metrics (default: spend,impressions,clicks,ctr,cpc,cpm,reach,frequency,conversions,cost_per_conversion,roas) |
| breakdowns | string | optional | age, gender, country, region, publisher_platform, platform_position, device_platform, product_id (per-product rows for catalog ads) |
| refresh | boolean | optional | Set to true to bypass cache |
curl "https://api.xylomcp.com/v1/insights?level=campaign&date_from=2026-03-01&date_to=2026-03-15" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"campaign_id": "120212223334440",
"campaign_name": "Retargeting - Cart Abandoners",
"date_start": "2026-03-01",
"date_stop": "2026-03-15",
"spend": 1234.56,
"impressions": 50000,
"clicks": 1500,
"ctr": 3.0,
"cpc": 0.82,
"cpm": 24.69,
"reach": 40000,
"frequency": 1.25,
"conversions": 120,
"cost_per_conversion": 10.29,
"roas": 4.85,
"link_clicks": 1350,
"landing_page_views": 1100
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}At adset level, responses also include adset_id and adset_name. At ad level, responses also include ad_id and ad_name.
Default fields: spend, impressions, clicks, ctr, cpc, cpm, reach, frequency, conversions, cost_per_conversion, roas. Additional fields available: link_clicks, landing_page_views.
Audiences
Audiences — List
/v1/audiences| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results |
| after | string | optional | Pagination cursor |
curl https://api.xylomcp.com/v1/audiences \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "23851234567890",
"name": "Website Visitors 30d",
"type": "CUSTOM",
"approximate_count": 2200,
"status": "unknown",
"created_time": 1772531200
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Audiences — Create
/v1/audiences| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Audience name |
| type | string | required | Audience type (e.g. website) |
| rule | object | required | Rule definition: { url_contains, retention_days } |
curl -X POST https://api.xylomcp.com/v1/audiences \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"name": "Checkout Visitors - 30 Days",
"type": "website",
"rule": {
"url_contains": "/checkout",
"retention_days": 30
}
}'Audiences — Create Lookalike
/v1/audiences/lookalike| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Lookalike audience name |
| source_audience_id | string | required | Source audience ID to base lookalike on |
| country | string | required | Target country ISO code (e.g. US) |
| ratio | number | required | Lookalike ratio 0.01 - 0.20 (0.01 = top 1%) |
curl -X POST https://api.xylomcp.com/v1/audiences/lookalike \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"name": "Purchase Lookalike 1%",
"source_audience_id": "23851234567890",
"country": "US",
"ratio": 0.01
}'Audiences — Delete
/v1/audiences/:idcurl -X DELETE https://api.xylomcp.com/v1/audiences/23851234567890 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Targeting
Targeting — Search Interests
/v1/targeting/search| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | required | interests, behaviors, or demographics |
| query | string | required | Search string (e.g. 'yoga') |
curl "https://api.xylomcp.com/v1/targeting/search?type=interests&query=yoga" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "6003107904431",
"name": "Yoga",
"audience_size": 150000000
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Targeting — Search Locations
/v1/targeting/locations| Parameter | Type | Required | Description |
|---|---|---|---|
| query | string | required | Search string (e.g. 'New York') |
| type | string | optional | city, country, region, zip, or geo_market |
curl "https://api.xylomcp.com/v1/targeting/locations?query=New+York" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"name": "New York",
"audience_size": 0
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Creatives
/v1/creatives/uploadUpload an image for use in ad creatives. Accepts either a URL to download or base64 data. Returns an image hash for use in ad creation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| image_url | string | optional | URL of image to download and upload to Meta |
| image_data | string | optional | Base64-encoded image data |
| filename | string | optional | Optional filename for the image |
Provide either image_url or image_data, not both.
curl -X POST https://api.xylomcp.com/v1/creatives/upload \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "image_url": "https://example.com/hero-banner.jpg" }'{
"data": {
"image_hash": "a1b2c3d4e5f6...",
"url": "https://..."
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Note: You typically do not need to call this endpoint directly. When creating ads via POST /v1/adsets/:adset_id/ads, you can pass image_url in the creative object and Xylo will handle the upload automatically.
Media Upload
A two-step media pipeline built for AI agents: stage assets to Xylo first, then finalize to push them to Meta. Staged assets live on Vercel Blob with a public preview URL that Claude (or any vision model) can view before you commit them to an ad account.
Workflow: POST /v1/media/upload (stage) → inspect via GET /v1/media/preview/:id or the agent groups assets → POST /v1/media/finalize → use the returned meta_ref as image_hash (images) or video_id (videos) in ad creatives.
Accepted types: image/jpeg, image/jpg, image/png, video/mp4, video/quicktime. Size limits: 30 MB (images), 300 MB (videos). Staged assets auto-expire after 24 hours if not finalized.
Media — Upload
/v1/media/uploadStage a media asset to Xylo. Does NOT push to Meta — call finalize for that. Returns the asset record including a public blob_url and, when a feed-size and story-size variant match, an auto-assigned group_id — matched by filename (placement markers like feed/story/1x1/9x16/1080x1920 are ignored when comparing) or by visual similarity (perceptual hash).
| Parameter | Type | Required | Description |
|---|---|---|---|
| file_data | string | required | Base64-encoded file contents |
| filename | string | required | Original filename (used for grouping) |
| mime_type | string | required | image/jpeg, image/jpg, image/png, video/mp4, or video/quicktime |
| ad_account_id | string | required | Ad account this asset will be finalized to |
curl -X POST https://api.xylomcp.com/v1/media/upload \
-H "x-api-key: xy_sk_your_key" \
-H "Content-Type: application/json" \
-d '{
"file_data": "iVBORw0KGgoAAAANSUhEUgAA...",
"filename": "hero-square.jpg",
"mime_type": "image/jpeg",
"ad_account_id": "act_123456789"
}'{
"data": {
"id": "asset_01HX9...",
"ad_account_id": "act_123456789",
"filename": "hero-square.jpg",
"asset_type": "image",
"meta_ref": null,
"blob_url": "https://blob.vercel-storage.com/media/...",
"aspect_ratio": "1:1",
"group_id": "grp_01HX9...",
"group_name": "hero",
"expires_at": "2026-04-20T12:00:00.000Z"
}
}Media — Finalize to Meta
/v1/media/finalizePush staged assets to Meta. If asset_ids is omitted, every unfinalized asset for the ad account is finalized. Returns per-asset results; the resulting meta_ref is the image_hash for images and the video_id for videos.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ad_account_id | string | required | Ad account to finalize to |
| asset_ids | string[] | optional | Specific asset IDs to finalize (default: all unfinalized for this account) |
curl -X POST https://api.xylomcp.com/v1/media/finalize \
-H "x-api-key: xy_sk_your_key" \
-H "Content-Type: application/json" \
-d '{
"ad_account_id": "act_123456789",
"asset_ids": ["asset_01HX9..."]
}'{
"data": {
"finalized": 1,
"failed": 0,
"total": 1,
"results": [
{
"id": "asset_01HX9...",
"meta_ref": "a1b2c3d4e5f6...",
"status": "success"
}
]
}
}Media — List
/v1/mediaList staged and finalized assets for an ad account.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ad_account_id | string | required | Ad account ID |
| group_id | string | optional | Filter to a single media group |
curl "https://api.xylomcp.com/v1/media?ad_account_id=act_123456789" \ -H "x-api-key: xy_sk_your_key"
Media — Preview
/v1/media/preview/:idPublic proxy that streams the staged asset through api.xylomcp.com. Use this URL whenever you need an AI/vision model to view the file — the raw Vercel Blob domain is blocked by many AI products.
curl https://api.xylomcp.com/v1/media/preview/asset_01HX9...
Media — Rename Group
/v1/media/groups/:groupIdRename every asset in a media group.
| Parameter | Type | Required | Description |
|---|---|---|---|
| group_name | string | required | New name for the group |
curl -X PATCH https://api.xylomcp.com/v1/media/groups/grp_01HX9... \
-H "x-api-key: xy_sk_your_key" \
-H "Content-Type: application/json" \
-d '{ "group_name": "Spring Launch Hero" }'Media — Remove from Group
/v1/media/groups/:groupId/remove/:assetIdDetach a single asset from its group (sets group_id and group_name to null).
curl -X POST https://api.xylomcp.com/v1/media/groups/grp_01HX9.../remove/asset_01HX9... \ -H "x-api-key: xy_sk_your_key"
Media — Merge Groups
/v1/media/groups/mergeMerge multiple assets into a single group. Useful when auto-grouping puts related creatives in separate groups.
| Parameter | Type | Required | Description |
|---|---|---|---|
| asset_ids | string[] | required | Asset IDs to merge (minimum 2) |
curl -X POST https://api.xylomcp.com/v1/media/groups/merge \
-H "x-api-key: xy_sk_your_key" \
-H "Content-Type: application/json" \
-d '{ "asset_ids": ["asset_01...", "asset_02...", "asset_03..."] }'{
"data": {
"group_id": "grp_new...",
"group_name": "hero",
"asset_ids": ["asset_01...", "asset_02...", "asset_03..."]
}
}Media — Delete
/v1/media/:idDelete a media asset record and its blob.
curl -X DELETE https://api.xylomcp.com/v1/media/asset_01HX9... \ -H "x-api-key: xy_sk_your_key"
Pages
Pages — List
/v1/pagesList Facebook Pages the user manages. This endpoint does NOT require the x-ad-account header.
curl https://api.xylomcp.com/v1/pages \ -H "x-api-key: xy_sk_your_key"
{
"data": [
{
"id": "112233445566778",
"name": "Acme Corp",
"category": "E-commerce website"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Pages — List Posts
/v1/pages/:id/postsList recent posts for a Page. Each post includes engagement counts (likes, reactions, comments, shares).
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results (default 25, max 100) |
curl "https://api.xylomcp.com/v1/pages/112233445566778/posts?limit=10" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "112233445566778_123456789",
"message": "Check out our latest products!",
"created_time": "2026-03-20T14:30:00+0000",
"type": "photo",
"likes": 142,
"reactions": 168,
"comments": 23,
"shares": 7
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Pages — Insights
/v1/pages/:id/insightsGet Page performance insights.
| Parameter | Type | Required | Description |
|---|---|---|---|
| metric | string | optional | Comma-separated metrics (e.g. page_impressions, page_engaged_users) |
| period | string | optional | day, week, or month |
| date_from | string | optional | Start date YYYY-MM-DD |
| date_to | string | optional | End date YYYY-MM-DD |
curl "https://api.xylomcp.com/v1/pages/112233445566778/insights?metric=page_impressions&period=day&date_from=2026-03-01&date_to=2026-03-15" \ -H "x-api-key: xy_sk_your_key"
{
"data": [
{
"metric": "page_impressions",
"period": "day",
"values": [
{ "value": 1250, "end_time": "2026-03-01T08:00:00+0000" },
{ "value": 1480, "end_time": "2026-03-02T08:00:00+0000" }
]
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Pages — Create Post
/v1/pages/:pageId/postsPublish a post to a Page. At least one of message, link, or image_url is required.
| Parameter | Type | Required | Description |
|---|---|---|---|
| message | string | optional | Post text |
| link | string | optional | URL to share |
| image_url | string | optional | Public image URL to attach |
| published | boolean | optional | If false, creates an unpublished draft |
curl -X POST https://api.xylomcp.com/v1/pages/112233445566778/posts \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"message": "New arrivals just landed!",
"link": "https://example.com/new"
}'{
"data": { "id": "112233445566778_999888777" },
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Pages — Schedule Post
/v1/pages/:pageId/posts/scheduleSchedule a post for future publishing. scheduled_publish_time must be at least 10 minutes in the future and within 30 days.
| Parameter | Type | Required | Description |
|---|---|---|---|
| scheduled_publish_time | string | required | ISO 8601 — between now+10min and now+30d |
| message | string | optional | Post text (one of message/link/image_url required) |
| link | string | optional | URL to share |
| image_url | string | optional | Public image URL to attach |
curl -X POST https://api.xylomcp.com/v1/pages/112233445566778/posts/schedule \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"message": "Flash sale tomorrow!",
"scheduled_publish_time": "2026-04-21T14:00:00Z"
}'Pages — List Scheduled Posts
/v1/pages/:pageId/scheduled-postsList posts queued for future publishing.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results (default 25, max 100) |
curl https://api.xylomcp.com/v1/pages/112233445566778/scheduled-posts \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Pages — Update Post
/v1/pages/posts/:postIdEdit the message on an existing post. page_id must be provided in the body so Xylo can resolve the correct page token.
| Parameter | Type | Required | Description |
|---|---|---|---|
| message | string | required | New post text |
| page_id | string | required | ID of the Page that owns the post |
curl -X PATCH https://api.xylomcp.com/v1/pages/posts/112233445566778_999888777 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "page_id": "112233445566778", "message": "Updated caption" }'Pages — Delete Post
/v1/pages/posts/:postIdDelete a post. Pass page_id in the body.
| Parameter | Type | Required | Description |
|---|---|---|---|
| page_id | string | required | ID of the Page that owns the post |
curl -X DELETE https://api.xylomcp.com/v1/pages/posts/112233445566778_999888777 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "page_id": "112233445566778" }'Pages — List Comments
/v1/pages/posts/:postId/commentsList comments on a Page post.
| Parameter | Type | Required | Description |
|---|---|---|---|
| page_id | string | required | ID of the Page that owns the post (query param) |
| limit | integer | optional | Max results (default 25, max 100) |
curl "https://api.xylomcp.com/v1/pages/posts/112233445566778_999888777/comments?page_id=112233445566778" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Pages — Reply to Comment
/v1/pages/comments/:commentId/replyPost a public reply to a comment.
| Parameter | Type | Required | Description |
|---|---|---|---|
| message | string | required | Reply text |
| page_id | string | required | ID of the Page that owns the comment |
curl -X POST https://api.xylomcp.com/v1/pages/comments/555444333_111/reply \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "page_id": "112233445566778", "message": "Thanks for the feedback!" }'Pages — Delete Comment
/v1/pages/comments/:commentIdDelete a comment.
| Parameter | Type | Required | Description |
|---|---|---|---|
| page_id | string | required | ID of the Page that owns the comment |
curl -X DELETE https://api.xylomcp.com/v1/pages/comments/555444333_111 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "page_id": "112233445566778" }'Pages — Latest Post
/v1/pages/latest-postOne-hop resolver: returns the most recent post for a Page and (by default) its comments. If page_id is omitted, falls back to the ad account's configured default_page_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
| page_id | string | optional | Page ID (optional — uses default if configured) |
| include_comments | boolean | optional | Include comments on the latest post (default true) |
| comments_limit | integer | optional | Max comments to return (default 25, max 100) |
curl "https://api.xylomcp.com/v1/pages/latest-post?page_id=112233445566778" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — List Accounts
/v1/instagram/accountsList connected Instagram Business accounts. This endpoint does NOT require the x-ad-account header.
curl https://api.xylomcp.com/v1/instagram/accounts \ -H "x-api-key: xy_sk_your_key"
{
"data": [
{
"id": "17841400987654321",
"username": "acme_store",
"name": "Acme Store",
"followers_count": 10000,
"media_count": 250
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Instagram — Connected Accounts
/v1/instagram/connected-accountsIG accounts specifically connected to the current ad account (i.e. usable for ad creation). This is different from /v1/instagram/accounts, which lists every IG account the user has access to across their Pages.
curl https://api.xylomcp.com/v1/instagram/connected-accounts \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — Account Insights
/v1/instagram/:id/insightsGet Instagram account insights.
| Parameter | Type | Required | Description |
|---|---|---|---|
| metric | string | optional | Comma-separated metrics (e.g. impressions, reach, follower_count) |
| period | string | optional | day, week, or month |
curl "https://api.xylomcp.com/v1/instagram/17841400987654321/insights?metric=impressions,reach&period=day" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"metric": "impressions",
"period": "day",
"values": [
{ "value": 8540, "end_time": "2026-03-20T08:00:00+0000" },
{ "value": 9120, "end_time": "2026-03-21T08:00:00+0000" }
]
},
{
"metric": "reach",
"period": "day",
"values": [
{ "value": 6230, "end_time": "2026-03-20T08:00:00+0000" },
{ "value": 6780, "end_time": "2026-03-21T08:00:00+0000" }
]
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Instagram — Publish Media
/v1/instagram/:igAccountId/publishPublish a single IMAGE, VIDEO, REEL, or STORY to an Instagram Business account.
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | required | IMAGE, VIDEO, REEL, or STORY |
| image_url | string | optional | Public image URL (required for IMAGE) |
| video_url | string | optional | Public video URL (required for VIDEO, REEL) |
| caption | string | optional | Caption text (max 2200 chars, 30 hashtags) |
| alt_text | string | optional | Accessibility alt text (images only) |
| user_tags | object[] | optional | Array of user tag objects to tag accounts in the media |
curl -X POST https://api.xylomcp.com/v1/instagram/17841400987654321/publish \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"type": "IMAGE",
"image_url": "https://example.com/hero.jpg",
"caption": "Spring drop just landed. #newarrivals"
}'Instagram — Publish Carousel
/v1/instagram/:igAccountId/publish/carouselPublish a multi-item carousel (2–10 items). Each item is either an image or video.
| Parameter | Type | Required | Description |
|---|---|---|---|
| items | object[] | required | Array of 2–10 items; each must have image_url OR video_url |
| caption | string | optional | Caption for the carousel |
curl -X POST https://api.xylomcp.com/v1/instagram/17841400987654321/publish/carousel \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{
"caption": "Our top 3 looks this week",
"items": [
{ "image_url": "https://example.com/1.jpg" },
{ "image_url": "https://example.com/2.jpg" },
{ "image_url": "https://example.com/3.jpg" }
]
}'Instagram — Publishing Limit
/v1/instagram/:igAccountId/publishing-limitCheck the account's current publishing quota. Instagram caps container-based publishing at 50 posts per rolling 24h.
curl https://api.xylomcp.com/v1/instagram/17841400987654321/publishing-limit \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — List Media
/v1/instagram/:igAccountId/mediaList recent media for an IG Business account.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results |
curl "https://api.xylomcp.com/v1/instagram/17841400987654321/media?limit=10" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — Get Media
/v1/instagram/media/:mediaIdFetch a single media object by ID.
curl https://api.xylomcp.com/v1/instagram/media/18023456789012345 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — Media Insights
/v1/instagram/media/:mediaId/insightsPer-post insights (impressions, reach, engagement, saves, etc.).
curl https://api.xylomcp.com/v1/instagram/media/18023456789012345/insights \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — List Comments
/v1/instagram/media/:mediaId/commentsList comments on an IG media object.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results (default 25, max 100) |
curl https://api.xylomcp.com/v1/instagram/media/18023456789012345/comments \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — Reply to Comment
/v1/instagram/comments/:commentId/replyReply publicly to an IG comment.
| Parameter | Type | Required | Description |
|---|---|---|---|
| message | string | required | Reply text |
curl -X POST https://api.xylomcp.com/v1/instagram/comments/17895555000/reply \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "message": "Thanks! DM us for details 🙌" }'Instagram — Hide / Unhide Comment
/v1/instagram/comments/:commentIdHide or unhide a comment. Hidden comments remain visible to the author and their friends but not to the wider audience.
| Parameter | Type | Required | Description |
|---|---|---|---|
| hide | boolean | required | true to hide, false to unhide |
curl -X PATCH https://api.xylomcp.com/v1/instagram/comments/17895555000 \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "hide": true }'Instagram — Delete Comment
/v1/instagram/comments/:commentIdPermanently delete a comment.
curl -X DELETE https://api.xylomcp.com/v1/instagram/comments/17895555000 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Instagram — Latest Post
/v1/instagram/latest-postOne-hop resolver: returns the most recent media object and (by default) its comments. Falls back to the ad account's configured default_instagram_id if ig_account_id is omitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ig_account_id | string | optional | IG Business account ID (optional — uses default if configured) |
| include_comments | boolean | optional | Include comments on the latest post (default true) |
| comments_limit | integer | optional | Max comments (default 25, max 100) |
curl "https://api.xylomcp.com/v1/instagram/latest-post?ig_account_id=17841400987654321" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Leads
Leads — List Forms
/v1/leads/formsList lead generation forms for connected Pages. Requires the x-ad-account header.
curl https://api.xylomcp.com/v1/leads/forms \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "1234567890",
"name": "Summer Sale Lead Form",
"status": "ACTIVE",
"leads_count": 50,
"created_time": "2026-03-01T10:00:00+0000"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Leads — Get Form Leads
/v1/leads/forms/:id/leadsGet lead submissions from a specific form.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results (default 25) |
| after | string | optional | Pagination cursor from paging.next_cursor |
curl "https://api.xylomcp.com/v1/leads/forms/1234567890/leads?limit=10" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "9876543210",
"created_time": "2026-03-20T09:15:00+0000",
"field_data": [
{ "name": "email", "values": ["user@example.com"] },
{ "name": "full_name", "values": ["Alex Smith"] }
]
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" },
"paging": { "next_cursor": "xyz789", "has_more": true }
}Catalogs
Catalogs — List
/v1/catalogsList product catalogs. Requires the x-ad-account header.
curl https://api.xylomcp.com/v1/catalogs \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "1122334455",
"name": "Main Product Catalog",
"product_count": 250,
"created_time": "2026-01-15T12:00:00+0000"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Catalogs — List Products
/v1/catalogs/:id/productsList products in a catalog, or look up specific products by retailer_id — the merchant's feed/SKU ID, which is also what the insights product_id breakdown reports. Pull insights with breakdowns=product_id, then resolve the winning rows here to see which products drive spend and purchases.
| Parameter | Type | Required | Description |
|---|---|---|---|
| retailer_ids | string | optional | Comma-separated retailer IDs (feed/SKU IDs) to look up, max 100. When set, limit defaults to 100. |
| limit | integer | optional | Max results (default 25) |
| after | string | optional | Pagination cursor from paging.next_cursor |
curl "https://api.xylomcp.com/v1/catalogs/1122334455/products?retailer_ids=45015356375236" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
{
"data": [
{
"id": "prod_001",
"retailer_id": "45015356375236",
"name": "Classic T-Shirt",
"price": "29.99",
"currency": "USD",
"availability": "in stock",
"image_url": "https://example.com/tshirt.jpg"
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" },
"paging": { "next_cursor": "def456", "has_more": true }
}Google Ads
Google Ads endpoints require the x-google-customer-id header (your Google Ads customer ID) in addition to the standard x-api-key header. All endpoints are under the /v1/google/ prefix.
Campaigns
/v1/google/campaigns— List all campaigns/v1/google/campaigns/:id— Get campaign details/v1/google/campaigns— Create campaign (SEARCH, DISPLAY, SHOPPING, PERFORMANCE_MAX, VIDEO)/v1/google/campaigns/:id— Update campaign/v1/google/campaigns/:id— Delete campaignAd Groups
/v1/google/campaigns/:campaign_id/ad-groups— List ad groups in campaign/v1/google/ad-groups/:id— Get ad group details/v1/google/campaigns/:campaign_id/ad-groups— Create ad group/v1/google/ad-groups/:id— Update ad group/v1/google/ad-groups/:id— Delete ad groupAds
/v1/google/ad-groups/:ad_group_id/ads— List ads in ad group/v1/google/ad-groups/:ad_group_id/ads— Create responsive search/display ad/v1/google/ad-groups/:ad_group_id/ads/:id— Update ad/v1/google/ad-groups/:ad_group_id/ads/:id— Delete adKeywords
/v1/google/ad-groups/:ad_group_id/keywords— List keywords/v1/google/ad-groups/:ad_group_id/keywords— Add keywords (bulk support via keywords array)/v1/google/ad-groups/:ad_group_id/keywords/:id— Update keyword/v1/google/ad-groups/:ad_group_id/keywords/:id— Remove keywordInsights
/v1/google/insights— Performance metrics (level: campaign | ad_group | ad)Audiences
/v1/google/audiences— List remarketing audiences/v1/google/audiences— Create audience/v1/google/audiences/:id— Delete audienceConversions
/v1/google/conversion-actions— List conversion actions/v1/google/conversion-actions— Create conversion action/v1/google/conversions/upload— Upload offline click conversionsAssets
/v1/google/assets— List assets (sitelinks, callouts, structured snippets, images)/v1/google/assets— Create asset/v1/google/campaigns/:id/assets— Link asset to campaignSearch Terms
/v1/google/search-terms— Search term performance reportNegative Keywords
/v1/google/campaigns/:id/negative-keywords— List campaign negative keywords/v1/google/campaigns/:id/negative-keywords— Add negative keywords/v1/google/negative-keyword-lists— List shared negative keyword lists/v1/google/negative-keyword-lists— Create shared listBudgets
/v1/google/budgets— List budgets/v1/google/budgets— Create budget/v1/google/budgets/:id— Update budgetAsset Groups (Performance Max)
/v1/google/asset-groups— List asset groups/v1/google/asset-groups— Create asset group for PMax campaign/v1/google/asset-groups/:id/assets— Link asset to groupExperiments
/v1/google/experiments— List A/B experiments/v1/google/experiments— Create experiment/v1/google/experiments/:id/promote— Promote winning experiment/v1/google/experiments/:id/end— End experimentRecommendations & Reporting
/v1/google/recommendations— List optimization recommendations/v1/google/recommendations/:id/apply— Apply recommendation/v1/google/change-history— Account change history/v1/google/quality-scores— Keyword quality scoresTikTok Ads
TikTok endpoints require the x-tiktok-advertiser-id header in addition to the standard x-api-key header. All endpoints are under the /v1/tiktok/ prefix.
Campaigns
/v1/tiktok/campaigns— List campaigns/v1/tiktok/campaigns— Create campaign (objectives: REACH, TRAFFIC, VIDEO_VIEWS, CONVERSIONS, etc.)/v1/tiktok/campaigns/:id— Update campaign/v1/tiktok/campaigns/:id— Delete campaignAd Groups
/v1/tiktok/campaigns/:campaign_id/ad-groups— List ad groups/v1/tiktok/campaigns/:campaign_id/ad-groups— Create ad group/v1/tiktok/ad-groups/:id— Update ad group/v1/tiktok/ad-groups/:id— Delete ad groupAds
/v1/tiktok/ad-groups/:ad_group_id/ads— List ads/v1/tiktok/ad-groups/:ad_group_id/ads— Create ad/v1/tiktok/ads/:id— Update ad/v1/tiktok/ads/:id— Delete adInsights
/v1/tiktok/insights— Performance metrics (level: campaign | ad_group | ad)Audiences
/v1/tiktok/audiences— List audiences/v1/tiktok/audiences— Create audience/v1/tiktok/audiences/:id— Delete audienceTikTok Shop
TikTok Shop is Xylo's commerce integration for analytics, product listing optimization, creator discovery, affiliate collaborations, sample settings, and creator text messages. REST routes live under /v1/tiktok-shop/ and use the x-tiktok-shop-id header. AI agents use the same approved surface through the commerce dispatcher. Listing edits, collaboration invitations, and outbound messages require explicit confirmation.
Analytics
/v1/tiktok-shop/analytics/shop— Shop GMV, sales, orders, and trends/v1/tiktok-shop/analytics/shop/products— Product performance/v1/tiktok-shop/analytics/shop/skus— SKU performance/v1/tiktok-shop/analytics/shop/videos— Video performance/v1/tiktok-shop/analytics/shop/lives/overview— LIVE performanceProduct listings
/v1/tiktok-shop/products— Search product listings/v1/tiktok-shop/products/:id— Read one listing and its review versions/v1/tiktok-shop/products/diagnose-optimize— Diagnose listing quality/v1/tiktok-shop/products/:id/partial-edit— Submit a confirmed partial listing editCreator discovery
/v1/tiktok-shop/affiliate/creators/search— Search Creator Marketplace/v1/tiktok-shop/affiliate/creators/:creatorOpenId/performance— Read Marketplace-wide trailing-30-day creator contextAffiliate collaborations
/v1/tiktok-shop/affiliate/open-plans— List open collaborations/v1/tiktok-shop/affiliate/targeted-plans— List target collaborations/v1/tiktok-shop/affiliate/target-collaborations— Create a confirmed target collaboration with sample settingsCreator messages
/v1/tiktok-shop/affiliate/conversations— List creator conversations/v1/tiktok-shop/affiliate/conversations/:conversationId/messages— Read a conversation/v1/tiktok-shop/affiliate/conversations/:conversationId/messages— Send a confirmed text messageX Ads
X Ads endpoints require the x-x-ads-account-id header in addition to x-api-key. The REST surface lives under /v1/x/. AI agents can access the same 111 operations through the x_ads MCP tool; see the complete generated route reference for every action and schema.
Campaigns & ad groups
/v1/x/campaigns— List campaigns/v1/x/campaigns— Create a campaign/v1/x/campaigns/:id— Update status, budget, dates, or campaign settings/v1/x/campaigns/batch— Create or update campaigns in a batch/v1/x/line-items— List ad groups/v1/x/line-items— Create an ad group/v1/x/line-items/:id— Update an ad group/v1/x/line-items/batch— Create or update ad groups in a batchAds & creative
/v1/x/promoted-tweets— List promoted tweets/v1/x/promoted-tweets— Create a promoted tweet/v1/x/tweets— Create a nullcast tweet for advertising/v1/x/cards— Create a unified website or app card/v1/x/media/upload— Upload image or video media from a URL or base64 payload/v1/x/media-library— List reusable account mediaTargeting
/v1/x/targeting-options/:type— Search locations, interests, events, devices, conversations, and other targeting types/v1/x/targeting-suggestions— Get account-specific targeting suggestions/v1/x/audience-estimate— Estimate reach before launch/v1/x/targeting-criteria— Add targeting criteria to an ad group/v1/x/targeting-criteria/:id— Remove targeting criteriaAudiences
/v1/x/audiences— List custom audiences/v1/x/audiences— Create a custom audience/v1/x/audiences/:id/users— Add locally hashed CRM identifiers/v1/x/do-not-reach-lists— List do-not-reach exclusions/v1/x/do-not-reach-lists/:id/users— Add users to an exclusion listAnalytics
/v1/x/insights— Synchronous performance analytics/v1/x/insights/jobs— Start a detailed asynchronous analytics job/v1/x/insights/jobs— Read or download asynchronous analytics results/v1/x/insights/active-entities— Find entities with delivery in a period/v1/x/account/history— Read account change historyMeasurement
/v1/x/tracking-tags— List website tracking tags/v1/x/tracking-tags— Create a website tracking tag/v1/x/web-conversions/:pixel_id— Send server-side web conversion events/v1/x/conversion-event— Send a mobile conversion event/v1/x/conversion-attribution— Read conversion-attribution settingsCatalogs & tests
/v1/x/product-catalogs— List product catalogs/v1/x/product-catalogs— Create a product catalog/v1/x/product-catalogs/:id/products— Bulk upsert catalog products/v1/x/product-catalogs/:id/scheduled-feeds— Create a scheduled catalog feed/v1/x/ab-tests— List A/B tests/v1/x/ab-tests— Create an A/B testKlaviyo
Klaviyo is Xylo's owned email and SMS channel. Its endpoints require the x-klaviyo-account header in addition to x-api-key. The REST surface lives under /v1/klaviyo/. AI agents access the same surface through the klaviyo MCP tool; see the complete generated route reference for every action and schema. Campaign sends always require a recipient estimate and explicit confirmation.
Accounts
/v1/klaviyo/accounts— List connected Klaviyo accounts/v1/klaviyo/account— Read live account name, timezone, currency, and contact details/v1/klaviyo/account/context— Read the saved account brief and memoryCampaigns & sending
/v1/klaviyo/campaigns— List email or SMS campaigns/v1/klaviyo/campaigns— Create a campaign draft/v1/klaviyo/campaigns/:id— Update campaign details/v1/klaviyo/campaigns/:id/clone— Clone a campaign as a draft/v1/klaviyo/campaigns/messages/:message_id— Update an email or SMS message definition/v1/klaviyo/campaigns/messages/:message_id/assign-template— Assign a template to a campaign message/v1/klaviyo/campaigns/:id/estimate-recipients— Run a live recipient estimate/v1/klaviyo/campaigns/:id/send— Send after the confirmation-gated preview/v1/klaviyo/campaigns/:id/send— Check campaign send statusFlows
/v1/klaviyo/flows— List flows/v1/klaviyo/flows/:id— Inspect one flow and its definition/v1/klaviyo/flows/:id/actions— List flow actions/v1/klaviyo/flow-actions/:id/messages— List messages for a flow action/v1/klaviyo/flows/:id/status— Pause or activate an existing flowProfiles & audiences
/v1/klaviyo/profiles— List or search profiles/v1/klaviyo/profiles— Create a profile/v1/klaviyo/profiles/:id— Update a profile/v1/klaviyo/lists— List lists/v1/klaviyo/lists— Create a list/v1/klaviyo/segments— List segments/v1/klaviyo/segments— Create a segment/v1/klaviyo/profiles/subscribe— Subscribe profiles with confirmation for bulk changes/v1/klaviyo/profiles/unsubscribe— Unsubscribe profiles with confirmation for bulk changesTemplates, offers & catalog
/v1/klaviyo/templates— Search or list templates/v1/klaviyo/templates— Create an HTML template/v1/klaviyo/templates/:id/render— Render a template for review/v1/klaviyo/images— List reusable images/v1/klaviyo/images— Upload an image from a URL/v1/klaviyo/coupons— List coupons and coupon codes/v1/klaviyo/coupons— Create a coupon/v1/klaviyo/catalogs/items— List catalog items/v1/klaviyo/tags— List tagsEvents, metrics & reports
/v1/klaviyo/events— List customer events/v1/klaviyo/metrics— List account metrics/v1/klaviyo/metrics/aggregate— Aggregate a metric over a time window/v1/klaviyo/reporting/campaigns— Campaign performance and attributed revenue/v1/klaviyo/reporting/flows— Flow performance and attributed revenue/v1/klaviyo/reporting/forms— Form performance/v1/klaviyo/reporting/segments— Segment performanceGoogle Analytics (GA4)
Google Analytics is Xylo's read-only site-measurement source: traffic, engagement, key events, and revenue as GA4 measures them on the website. Its endpoints require the x-ga4-property-id header (the numeric property id) in addition to x-api-key; only GET /v1/ga4/properties works without it. The REST surface lives under /v1/ga4/. AI agents use the ga4 MCP tool; see the complete generated route reference for every action and schema. Report responses return flattened rows, report bodies accept snake_case or Google's camelCase, and reports cache for 15 minutes (?refresh=truebypasses) to protect GA4's shared per-property quota.
Properties & config
/v1/ga4/properties— List connected GA4 properties (no property header needed)/v1/ga4/property— Property details: name, timezone, currency, data retention/v1/ga4/data-streams— List web and app data streams with measurement ids/v1/ga4/key-events— List key events (GA4 conversions)/v1/ga4/custom-dimensions— List custom dimensions/v1/ga4/custom-metrics— List custom metrics/v1/ga4/audiences— List GA4 audiences/v1/ga4/google-ads-links— List linked Google Ads accounts/v1/ga4/annotations— List reporting data annotationsReports
/v1/ga4/metadata— Every dimension and metric valid for the property, including custom definitions/v1/ga4/reports— Run a report: dimensions x metrics over date ranges, with filters, ordering, and cohorts/v1/ga4/reports/batch— Run up to 5 reports in one call/v1/ga4/reports/pivot— Run a pivot (cross-tab) report/v1/ga4/reports/realtime— Realtime report over the last 30 minutes (never cached)/v1/ga4/reports/funnel— Funnel report with step-by-step completion and abandonment/v1/ga4/reports/compatibility— Check which dimensions and metrics can join the report being composed/v1/ga4/insights— Normalized daily rows powering GA4 dashboard cards (account, channel, or source level)AI Features
AI-powered campaign analysis and optimization. These endpoints analyze performance data and return actionable recommendations.
Campaign Audit
/v1/ai/audit— Score campaigns 0-100 with findings and recommendations. Body: campaign_id(s), date_from, date_toMorpheus Structural Audit
/v1/ai/morpheus-auditDeep structural audit of an entire ad account. Goes beyond per-campaign scoring to evaluate account organization, keyword hygiene, retargeting overlap, billing structure, and audience strategy. Returns an overall 0–100 score with findings grouped by category and severity. Use this for quarterly-review-level audits; use /v1/ai/audit for lightweight per-campaign scoring.
| Parameter | Type | Required | Description |
|---|---|---|---|
| platform | string | required | meta or google |
| date_from | string | optional | ISO date (defaults to 30 days ago) |
| date_to | string | optional | ISO date (defaults to yesterday) |
curl -X POST https://api.xylomcp.com/v1/ai/morpheus-audit \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "platform": "meta" }'{
"data": {
"score": 72,
"platform": "meta",
"date_from": "2026-03-20",
"date_to": "2026-04-19",
"findings": [
{
"category": "account_organization",
"severity": "high",
"title": "Overlapping retargeting audiences across 3 ad sets",
"detail": "...",
"recommendation": "..."
}
]
},
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v25.0" }
}Budget Optimization
/v1/ai/optimize-budget— AI budget allocation using ROAS (40%), CPA (30%), CTR (30%) scoring. Body: campaign_ids, total_budget, date_from, date_toReport Generation
/v1/ai/report— Executive summary with key metrics, top/bottom performers, and recommendations. Body: campaign_ids, date_from, date_toCross-Platform Insights
/v1/insights/cross-platform— Unified metrics across Meta, Google, TikTok, and X with platform totals and grand totals. Query: platforms, date_from, date_to, levelReporting Dashboards
Live, block-based reporting dashboards composed from a declarative spec. Available on Brand and Agency plans (not Free). Each dashboard is bound to a set of connected accounts, holds KPI, time-series, bar, donut, table, and text blocks on a 12-column grid, and can be shared as a public, logged-out, unindexed link. Org-level, not ad-account scoped: these endpoints take no x-ad-account header.
List / Get
/v1/dashboards— List the org's dashboards (id, title, bound accounts, share status). Does not include the widget spec./v1/dashboards/:id— Get one dashboard, including its full widget spec and bound accounts.Metric Catalog
Grounds every widget in metrics the account actually has. Call this before binding any conversion metric, never guess an action name. Returns core metrics plus, per platform, Meta action types and custom conversions or Google conversion actions, along with the valid levels, breakdowns, and filters.
/v1/dashboards/metric-catalog— Query: platform (meta, google, or tiktok), account_id, refresh. Cached; a degraded upstream response is served but not cached.Create / Update
/v1/dashboards— Body: title, accounts ([{ platform, account_id, name? }], each an active connected account of the org), spec (optional, omit to create empty)./v1/dashboards/:id— Body: title?, accounts?, spec? (each replaces the existing value wholesale). The spec is validated server-side (@xylo/shared) on every write./v1/dashboards/:id— Permanently delete a dashboard.Run Data
/v1/dashboards/:id/data— Execute the spec's widget queries and return live data. Body: widget_ids? (omit to run every widget). Ungated: free to call, only editing is plan-gated.Creative Intelligence
A rolling 90-day per-account creative library: every creative the ad account ran in the last 90 days, deduped, scored against the account's own history, and tagged by AI, with nightly Meta archives for ad comments and primary-text/headline performance. Creative Intelligence is a per-account add-on billed on top of a Brand or Agency plan: $25/month per ad account on Brand, $15/month per ad account on Agency. It is not available on Free. Enable it per account in the dashboard under Creatives. The library fills on the next nightly sync and tags land on the run after that, because sync and tagging are separate nightly phases, so expect a populated, tagged tab within two days of enabling rather than immediately.
Account-scoped: every endpoint below takes the platform's account header (x-ad-account for Meta, x-tiktok-advertiser-id for TikTok). Through MCP the same capability is six actions on existing dispatchers, not new tools: creative({action:"library"|"digest"|"comments"|"copy"|"assets"}) and update({resource:"creative_tags"}).
The Creative Store
The store holds a rolling 90 daysof creative-day history and nothing older; a longer account history is not backfilled, and the 90-day cap on a query window is that horizon rather than a page size. A nightly cron walks each enabled ad account and writes one row per deduped creative plus a row per creative per day. Deduplication is by the creative's own media, so the same video running in six ad sets is one library row with six sibling ads, not six rows. Metrics come from the ad account's own insights, and a creative's results are counted on the optimized event of the ad set that served it.
Two consequences worth knowing before you divide anything. Windows end yesterday, because that is the last day the nightly sync could have filled; including today would report a hole as a decline. And a creative that ran under two ad sets optimizing for two different events sums two different events, so its cost per result has no nameable unit and is suppressed rather than guessed. A result of null means unverifiable, never zero.
Scores are percentiles across the whole account, computed before any filter is applied, so "top decile hook rate" means top decile in this account rather than against a generic benchmark. A weekly pass runs deep creative analysis on a standing set: the account's winners plus up to five notable losers.
Library / Detail
/v1/creative-store/creatives— List the account's creatives. Query: date_from, date_to (default the 30 days ending yesterday, max 90-day span), status (active | paused | all), min_spend, tag_filters (JSON object of dimension to value), group_by (any taxonomy dimension), sort, dir, limit (default 50, max 200), offset, detail (lean | full).group_by returns spend-weighted rollups per tag value instead of rows. Group on a categorical dimension (hook tactic, visual format, messaging angle, offer type, awareness level, Life Force 8, mindstate, and the like); the four timing dimensions hold raw seconds, so grouping on one produces a bucket per distinct value rather than a useful rollup. A creative on a multi-valued dimension counts in every group it belongs to, so group spend can exceed account spend; untagged creatives stay visible in an untagged bucket rather than vanishing from the report.
/v1/creative-store/creatives/:media_key— One creative in depth: day-by-day rows, window-summed quartile retention (null for a static image), the full tag set with overrides applied, the sibling ads with their statuses, and the stored deep analysis when one exists./v1/creative-store/creatives/:media_key/comments— Stored Facebook and Instagram ad comments and replies across every sibling ad using this creative, with verified advertiser replies, freshness, filters, and paging. Meta only./v1/creative-store/creatives/:media_key/copy— Stored daily primary-text and headline performance across every sibling ad using this creative. Identical wording is combined; query date_from/date_to within the rolling 90-day window. Meta only./v1/creative-store/creatives/:media_key/assets— Live per-asset and Advantage+ enhancement performance for one multi-asset creative. Meta only./v1/creative-store/teaser— The pre-purchase preview for an account whose plan supports Creative Intelligence but which has not enabled the add-on. Live 30-day pull folded to creative grain, no tags, no scores, nothing added to your library. Meta only.curl "https://api.xylomcp.com/v1/creative-store/creatives?group_by=hook_tactic&min_spend=100" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_123456789"
Gating is tri-state
- Plan has no Creative Intelligence:
403 FEATURE_NOT_AVAILABLE. - Plan supports it, the account has not enabled it: reads return
200 { data: { available: false, reason: "ADDON_NOT_ENABLED", teaser_available } }. That is a successful call, not an error. An agent should offer to enable the add-on rather than retry. - Add-on enabled:
data.available: trueand the full payload.
Weekly Digest
/v1/creative-store/digest— One call, one week: period-over-period movers, top and bottom creatives on the optimized event, new launches, and fatigue candidates. Query: period_days (1 to 90, default 7); the period ends yesterday and is compared against the equal period before it.Every list in the response is capped at 10 entries, which keeps the payload small enough for a scheduled agent run. The dashboard's Creatives tab header reads this exact endpoint, so an agent and the tab cannot tell two different stories about the same week. See the Claude automation guide for the Monday-report recipe.
Tag Overrides
/v1/creative-store/tags— Correct a creative's tags. Body: media_key, overrides (dimension to corrected value; null clears that dimension's override and falls back to the AI value). Writes only the override field, never the model's own tags.Unlike the reads, the write is refused with 403 ADDON_NOT_ENABLED when the add-on is off, and it needs a write grant on the account. Values are validated against the same dimension table the tagging model answers into, so an editor cannot submit a value the API rejects.
curl -X PATCH https://api.xylomcp.com/v1/creative-store/tags \
-H "x-api-key: xy_sk_your_key" \
-H "x-ad-account: act_123456789" \
-H "Content-Type: application/json" \
-d '{ "media_key": "video:120345678901234", "overrides": { "hook_tactic": "bold_claim" } }'Platform Coverage
Meta is deep and TikTok follows. Both platforms sync, tag, score, and support pausing or resuming an ad from the Creatives tab. The differences are real and worth stating rather than glossing:
| Capability | Meta | TikTok | X |
|---|---|---|---|
| Nightly creative sync | Yes | Yes | Not covered |
| AI taxonomy tagging | Yes | Yes, except Spark ads | Not covered |
| Account-relative scores and digest | Yes | Yes | Not covered |
| Stored ad comments and copy performance | Yes | No | Not covered |
| Pre-purchase teaser preview | Yes | No | Not covered |
| Pause / resume from the Creatives tab | Yes, each ad reported on its own | Yes, but one failed ad can affect others in the same batch | Not covered |
| Hook rate measured on | 3-second video plays | 2-second views | Not covered |
A TikTok Spark ad's creative lives in a TikTok post whose bytes Xylo cannot resolve, so a Spark ad is stored as its own row, is not deduped across ad groups, and carries no tags. Because Meta's hook rate counts 3-second plays and TikTok's counts 2-second views, the two numbers are not interchangeable and no report should present them as one metric. X Ads has no creative store, no tagging, and no digest.
Teams
Team management with role-based access control. Roles: owner, admin, member, viewer. Owners and admins manage everything, members are scoped to the ad accounts you grant them, and viewers read every account in the organization without being able to change anything.
Members
/v1/team/members— List team members with roles/v1/team/invite— Invite user by email (admin/owner only)/v1/team/members/:id— Update member role/v1/team/members/:id— Remove memberInvites
/v1/team/invites— List pending invites/v1/team/invites/:id— Cancel inviteWebhooks
Subscribe to campaign.status_changed, campaign.budget_updated, and live notification.created events. Max 20 subscriptions per organization. URLs are validated with SSRF protection. See the Claude notifications guide for signature verification and delivery recipes.
List Subscriptions
/v1/webhooks— List all webhook subscriptions/v1/webhooks/:id— Get subscription detailsCreate Subscription
/v1/webhooks— Create subscription. Body: url, events array, optional secret/v1/webhooks/:id— Update subscription/v1/webhooks/:id— Delete subscriptionBulk Operations
Execute multiple operations in a single request or batch campaign actions.
/v1/bulk/operations— Execute multiple API operations sequentially. Body: operations array of {method, path, body}/v1/bulk/pause-campaigns— Pause up to 100 campaigns at once/v1/bulk/resume-campaigns— Resume up to 100 campaigns at onceError Codes
All errors return an error object with code, message, and optional platform-specific error details (meta_error, google_error, or tiktok_error).
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY | No x-api-key header provided |
| 401 | INVALID_API_KEY | API key not found or revoked |
| 400 | MISSING_AD_ACCOUNT | No x-ad-account header on an endpoint that requires it |
| 403 | AD_ACCOUNT_NOT_CONNECTED | Ad account not connected to this organization |
| 403 | ACCOUNT_NOT_ACTIVE | Ad account is not activated on your plan |
| 401 | TOKEN_EXPIRED | Meta token expired, user must re-authorize via connect session |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Check X-RateLimit-* headers. |
| 400 | VALIDATION_ERROR | Request body or query params failed validation |
| 502 | META_API_ERROR | Meta's API returned an error (includes meta_error object) |
| 429 | META_RATE_LIMITED | Meta is rate-limiting the underlying ad account |
| 502 | GOOGLE_API_ERROR | Google Ads API returned an error (includes google_error object) |
| 429 | GOOGLE_RATE_LIMIT | Google Ads rate limit exceeded |
| 403 | GOOGLE_CUSTOMER_NOT_CONNECTED | Google customer ID not connected to organization |
| 502 | TIKTOK_API_ERROR | TikTok API returned an error (includes tiktok_error object) |
| 429 | TIKTOK_RATE_LIMIT | TikTok rate limit exceeded |
| 403 | TIKTOK_ADVERTISER_NOT_CONNECTED | TikTok advertiser not connected to organization |
| 404 | NOT_FOUND | The requested resource does not exist |
| 500 | INTERNAL_ERROR | Something went wrong on Xylo's end |
{
"error": {
"code": "META_API_ERROR",
"message": "Meta rejected the request.",
"meta_error": {
"message": "(#100) Daily budget is too low.",
"type": "OAuthException",
"code": 100
}
}
}Data Warehouse
Data warehousing is an optional add-on on the Brand and Agency plans, billed per account. An account with the add-on enabled gets a local Postgres mirror of its Meta ad state, async insights for heavy queries, and an ad integrity checker designed to run on a schedule. Every response that resolves from the local mirror includes a data_freshness field so agents can decide whether to trust the cached result or force a live pull with refresh=true.
Daily Scheduled Bulk Pulls
Once per day Xylo fetches the full state of each warehoused ad account — campaigns, ad sets, ads with creatives, and daily-granularity insights on a rolling 90-day window — and writes it to local snapshot tables. Read endpoints (list campaigns, list ads, read insights) answer from this snapshot when one is available, which makes warehoused reads dramatically faster and avoids Meta rate limits entirely. Insights for any date range within the 90-day window are aggregated from the daily rows at query time, so last_7d, last_14d, last_30d, last_60d, last_90d, yesterday, and explicit date_from/date_to all serve from the snapshot. Write endpoints always go straight to Meta.
The sync is incremental: each daily run only re-pulls the last 7 days of insights (to catch Meta's late-arriving conversion attribution) plus one older 30-day chunk if we haven't yet backfilled the full 90 days. A brand-new account reaches full 90-day depth after ~3 runs. Steady-state daily load is small enough to stay well under Meta's Business Use Case rate limits.
data_freshness Response Field
When an insights response is served from the local snapshot, the meta envelope includes a data_freshness field set to "local_snapshot" along with a snapshot_at timestamp. Live Meta calls (an account without the add-on, or a warehoused account with refresh=true) return "live".
{
"data": [ /* ... */ ],
"meta": {
"cached": false,
"data_freshness": "local_snapshot",
"snapshot_at": "2026-04-20T08:00:00Z",
"meta_api_version": "v25.0"
}
}Async Insights Jobs
Very large insights queries (long date ranges, ad-level breakdowns across thousands of ads) will time out if run synchronously. Warehoused accounts get two MCP routes for these: insights({ channel: "meta", lens: "job", mode: "job_create" }) kicks off a background job and returns a job_id; mode: "job_check" polls that job and returns results once complete.
Synchronous insights endpoints auto-route to async when the estimated row count exceeds ~15,000, and refuse outright above ~5,000,000 rows. Agents should poll roughly every 5–15 seconds until status is "completed" or "failed".
/v1/insights/async/start— Start a background insights job. Returns { job_id, status, estimated_rows, estimated_seconds }/v1/insights/async/:job_id— Poll job status. Running: { status, percent }. Complete: { status: 'complete', rows, actual_rows }.Ad Integrity Checker
The insights({ channel: "meta", lens: "ad_integrity" }) MCP route (and its REST counterpart GET /v1/ai/integrity-check) scans every active ad on the account and flags anomalies that usually indicate a hijacked creative, a wrong Page selection, or a misconfigured landing page. It is designed to be run once a day on a schedule.
What it flags
- Ads linking to a domain that is not the account's business website (and not on the approved list).
- Ads running from an unknown Facebook Page or Instagram account.
- Ads with no destination URL at all.
/v1/ai/integrity-check— Run the integrity scan. Returns { total_active_ads, anomalies_found, anomalies, reference }Business Website & Approved Domains
Each connected account has a business website field, set in the dashboard. The integrity checker uses this as the reference domain; subdomains of the business website are automatically accepted (e.g. setting example.com also accepts shop.example.com).
When a new domain shows up on an ad, the checker flags it. If the domain is legitimate (a checkout partner, a tracking redirect, a co-branded landing page), open the account's settings page in the dashboard and add it to the Approved domains list. Future scans will stop flagging ads that link to approved domains.
Agent Playbook
A comprehensive guide for AI agents managing Meta (Facebook & Instagram), Google, TikTok, and X advertising, plus Google Analytics (GA4) measurement, through Xylo. The playbooks cover account discovery, daily monitoring routines, campaign management actions, and safety rules to follow.
For the complete playbook, see llms-full.txt or agents.txt. For the full list of 300+ MCP ad operations, see mcp-tools.md.
Overview
When an AI agent first connects to a Xylo-managed ad account, it should perform a structured discovery sequence to understand the account state, then establish daily monitoring routines. All campaign modifications follow a safe creation pattern: create in a PAUSED state, confirm with the user, then activate. The safety rules below must always be respected to prevent accidental budget overspend or destructive actions.
Account Discovery
When onboarding a new ad account, run the following 6-step discovery sequence to build a complete picture of the account:
Step 1: List connected ad accounts
curl -X GET https://api.xylomcp.com/v1/accounts \ -H "x-api-key: YOUR_API_KEY"
Step 2: List active campaigns
curl -X GET "https://api.xylomcp.com/v1/campaigns?status=ACTIVE" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
Step 3: Pull account-level insights
curl -X GET "https://api.xylomcp.com/v1/insights?level=account&date_from=2026-03-18&date_to=2026-03-25" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
Step 4: List saved audiences
curl -X GET https://api.xylomcp.com/v1/audiences \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
Step 5: List pages and Instagram accounts
curl -X GET https://api.xylomcp.com/v1/pages \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456" curl -X GET https://api.xylomcp.com/v1/instagram/accounts \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
Step 6: List lead forms and catalogs
curl -X GET https://api.xylomcp.com/v1/leads/forms \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456" curl -X GET https://api.xylomcp.com/v1/catalogs \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
After discovery, save a memory structure like the following so the agent can reference it on subsequent interactions:
{
"account_id": "act_123456",
"account_name": "My Business",
"currency": "USD",
"timezone": "America/New_York",
"active_campaigns": 5,
"total_daily_budget": 250.00,
"pages": ["Page A (id: 111)", "Page B (id: 222)"],
"instagram_accounts": ["@mybusiness (id: 333)"],
"saved_audiences": 3,
"lead_forms": 2,
"catalogs": 1,
"last_7d_spend": 1450.00,
"last_7d_roas": 3.2,
"discovery_date": "2026-03-25"
}Daily Monitoring
Run this morning check sequence daily to surface anomalies and keep the user informed about account health.
curl -X GET "https://api.xylomcp.com/v1/insights?level=account&date_from=2026-03-24&date_to=2026-03-25" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
curl -X GET "https://api.xylomcp.com/v1/insights?level=campaign&date_from=2026-03-24&date_to=2026-03-25" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
curl -X GET "https://api.xylomcp.com/v1/insights?level=adset&date_from=2026-03-24&date_to=2026-03-25" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-ad-account: act_123456"
Compare results against the following alert thresholds:
| Metric | Warning | Critical |
|---|---|---|
| Cost per conversion | >25% above avg | >50% above avg |
| ROAS | <20% below target | <50% below target |
| CTR | <30% below avg | <50% below avg |
| Frequency (prospecting) | >2.5 | >3.5 |
Campaign Actions
Always follow the 4-step safe creation sequence when building new campaigns. Never create campaigns in an ACTIVE state directly.
Step 1: Create campaign in PAUSED state
curl -X POST https://api.xylomcp.com/v1/campaigns \
-H "x-api-key: YOUR_API_KEY" \
-H "x-ad-account: act_123456" \
-H "Content-Type: application/json" \
-d '{
"name": "Spring Sale - Conversions",
"objective": "OUTCOME_SALES",
"status": "PAUSED",
"special_ad_categories": []
}'Step 2: Create ad set under the campaign
curl -X POST https://api.xylomcp.com/v1/adsets \
-H "x-api-key: YOUR_API_KEY" \
-H "x-ad-account: act_123456" \
-H "Content-Type: application/json" \
-d '{
"campaign_id": "CAMPAIGN_ID",
"name": "Broad - 25-54 - Purchase",
"daily_budget": 5000,
"billing_event": "IMPRESSIONS",
"optimization_goal": "OFFSITE_CONVERSIONS",
"targeting": {
"age_min": 25,
"age_max": 54,
"geo_locations": { "countries": ["US"] }
},
"status": "PAUSED"
}'Step 3: Create ad within the ad set
curl -X POST https://api.xylomcp.com/v1/ads \
-H "x-api-key: YOUR_API_KEY" \
-H "x-ad-account: act_123456" \
-H "Content-Type: application/json" \
-d '{
"adset_id": "ADSET_ID",
"name": "Spring Sale - Image Ad",
"creative_id": "CREATIVE_ID",
"status": "PAUSED"
}'Step 4: Activate after user confirmation
curl -X PATCH https://api.xylomcp.com/v1/campaigns/CAMPAIGN_ID \
-H "x-api-key: YOUR_API_KEY" \
-H "x-ad-account: act_123456" \
-H "Content-Type: application/json" \
-d '{ "status": "ACTIVE" }'Scaling guidance:When scaling budgets, increase by 20-30% at a time. Wait 48-72 hours between increases to allow Meta's learning phase to stabilize. Never increase a budget by more than 50% in a single change without explicit user approval.
Safety Rules
Agents must follow these 10 rules at all times when managing ad accounts through the Xylo API:
Never set a campaign, ad set, or ad to ACTIVE without explicit user confirmation.
Never increase a budget by more than 50% in a single change without warning the user of the risk.
Always confirm with the user before deleting any campaign, ad set, ad, or audience.
Special Ad Categories (housing, credit, employment, politics) have legal requirements — always ask the user before setting these.
Alert the user if actual spend exceeds 125% of the intended daily budget (Meta can overspend by up to 25%).
Do not modify campaigns during high-spend hours (typically 10 AM – 2 PM in the account timezone) unless urgent.
When Meta returns an error, explain it in plain language and suggest a fix rather than retrying blindly.
Respect the learning phase — avoid editing ad sets that are in the learning phase unless performance is critically poor.
Handle ACCOUNT_NOT_ACTIVE errors gracefully by guiding the user to reconnect via a connect session.
When filtering ad sets by campaign, apply client-side filtering if the API does not support server-side filtering for that parameter.