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
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.xyloapi.dev/v1/accounts \ -H "x-api-key: xy_sk_your_key"
Step 2: Get campaigns for an account
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 "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.
# 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.
| 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_10202667788488822). NOT required for GET /v1/accounts and POST /v1/connect-sessions. |
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.
/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://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 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.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.
| Plan | Rate Limit |
|---|---|
| Free | 30 rpm |
| Starter | 120 rpm |
| Pro | 600 rpm |
| Enterprise | Custom |
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.xyloapi.dev/v1/accounts \ -H "x-api-key: xy_sk_your_key"
{
"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
/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.xyloapi.dev/v1/campaigns?status=ACTIVE&limit=10" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/v1/campaigns/:idReturns a single campaign by its Meta campaign ID. Same data shape as list.
curl https://api.xyloapi.dev/v1/campaigns/6830045923554 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/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.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
}'{
"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
/v1/campaigns/:idPartial update. Only include the fields you want to change.
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 }'{
"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
/v1/campaigns/:idSets the campaign status to DELETED in Meta. This is not a hard delete.
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
/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.xyloapi.dev/v1/campaigns/6830045923554/adsets \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/v1/adsets/:idReturns a single ad set by ID. Same data shape as the list response.
curl https://api.xyloapi.dev/v1/adsets/6830045924954 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/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 |
| start_time | string | optional | ISO 8601 start time |
| end_time | string | optional | ISO 8601 end time |
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
/v1/adsets/:idPartial update. Only include the fields you want to change.
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
/v1/adsets/:idDeletes the ad set in Meta.
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
/v1/adsets/:adset_id/adsReturns all ads within an ad set.
curl https://api.xyloapi.dev/v1/adsets/6830045924954/ads \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/v1/ads/:idReturns a single ad by ID. Same data shape as the list response.
curl https://api.xyloapi.dev/v1/ads/6830046371154 \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/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.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
/v1/ads/:idPartial update. Only include the fields you want to change.
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
/v1/ads/:idDeletes the ad in Meta.
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
/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: today) |
| 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, placement, device_platform |
| refresh | boolean | optional | Set to true to bypass cache |
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"
{
"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
/v1/audiences| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Max results |
| after | string | optional | Pagination cursor |
curl https://api.xyloapi.dev/v1/audiences \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"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
/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.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
/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.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
/v1/audiences/:idcurl -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 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.xyloapi.dev/v1/targeting/search?type=interests&query=yoga" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"data": [
{
"id": "6003306084421",
"name": "Yoga",
"audience_size": 449755406
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.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.xyloapi.dev/v1/targeting/locations?query=New+York" \ -H "x-api-key: xy_sk_your_key" \ -H "x-ad-account: act_10202667788488822"
{
"data": [
{
"name": "New York",
"audience_size": 0
}
],
"meta": { "cached": false, "cache_age_seconds": null, "meta_api_version": "v22.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.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" }'{
"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).
| 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 |
| 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 |
| 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
}
}
}