API Reference · v1
Snap Campaigns API
Research, launch, measure, and optimize ad campaigns programmatically.
Getting started
Four steps from zero to your first metrics pull. All requests go to https://snapcampaigns.com.
- 1
Create an API key
Open the developer settings in your dashboard and create a key. Test-mode keys start with sc_test_, live keys with sc_live_. The full key is shown once — store it securely. Open developer settings →
- 2
List your campaigns
Pass the key as a Bearer token. Every v1 endpoint uses the same header.
curl https://snapcampaigns.com/api/v1/campaigns \ -H "Authorization: Bearer sc_test_…" - 3
Generate a research report
Kick off audience and competitor research for a campaign. Add up to five competitors to pull their active ads.
curl https://snapcampaigns.com/api/v1/research-reports \ -X POST \ -H "Authorization: Bearer sc_test_…" \ -H "Content-Type: application/json" \ -d '{ "campaign_id": "cmp_4e21ab90c7d3", "competitors": [{ "name": "TrailPeak Gear", "domain": "trailpeak.com" }] }' - 4
Fetch performance metrics
Pull time-windowed totals, KPIs, per-platform splits, daily series, and breakdowns.
curl "https://snapcampaigns.com/api/v1/campaigns/cmp_4e21ab90c7d3/metrics?window=30" \ -H "Authorization: Bearer sc_test_…"
Authentication
Every request must include an API key as a Bearer token. Keys come in two modes: sc_test_… for test mode and sc_live_… for live mode.
Authorization: Bearer sc_test_3f9a1c2d4e5b6a7f8090a1b2c3d4e5f60718293aEach key carries scopes that gate what it can do. Calling an endpoint the key is not scoped for returns 403 insufficient_scope.
The full key is shown exactly once at creation — only a hash is stored. Revoked keys fail with 401 unauthorized.
Rate limits
Requests are limited per key, per minute, across all endpoints. When you exceed your limit the API responds with 429 and a rate_limited error envelope, plus these headers:
Retry-After: 60— seconds until the next minute window.X-RateLimit-Limit— your key’s requests-per-minute limit.X-RateLimit-Remaining— requests left in the current window (0 when limited).
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
{ "error": { "code": "rate_limited", "message": "Rate limit of 120 requests/minute exceeded." } }Research Reports
Audience & competitor research reports with shareable web and PDF views.
/api/v1/research-reportsList research reports
Returns up to 50 of the workspace’s research reports, newest first. Requires the `research:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| campaign_id | query | string | No | Only return reports for this campaign. |
Example response
{
"object": "list",
"data": [
{
"id": "rpt_8f3a1c2d4e5b",
"object": "research_report",
"campaignId": "cmp_4e21ab90c7d3",
"title": "Acme Outdoor — Audience & Competitor Research",
"status": "ready",
"shareUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1",
"pdfUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1/pdf",
"generatedAt": "2026-06-01T18:24:09.000Z",
"createdAt": "2026-06-01T18:22:41.000Z"
}
]
}/api/v1/research-reportsGenerate a research report
Generates an audience & competitor research report for a campaign. Up to 5 competitors can be analyzed. Requires the `research:write` scope.
Request body
{
"campaign_id": "cmp_4e21ab90c7d3",
"competitors": [
{
"name": "TrailPeak Gear",
"domain": "trailpeak.com"
}
]
}Example response
{
"id": "rpt_8f3a1c2d4e5b",
"object": "research_report",
"campaignId": "cmp_4e21ab90c7d3",
"title": "Acme Outdoor — Audience & Competitor Research",
"status": "ready",
"shareUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1",
"pdfUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1/pdf",
"generatedAt": "2026-06-01T18:24:09.000Z",
"createdAt": "2026-06-01T18:22:41.000Z"
}/api/v1/research-reports/{id}Retrieve a research report
Returns one report including its full `content` and `competitorIntel`. Requires the `research:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Research report ID. |
Example response
{
"id": "rpt_8f3a1c2d4e5b",
"object": "research_report",
"campaignId": "cmp_4e21ab90c7d3",
"title": "Acme Outdoor — Audience & Competitor Research",
"status": "ready",
"shareUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1",
"pdfUrl": "https://snapcampaigns.com/reports/shr_p2x9k4m1/pdf",
"generatedAt": "2026-06-01T18:24:09.000Z",
"createdAt": "2026-06-01T18:22:41.000Z",
"content": {
"summary": "Acme Outdoor’s buyers skew 28–45, research-heavy, and respond to durability proof points.",
"personas": [
{
"name": "Weekend Basecamper",
"painPoints": [
"gear that fails in rain"
]
}
]
},
"competitorIntel": {
"competitors": [
{
"name": "TrailPeak Gear",
"activeAds": 14,
"topAngle": "lifetime warranty"
}
]
}
}Campaigns
List campaigns, inspect launch state, generate channel plans, and launch.
/api/v1/campaignsList campaigns
Returns up to 50 of the workspace’s campaigns with launch state and channel plan, most recently updated first. Requires the `campaigns:read` scope.
Example response
{
"object": "list",
"data": [
{
"id": "cmp_4e21ab90c7d3",
"object": "campaign",
"name": "Acme Outdoor",
"status": "launched",
"objective": "leads",
"focus": "Spring tent sale",
"channels": {
"meta": "120218890123456789",
"google": "22765432109",
"linkedin": null
},
"channelPlan": {
"totalDailyBudgetUsd": 150,
"generatedAt": "2026-06-02T09:11:30.000Z",
"channels": [
{
"channel": "meta",
"budgetSharePct": 55,
"dailyBudgetUsd": 82.5,
"rationale": "Primary prospecting channel: scale, interest targeting, and fast creative iteration.",
"formats": [
"single_image",
"carousel"
],
"status": "planned"
},
{
"channel": "google",
"budgetSharePct": 30,
"dailyBudgetUsd": 45,
"rationale": "High-intent search capture converts demand the social channels create.",
"formats": [
"responsive_search_ad"
],
"status": "planned"
},
{
"channel": "linkedin",
"budgetSharePct": 15,
"dailyBudgetUsd": 22.5,
"rationale": "Secondary reach for professional segments identified in research.",
"formats": [
"single_image",
"carousel",
"text_ad"
],
"status": "planned"
}
]
},
"createdAt": "2026-05-28T16:02:12.000Z",
"updatedAt": "2026-06-08T07:45:51.000Z"
}
]
}/api/v1/campaigns/{id}Retrieve a campaign
Campaign detail including per-channel launch IDs, the stored channel plan, and metrics sync status. Requires the `campaigns:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
Example response
{
"id": "cmp_4e21ab90c7d3",
"object": "campaign",
"name": "Acme Outdoor",
"status": "launched",
"objective": "leads",
"focus": "Spring tent sale",
"channels": {
"meta": "120218890123456789",
"google": "22765432109",
"linkedin": null
},
"channelPlan": {
"totalDailyBudgetUsd": 150,
"generatedAt": "2026-06-02T09:11:30.000Z",
"channels": [
{
"channel": "meta",
"budgetSharePct": 55,
"dailyBudgetUsd": 82.5,
"rationale": "Primary prospecting channel: scale, interest targeting, and fast creative iteration.",
"formats": [
"single_image",
"carousel"
],
"status": "planned"
},
{
"channel": "google",
"budgetSharePct": 30,
"dailyBudgetUsd": 45,
"rationale": "High-intent search capture converts demand the social channels create.",
"formats": [
"responsive_search_ad"
],
"status": "planned"
},
{
"channel": "linkedin",
"budgetSharePct": 15,
"dailyBudgetUsd": 22.5,
"rationale": "Secondary reach for professional segments identified in research.",
"formats": [
"single_image",
"carousel",
"text_ad"
],
"status": "planned"
}
]
},
"createdAt": "2026-05-28T16:02:12.000Z",
"updatedAt": "2026-06-08T07:45:51.000Z",
"metricsSync": [
{
"platform": "meta",
"status": "success",
"lastError": null,
"finishedAt": "2026-06-08T07:45:51.000Z",
"nextRetryAt": null
}
]
}/api/v1/campaigns/{id}/planGenerate a channel plan
Generates (or regenerates) the multi-channel budget plan for a campaign. Budget is clamped to $10–$100,000/day; unknown channels are ignored. Requires the `campaigns:write` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
Request body
{
"total_daily_budget_usd": 150,
"channels": [
"meta",
"google",
"linkedin"
]
}Example response
{
"object": "channel_plan",
"campaignId": "cmp_4e21ab90c7d3",
"totalDailyBudgetUsd": 150,
"generatedAt": "2026-06-02T09:11:30.000Z",
"channels": [
{
"channel": "meta",
"budgetSharePct": 55,
"dailyBudgetUsd": 82.5,
"rationale": "Primary prospecting channel: scale, interest targeting, and fast creative iteration.",
"formats": [
"single_image",
"carousel"
],
"status": "planned"
},
{
"channel": "google",
"budgetSharePct": 30,
"dailyBudgetUsd": 45,
"rationale": "High-intent search capture converts demand the social channels create.",
"formats": [
"responsive_search_ad"
],
"status": "planned"
},
{
"channel": "linkedin",
"budgetSharePct": 15,
"dailyBudgetUsd": 22.5,
"rationale": "Secondary reach for professional segments identified in research.",
"formats": [
"single_image",
"carousel",
"text_ad"
],
"status": "planned"
}
]
}/api/v1/campaigns/{id}/launchLaunch a campaign
Unified launch across the planned channels (or an explicit subset). Channels already live report `already_launched`; channels missing an ad-platform connection report `needs_setup`. Requires the `campaigns:write` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
Request body
{
"channels": [
"meta",
"google"
]
}Example response
{
"object": "launch_result",
"campaignId": "cmp_4e21ab90c7d3",
"results": [
{
"channel": "meta",
"status": "launched",
"campaignId": "120218890123456789",
"detail": "Campaign created and activated."
},
{
"channel": "google",
"status": "needs_setup",
"detail": "Connect Google Ads in the dashboard to launch this channel."
}
],
"channelPlan": {
"totalDailyBudgetUsd": 150,
"generatedAt": "2026-06-02T09:11:30.000Z",
"channels": [
{
"channel": "meta",
"budgetSharePct": 55,
"dailyBudgetUsd": 82.5,
"rationale": "Primary prospecting channel: scale, interest targeting, and fast creative iteration.",
"formats": [
"single_image",
"carousel"
],
"status": "planned"
},
{
"channel": "google",
"budgetSharePct": 30,
"dailyBudgetUsd": 45,
"rationale": "High-intent search capture converts demand the social channels create.",
"formats": [
"responsive_search_ad"
],
"status": "planned"
},
{
"channel": "linkedin",
"budgetSharePct": 15,
"dailyBudgetUsd": 22.5,
"rationale": "Secondary reach for professional segments identified in research.",
"formats": [
"single_image",
"carousel",
"text_ad"
],
"status": "planned"
}
]
}
}Metrics
Time-windowed performance metrics: totals, KPIs, per-platform splits, daily series, breakdowns.
/api/v1/campaigns/{id}/metricsRetrieve campaign metrics
Time-windowed performance: totals with derived KPIs, per-platform split, daily series, and ad set / ad / audience breakdowns. Requires the `metrics:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
| window | query | integer | No | Lookback window in days (1–180). |
Example response
{
"object": "campaign_metrics",
"campaignId": "cmp_4e21ab90c7d3",
"windowDays": 30,
"totals": {
"impressions": 184220,
"clicks": 3911,
"spend": 1873.4,
"conversions": 162,
"conversionsValue": 9612.75,
"reach": 121408,
"ctr": 2.123,
"cpc": 0.48,
"cpm": 10.17,
"cpa": 11.56,
"roas": 5.13
},
"byPlatform": {
"meta": {
"impressions": 184220,
"clicks": 3911,
"spend": 1873.4,
"conversions": 162,
"conversionsValue": 9612.75,
"reach": 121408,
"ctr": 2.123,
"cpc": 0.48,
"cpm": 10.17,
"cpa": 11.56,
"roas": 5.13
}
},
"daily": [
{
"date": "2026-06-07",
"platform": "meta",
"impressions": 6140,
"clicks": 131,
"spend": 62.18,
"conversions": 6,
"conversionsValue": 348.5
}
],
"breakdowns": {
"adsets": [
{
"platform": "meta",
"level": "adset",
"entityId": "238410000123",
"entityName": "Prospecting — Hiking interests",
"audienceKey": "hiking_interests",
"totals": {
"impressions": 184220,
"clicks": 3911,
"spend": 1873.4,
"conversions": 162,
"conversionsValue": 9612.75,
"reach": 121408,
"ctr": 2.123,
"cpc": 0.48,
"cpm": 10.17,
"cpa": 11.56,
"roas": 5.13
}
}
],
"ads": [],
"audiences": [
{
"audienceKey": "hiking_interests",
"totals": {
"impressions": 184220,
"clicks": 3911,
"spend": 1873.4,
"conversions": 162,
"conversionsValue": 9612.75,
"reach": 121408,
"ctr": 2.123,
"cpc": 0.48,
"cpm": 10.17,
"cpa": 11.56,
"roas": 5.13
}
}
]
}
}Recommendations
Ranked optimization recommendations generated from the latest performance data.
/api/v1/campaigns/{id}/recommendationsList open recommendations
Returns up to 25 open recommendations for the campaign, ranked by predicted impact (rank 1 first). Requires the `recommendations:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
Example response
{
"object": "list",
"data": [
{
"id": "rec_1d9e7b3a5c2f",
"object": "recommendation",
"campaignId": "cmp_4e21ab90c7d3",
"kind": "budget_shift",
"title": "Shift $20/day from Google to Meta",
"rationale": "Meta CPA ($9.80) is 41% lower than Google ($16.60) over the last 14 days at comparable volume.",
"predictedImpact": "+12 conversions/month at flat spend",
"rank": 1,
"status": "open",
"source": "performance_analysis",
"dataThrough": "2026-06-08T00:00:00.000Z",
"createdAt": "2026-06-08T07:50:02.000Z"
}
]
}/api/v1/campaigns/{id}/recommendationsRegenerate recommendations
Re-runs recommendation generation from the latest synced performance data and returns how many were created. Requires the `recommendations:read` scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| id | path | string | Yes | Campaign ID. |
Example response
{
"object": "regeneration_result",
"campaignId": "cmp_4e21ab90c7d3",
"created": 4
}Webhooks
Subscribe HTTPS endpoints to events from your dashboard. Failed deliveries are retried with exponential backoff for up to 6 attempts.
Event types
campaign.launchedA campaign finished launching on one or more ad channels.metrics.updatedFresh performance metrics were synced for a campaign.recommendation.createdA new optimization recommendation was generated.autopilot.action_executedAutopilot executed an optimization action on your behalf.
Payload
Events are delivered as JSON POSTs with the envelope { id, type, createdAt, data } and an X-SnapCampaigns-Event header.
{
"id": "whd_5e8b2c91a4f7",
"type": "campaign.launched",
"createdAt": "2026-06-08T07:45:51.000Z",
"data": {
"campaignId": "cmp_4e21ab90c7d3",
"results": [{ "channel": "meta", "status": "launched", "campaignId": "120218890123456789" }]
}
}Verifying signatures
Each delivery is signed with your endpoint’s secret (shown once at creation). The X-SnapCampaigns-Signature header has the form t=<unix>,v1=<signature>, where v1 is the hex HMAC-SHA256 of `${t}.${body}` using your signing secret. Verify with a timing-safe compare:
Node.js
const crypto = require('node:crypto')
// X-SnapCampaigns-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>
function verifySnapCampaignsSignature(secret, signatureHeader, rawBody) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((pair) => pair.split('=')),
)
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(parts.v1, 'hex'),
Buffer.from(expected, 'hex'),
)
}Errors
All errors share a single JSON envelope:
{
"error": {
"code": "insufficient_scope",
"message": "This key is missing the required scope \"campaigns:write\"."
}
}| Code | HTTP status | Meaning |
|---|---|---|
| unauthorized | 401 | Missing, malformed, or revoked API key. |
| insufficient_scope | 403 | The key lacks the scope this endpoint requires. |
| rate_limited | 429 | Per-key per-minute rate limit exceeded — retry after Retry-After seconds. |
| not_found | 404 | No matching resource exists in your workspace. |
| invalid_request | 400 | The request body or parameters failed validation. |
| internal_error | 500 | Something went wrong on our side — safe to retry. |
OpenAPI
The full machine-readable OpenAPI 3.1 specification — everything on this page is generated from it. Feed it to your codegen tool, Postman, or an LLM.
curl https://snapcampaigns.com/api/v1/openapi