Xylo API Documentation

Base URL: https://api.xyloapi.dev (production) or http://localhost:3001 (dev)

Quick Start for AI Agents

Xylo is a unified REST API for Meta (Facebook) Ads. It wraps the Meta Marketing API with these key simplifications:

  • Budgets in dollars — Meta uses cents internally; Xylo accepts and returns dollars (e.g. daily_budget: 350.00).
  • Flat insights — Meta returns conversions as nested actions arrays. Xylo flattens them to top-level conversions, cost_per_conversion, and roas fields.
  • Single-call ad creation — Pass image_url in 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 set status: "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
curl https://api.xyloapi.dev/v1/accounts \
  -H "x-api-key: xy_sk_your_key"

Step 2: Get campaigns for an account

curl
curl https://api.xyloapi.dev/v1/campaigns \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Step 3: Get performance insights

curl
curl "https://api.xyloapi.dev/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_10202667788488822"

Full Agent Workflow Example

A typical AI agent lifecycle: list accounts, pick one, read campaigns, check performance, create a new campaign.

Agent workflow
# 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_10202667788488822

# 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-15&date_to=2026-03-22

# 5. Create a new campaign (defaults to PAUSED)
POST /v1/campaigns
{ "name": "AI Agent Test", "objective": "OUTCOME_SALES", "daily_budget": 50.00 }

# 6. Create an ad set under the campaign
POST /v1/campaigns/:campaign_id/adsets
{ "name": "US Broad", "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": "Hero Image Ad", "creative": { "page_id": "541746272644756",
  "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.

ParameterTypeRequiredDescription
x-api-keystringrequiredYour Xylo API key (starts with xy_sk_)
x-ad-accountstringrequiredMeta ad account ID (e.g. act_10202667788488822). NOT required for GET /v1/accounts and POST /v1/connect-sessions.
Example request with both headers
curl https://api.xyloapi.dev/v1/campaigns \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

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.

POST/v1/connect-sessions
ParameterTypeRequiredDescription
redirect_urlstringoptionalURL to redirect user to after OAuth (e.g. https://yourapp.com/callback)
Request body
{
  "redirect_url": "https://yourapp.com/callback"
}
Response
{
  "data": {
    "connect_url": "https://xyloapi.dev/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": "v22.0"
  }
}

Paginated Success (2xx)

{
  "data": [ ... ],
  "meta": {
    "cached": false,
    "cache_age_seconds": null,
    "meta_api_version": "v22.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 TypeCache TTL
Account structure (campaigns, ad sets, ads)5 minutes
Performance metrics (insights)15 minutes
Ad creatives30 minutes
Account info1 hour
Force cache refresh
curl "https://api.xyloapi.dev/v1/campaigns?refresh=true" \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Rate Limits

Rate limits are per API key, measured in requests per minute (rpm). Every response includes rate limit headers.

PlanRate Limit
Free30 rpm
Starter120 rpm
Pro600 rpm
EnterpriseCustom

Response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

Accounts

List Accounts

GET/v1/accounts

Returns all connected Meta ad accounts. This endpoint does NOT require the x-ad-account header.

curl
curl https://api.xyloapi.dev/v1/accounts \
  -H "x-api-key: xy_sk_your_key"
Response
{
  "data": [
    {
      "id": "act_10202667788488822",
      "name": "GPCA",
      "currency": "USD",
      "timezone": "America/Los_Angeles",
      "status": "ACTIVE",
      "spend_cap": 0,
      "amount_spent": 463494.31,
      "connected_at": "2026-03-22T02:00:48.453+00:00"
    }
  ],
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Campaigns

Campaigns — List

GET/v1/campaigns

Returns all campaigns for the specified ad account. Supports filtering and pagination.

ParameterTypeRequiredDescription
statusstringoptionalFilter: ACTIVE, PAUSED, DELETED, ARCHIVED
limitintegeroptionalMax results (default 25, max 100)
afterstringoptionalPagination cursor from paging.next_cursor
refreshbooleanoptionalSet to true to bypass cache
curl
curl "https://api.xyloapi.dev/v1/campaigns?status=ACTIVE&limit=10" \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": [
    {
      "id": "6830045923554",
      "name": "MM - ADV+ Winners",
      "status": "ACTIVE",
      "objective": "OUTCOME_SALES",
      "daily_budget": 350.00,
      "lifetime_budget": null,
      "budget_remaining": 20.64,
      "spend_today": null,
      "start_time": "2025-11-03T00:00:00-0800",
      "stop_time": null,
      "created_time": "2025-11-02T16:37:23-0800",
      "updated_time": "2026-02-23T07:40:05-0800"
    }
  ],
  "meta": { "cached": true, "cache_age_seconds": 45, "meta_api_version": "v22.0" },
  "paging": { "next_cursor": "abc123", "has_more": true }
}

Campaigns — Read

GET/v1/campaigns/:id

Returns a single campaign by its Meta campaign ID. Same data shape as list.

curl
curl https://api.xyloapi.dev/v1/campaigns/6830045923554 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": {
    "id": "6830045923554",
    "name": "MM - ADV+ Winners",
    "status": "ACTIVE",
    "objective": "OUTCOME_SALES",
    "daily_budget": 350.00,
    "lifetime_budget": null,
    "budget_remaining": 20.64,
    "spend_today": null,
    "start_time": "2025-11-03T00:00:00-0800",
    "stop_time": null,
    "created_time": "2025-11-02T16:37:23-0800",
    "updated_time": "2026-02-23T07:40:05-0800"
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Campaigns — Create

POST/v1/campaigns

Creates a new campaign. Defaults to status: "PAUSED" as a safety measure. Budget values are in dollars.

ParameterTypeRequiredDescription
namestringrequiredCampaign name
objectivestringrequiredOUTCOME_SALES, OUTCOME_TRAFFIC, OUTCOME_AWARENESS, OUTCOME_LEADS, OUTCOME_ENGAGEMENT
statusstringoptionalACTIVE or PAUSED (default PAUSED)
daily_budgetnumberoptionalDaily budget in dollars (e.g. 50.00)
lifetime_budgetnumberoptionalLifetime budget in dollars
start_timestringoptionalISO 8601 start time
stop_timestringoptionalISO 8601 stop time
special_ad_categoriesarrayoptionalSpecial ad categories if applicable
buying_typestringoptionalBuying type (e.g. AUCTION)
curl
curl -X POST https://api.xyloapi.dev/v1/campaigns \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer Campaign",
    "objective": "OUTCOME_SALES",
    "daily_budget": 75.00
  }'
Response (201)
{
  "data": {
    "id": "6830045999001",
    "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-22T10:00:00-0700",
    "updated_time": "2026-03-22T10:00:00-0700"
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Campaigns — Update

PATCH/v1/campaigns/:id

Partial update. Only include the fields you want to change.

curl
curl -X PATCH https://api.xyloapi.dev/v1/campaigns/6830045999001 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{ "status": "ACTIVE", "daily_budget": 100.00 }'
Response
{
  "data": {
    "id": "6830045999001",
    "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-22T10:00:00-0700",
    "updated_time": "2026-03-22T12:00:00-0700"
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Campaigns — Delete

DELETE/v1/campaigns/:id

Sets the campaign status to DELETED in Meta. This is not a hard delete.

curl
curl -X DELETE https://api.xyloapi.dev/v1/campaigns/6830045999001 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Ad Sets

Ad Sets — List

GET/v1/campaigns/:campaign_id/adsets

Returns all ad sets within a campaign.

ParameterTypeRequiredDescription
limitintegeroptionalMax results (default 25, max 100)
afterstringoptionalPagination cursor from paging.next_cursor
refreshbooleanoptionalSet to true to bypass cache
curl
curl https://api.xyloapi.dev/v1/campaigns/6830045923554/adsets \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": [
    {
      "id": "6830045924954",
      "name": "ADV+",
      "status": "ACTIVE",
      "campaign_id": "6830045923554",
      "daily_budget": null,
      "lifetime_budget": null,
      "billing_event": "IMPRESSIONS",
      "optimization_goal": "VALUE",
      "bid_strategy": null,
      "targeting": {
        "age_min": 18,
        "age_max": 65,
        "geo_locations": {
          "countries": ["US"],
          "location_types": ["home", "recent"]
        }
      },
      "start_time": "2025-11-03T00:00:00-0800",
      "end_time": null,
      "created_time": "2025-11-02T16:37:24-0800",
      "updated_time": "2025-11-02T16:37:40-0800"
    }
  ],
  "meta": { "cached": true, "cache_age_seconds": 120, "meta_api_version": "v22.0" },
  "paging": { "next_cursor": null, "has_more": false }
}

Ad Sets — Read

GET/v1/adsets/:id

Returns a single ad set by ID. Same data shape as the list response.

curl
curl https://api.xyloapi.dev/v1/adsets/6830045924954 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": {
    "id": "6830045924954",
    "name": "ADV+",
    "status": "ACTIVE",
    "campaign_id": "6830045923554",
    "daily_budget": null,
    "lifetime_budget": null,
    "billing_event": "IMPRESSIONS",
    "optimization_goal": "VALUE",
    "bid_strategy": null,
    "targeting": {
      "age_min": 18,
      "age_max": 65,
      "geo_locations": {
        "countries": ["US"],
        "location_types": ["home", "recent"]
      }
    },
    "start_time": "2025-11-03T00:00:00-0800",
    "end_time": null,
    "created_time": "2025-11-02T16:37:24-0800",
    "updated_time": "2025-11-02T16:37:40-0800"
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Ad Sets — Create

POST/v1/campaigns/:campaign_id/adsets

Creates a new ad set under a campaign. Defaults to status: "PAUSED".

ParameterTypeRequiredDescription
namestringrequiredAd set name
billing_eventstringrequirede.g. IMPRESSIONS
optimization_goalstringrequirede.g. VALUE, CONVERSIONS, LINK_CLICKS, REACH
targetingobjectrequiredTargeting spec: { age_min, age_max, geo_locations: { countries: [...] } }
statusstringoptionalACTIVE or PAUSED (default PAUSED)
daily_budgetnumberoptionalDaily budget in dollars
lifetime_budgetnumberoptionalLifetime budget in dollars
bid_strategystringoptionalBid strategy
start_timestringoptionalISO 8601 start time
end_timestringoptionalISO 8601 end time
curl
curl -X POST https://api.xyloapi.dev/v1/campaigns/6830045923554/adsets \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "US - Broad - Value",
    "billing_event": "IMPRESSIONS",
    "optimization_goal": "VALUE",
    "targeting": {
      "age_min": 18,
      "age_max": 65,
      "geo_locations": { "countries": ["US"] }
    }
  }'

Ad Sets — Update

PATCH/v1/adsets/:id

Partial update. Only include the fields you want to change.

curl
curl -X PATCH https://api.xyloapi.dev/v1/adsets/6830045924954 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{ "daily_budget": 50.00, "status": "ACTIVE" }'

Ad Sets — Delete

DELETE/v1/adsets/:id

Deletes the ad set in Meta.

curl
curl -X DELETE https://api.xyloapi.dev/v1/adsets/6830045924954 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Ads

Ads — List

GET/v1/adsets/:adset_id/ads

Returns all ads within an ad set.

curl
curl https://api.xyloapi.dev/v1/adsets/6830045924954/ads \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": [
    {
      "id": "6830046371154",
      "name": "Catalog Ad",
      "status": "ACTIVE",
      "adset_id": "6830045924954",
      "creative": {
        "page_id": "541746272644756",
        "headline": "",
        "body": "",
        "link": "",
        "call_to_action": ""
      },
      "created_time": "2025-11-02T16:37:38-0800",
      "updated_time": "2026-01-26T05:25:56-0800"
    }
  ],
  "meta": { "cached": true, "cache_age_seconds": 60, "meta_api_version": "v22.0" },
  "paging": { "next_cursor": null, "has_more": false }
}

Ads — Read

GET/v1/ads/:id

Returns a single ad by ID. Same data shape as the list response.

curl
curl https://api.xyloapi.dev/v1/ads/6830046371154 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": {
    "id": "6830046371154",
    "name": "Catalog Ad",
    "status": "ACTIVE",
    "adset_id": "6830045924954",
    "creative": {
      "page_id": "541746272644756",
      "headline": "",
      "body": "",
      "link": "",
      "call_to_action": ""
    },
    "created_time": "2025-11-02T16:37:38-0800",
    "updated_time": "2026-01-26T05:25:56-0800"
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Ads — Create

POST/v1/adsets/:adset_id/ads

Creates 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.

ParameterTypeRequiredDescription
namestringrequiredAd name
creativeobjectrequiredCreative object with page_id, headline, body, link, call_to_action. Optionally image_url (Xylo auto-uploads).
statusstringoptionalACTIVE or PAUSED (default PAUSED)

Creative object fields: page_id (required), headline, body, link, call_to_action, image_url (optional, auto-uploaded).

curl
curl -X POST https://api.xyloapi.dev/v1/adsets/6830045924954/ads \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Hero Image Ad",
    "creative": {
      "page_id": "541746272644756",
      "headline": "Shop the Summer Sale",
      "body": "Up to 50% off everything",
      "link": "https://example.com/summer",
      "call_to_action": "SHOP_NOW",
      "image_url": "https://example.com/hero.jpg"
    }
  }'

Ads — Update

PATCH/v1/ads/:id

Partial update. Only include the fields you want to change.

curl
curl -X PATCH https://api.xyloapi.dev/v1/ads/6830046371154 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Updated Ad Name", "status": "ACTIVE" }'

Ads — Delete

DELETE/v1/ads/:id

Deletes the ad in Meta.

curl
curl -X DELETE https://api.xyloapi.dev/v1/ads/6830046371154 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Insights

GET/v1/insights

Retrieve 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).

ParameterTypeRequiredDescription
levelstringrequiredaccount, campaign, adset, or ad
date_fromstringoptionalStart date YYYY-MM-DD (default: 7 days ago)
date_tostringoptionalEnd date YYYY-MM-DD (default: today)
campaign_idstringoptionalFilter by campaign ID
adset_idstringoptionalFilter by ad set ID
ad_idstringoptionalFilter by ad ID
time_granularitystringoptionalall (default, aggregated), daily, weekly, monthly
fieldsstringoptionalComma-separated metrics (default: spend,impressions,clicks,ctr,cpc,cpm,reach,frequency,conversions,cost_per_conversion,roas)
breakdownsstringoptionalage, gender, country, placement, device_platform
refreshbooleanoptionalSet to true to bypass cache
curl
curl "https://api.xyloapi.dev/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_10202667788488822"
Response (campaign level)
{
  "data": [
    {
      "campaign_id": "6272111231354",
      "campaign_name": "MM - Retargeting",
      "date_start": "2026-03-01",
      "date_stop": "2026-03-15",
      "spend": 523.94,
      "impressions": 69756,
      "clicks": 1288,
      "ctr": 3.0,
      "cpc": 0.27,
      "cpm": 8.23,
      "reach": 95000,
      "frequency": 1.58,
      "conversions": 2252,
      "cost_per_conversion": 0.23,
      "roas": 245.45,
      "link_clicks": 1143,
      "landing_page_views": 1035
    }
  ],
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.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

GET/v1/audiences
ParameterTypeRequiredDescription
limitintegeroptionalMax results
afterstringoptionalPagination cursor
curl
curl https://api.xyloapi.dev/v1/audiences \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": [
    {
      "id": "6823504735354",
      "name": "FKL heat-hustle giveaway",
      "type": "CUSTOM",
      "approximate_count": 2200,
      "status": "unknown",
      "created_time": 1760971735
    }
  ],
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Audiences — Create

POST/v1/audiences
ParameterTypeRequiredDescription
namestringrequiredAudience name
typestringrequiredAudience type (e.g. website)
ruleobjectrequiredRule definition: { url_contains, retention_days }
curl
curl -X POST https://api.xyloapi.dev/v1/audiences \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Checkout Visitors - 30 Days",
    "type": "website",
    "rule": {
      "url_contains": "/checkout",
      "retention_days": 30
    }
  }'

Audiences — Create Lookalike

POST/v1/audiences/lookalike
ParameterTypeRequiredDescription
namestringrequiredLookalike audience name
source_audience_idstringrequiredSource audience ID to base lookalike on
countrystringrequiredTarget country ISO code (e.g. US)
rationumberrequiredLookalike ratio 0.01 - 0.20 (0.01 = top 1%)
curl
curl -X POST https://api.xyloapi.dev/v1/audiences/lookalike \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "US Lookalike - Purchasers 1%",
    "source_audience_id": "6823504735354",
    "country": "US",
    "ratio": 0.01
  }'

Audiences — Delete

DELETE/v1/audiences/:id
curl
curl -X DELETE https://api.xyloapi.dev/v1/audiences/6823504735354 \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"

Targeting

Targeting — Search Locations

GET/v1/targeting/locations
ParameterTypeRequiredDescription
querystringrequiredSearch string (e.g. 'New York')
typestringoptionalcity, country, region, zip, or geo_market
curl
curl "https://api.xyloapi.dev/v1/targeting/locations?query=New+York" \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822"
Response
{
  "data": [
    {
      "name": "New York",
      "audience_size": 0
    }
  ],
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.0" }
}

Creatives

POST/v1/creatives/upload

Upload 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.

ParameterTypeRequiredDescription
image_urlstringoptionalURL of image to download and upload to Meta
image_datastringoptionalBase64-encoded image data
filenamestringoptionalOptional filename for the image

Provide either image_url or image_data, not both.

curl (URL upload)
curl -X POST https://api.xyloapi.dev/v1/creatives/upload \
  -H "x-api-key: xy_sk_your_key" \
  -H "x-ad-account: act_10202667788488822" \
  -H "Content-Type: application/json" \
  -d '{ "image_url": "https://example.com/hero-banner.jpg" }'
Response
{
  "data": {
    "image_hash": "a1b2c3d4e5f6...",
    "url": "https://..."
  },
  "meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.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.

Error Codes

All errors return an error object with code, message, and meta_error (null unless code is META_API_ERROR).

HTTPCodeDescription
401MISSING_API_KEYNo x-api-key header provided
401INVALID_API_KEYAPI key not found or revoked
400MISSING_AD_ACCOUNTNo x-ad-account header on an endpoint that requires it
403AD_ACCOUNT_NOT_CONNECTEDAd account not connected to this organization
401TOKEN_EXPIREDMeta token expired, user must re-authorize via connect session
429RATE_LIMIT_EXCEEDEDToo many requests. Check X-RateLimit-* headers.
400VALIDATION_ERRORRequest body or query params failed validation
502META_API_ERRORMeta's API returned an error (includes meta_error object)
429META_RATE_LIMITEDMeta is rate-limiting the underlying ad account
404NOT_FOUNDThe requested resource does not exist
500INTERNAL_ERRORSomething went wrong on Xylo's end
Example: META_API_ERROR with meta_error details
{
  "error": {
    "code": "META_API_ERROR",
    "message": "Meta rejected the request.",
    "meta_error": {
      "message": "(#100) Daily budget is too low.",
      "type": "OAuthException",
      "code": 100
    }
  }
}