GET /v1/projects/:projectId/ads/ad-accounts
List Meta, TikTok, and Apple ad accounts connected to a project, with credential health.
/v1/projects/{projectId}/ads/ad-accounts- Auth
- Bearer
- Scope
- ads:read
Returns every ad account connected to the project across Meta, TikTok, and Apple Search Ads. Each row carries the platform-native id and current credential health. Layers' ad platform integrations are bring-your-own — partners connect their own ad accounts via OAuth and platform billing runs against the partner's own funding source. This is the first endpoint to hit when a partner agent needs to know which platforms a project can actually buy ads on right now.
projectIdstring (UUID)requiredProject to list within.
platformsstring[]optionalRestrict to one or more platforms.One of:meta_ads,tiktok_ads,apple_adscredentialModestringoptionalFilter by credential source. All Layers ad connections are `byo` today; `agency` is reserved.One of:agency,byohealthybooleanoptionalKeep only ad accounts with tokenStatus=valid.
Example request
curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/ads/ad-accounts?healthy=true" \
-H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."const res = await fetch(
`https://api.layers.com/v1/projects/${projectId}/ads/ad-accounts?healthy=true`,
{ headers: { Authorization: `Bearer ${apiKey}` } },
);
const { items } = await res.json();import httpx
r = httpx.get(
f"https://api.layers.com/v1/projects/{project_id}/ads/ad-accounts",
params={"healthy": True},
headers={"Authorization": f"Bearer {api_key}"},
)
items = r.json()["items"]Response
{
"items": [
{
"adAccountId": "01HXF1A2B3C4D5E6F7G8H9J0K1",
"platform": "meta_ads",
"externalId": "act_123456789012345",
"name": "Acme Coffee — US",
"currency": "USD",
"timezone": "UTC",
"credentialMode": "byo",
"tokenStatus": "valid",
"tokenLastValidatedAt": null,
"walletBalance": null,
"connectedAt": "2026-03-02T14:11:00Z"
},
{
"adAccountId": "01HXF2A2B3C4D5E6F7G8H9J0K1",
"platform": "tiktok_ads",
"externalId": "7389201...",
"name": "Acme Coffee TikTok",
"currency": "USD",
"timezone": "UTC",
"credentialMode": "byo",
"tokenStatus": "valid",
"tokenLastValidatedAt": null,
"walletBalance": null,
"connectedAt": "2026-03-18T09:42:00Z"
},
{
"adAccountId": "01HXF3A2B3C4D5E6F7G8H9J0K1",
"platform": "apple_ads",
"externalId": "org-9823412",
"name": "Acme Coffee iOS",
"currency": "USD",
"timezone": "UTC",
"credentialMode": "byo",
"tokenStatus": "expired",
"tokenLastValidatedAt": null,
"walletBalance": null,
"connectedAt": "2026-02-11T19:05:00Z"
}
]
}The response is unpaginated — each project has at most one credential row per platform, so items returns the union directly with no nextCursor.
Credential modes
byo— The end-customer connected their own ad account via OAuth. This is the only mode shipping today. Billing runs against the customer's funding source on the platform;walletBalanceis alwaysnullbecause Layers does not aggregate platform wallet state in Phase 1. Token refresh is automatic but tied to the customer's OAuth grant — a revoked grant surfaces astokenStatus: "expired"until reconnected.agency— Reserved for a future agency / Business Center flow. No production projects use this mode yet.
Token status
| Value | Meaning |
|---|---|
valid | Last validation call succeeded. Safe to use. |
expired | Refresh attempt failed. Reconnect via POST /v1/projects/:projectId/ads/ad-accounts/oauth-url. |
revoked | User revoked the grant on the platform. Reconnect required. |
pending | Connection just initiated; first validation has not landed. |
tokenLastValidatedAt is reserved for the validation timestamp once Layers wires up periodic validation; it is null in Phase 1.
Notes
walletBalanceis reserved for a future agency / Business Center mode. It is alwaysnulltoday. For BYO, query the platform's own API if you need wallet state.externalIdis the platform-native account id —act_...on Meta, a numeric string on TikTok,org-...on Apple. Use it when linking out to the platform's ad manager.timezoneis reported as"UTC"for every account in Phase 1 — Layers does not yet pull the platform-native timezone. If you need the local-clock timezone the ads are bidding against, query the platform's own API.- A
tokenStatusofexpiredorrevokeddoes not immediately pause running ads — the platform keeps those running from its side. New ad creation against that ad account will fail until reconnected. - Disconnected ad accounts (rows where
disconnected_atis set) are excluded from the response by default. There is no include-disconnected flag.
See also
POST /v1/projects/:projectId/ads/ad-accounts/oauth-url— connect or reconnectGET /v1/projects/:projectId/ads/campaigns— campaigns on these accountsGET /v1/projects/:projectId/ads/capi-status— CAPI relay health per platform
Ads
Read-only access to ad accounts, campaigns, ad sets, ads, and metrics across Meta, TikTok, and Apple. Use these to monitor spend and performance without needing the platform dashboards.
POST /v1/projects/:projectId/ads/ad-accounts/oauth-url
Create a one-time OAuth URL for connecting a Meta, TikTok, or Apple ad account to a project.