Skip to content
Developers
Open app

Get an ad account

GET
/v1/metrics/ad-accounts/{id}
const url = 'https://api.focal.inc/v1/metrics/ad-accounts/f0958ef1-b662-425b-8794-79ca648489c7?space_id=6916bc3d-e1ab-425b-ac25-eefada01a9a2';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}

Fetch a single connected ad account by id.

Returns 404 when the account doesn’t exist, is archived, is private to another user, belongs to another organization, or isn’t shared with space_id — the cases share a response so a caller can’t probe for the existence of accounts it may not read.

Pass fields to trim the response to the properties you actually need — e.g. ?fields=name,currency returns just those (plus id, always). Unknown names are rejected rather than ignored.

Required scope: metrics:read — a scope of its own, deliberately not covered by media:read, because ad spend and revenue are a different sensitivity class from asset metadata. (all:read does cover it.)

space_id is required and decides what you can see. The caller must be able to read that space (404 otherwise, so a space you can’t reach is indistinguishable from one that doesn’t exist), and an ad account is returned only when it is shared with that space or with the whole organization — or is private and was connected by the calling user. Rate-limit cost: 1.

id
required
string format: uuid

Example

f0958ef1-b662-425b-8794-79ca648489c7
space_id
required
string format: uuid

Example

6916bc3d-e1ab-425b-ac25-eefada01a9a2

Required. The single space to query, as a UUID. The API reads one space at a time — comma-separated lists and wildcards are not accepted, and there is no implicit default.

fields
string

Return only these properties of each ad account, instead of the full object. id is always included. Omit for the complete object. Selectable: id, external_id, name, service, currency, timezone_name, external_created_at, cutoff_date, created_at, updated_at.

OK

Media typeapplication/json
object
data
required
object
id
required

Focal’s id for the ad account — the value every other /v1/metrics/* endpoint takes as ad_account_id.

string format: uuid
external_id
required

The account’s id on the ad platform itself (e.g. a Meta ad account id). Use this to correlate with data pulled straight from the platform; use id when calling Focal.

string
name
required

The account name as it appears on the ad platform.

string
service
required

Which ad platform this account belongs to.

string
Allowed values: meta tiktok
currency
required

ISO-4217 currency of this account’s monetary metrics (e.g. spend), or null when the platform hasn’t reported one. Values are not converted: totals across accounts with different currencies are not comparable, so read this before summing spend across accounts.

string
nullable
timezone_name
required

IANA timezone the platform reports this account’s daily figures in (e.g. America/Los_Angeles), or null when unknown. Daily metric values are bucketed by the platform in this timezone, not in UTC — so a date window means slightly different things for accounts in different zones.

string
nullable
external_created_at
required

When the account was created on the ad platform, or null when the platform hasn’t reported it. Distinct from created_at, which is when Focal connected it.

string format: date-time
nullable
cutoff_date
required

YYYY-MM-DD before which Focal holds no metrics for this account, or null when there is no cutoff. A date window that starts earlier isn’t an error — it simply has nothing to return for the earlier part, which is not the same as zero spend. Date-only on purpose: it has no meaningful time of day.

string
nullable
created_at
required

When the account was connected to Focal.

string format: date-time
updated_at
required
string format: date-time

Example

{
"data": {
"id": "f0958ef1-b662-425b-8794-79ca648489c7",
"external_id": "act_4829175306",
"name": "Alder & Pine – Meta",
"service": "meta",
"currency": "USD",
"timezone_name": "America/Los_Angeles",
"external_created_at": "2025-09-12T16:04:00Z",
"cutoff_date": "2027-01-01",
"created_at": "2027-03-04T10:15:00Z",
"updated_at": "2027-03-06T14:42:00Z"
}
}

The request was malformed (invalid cursor, unknown type, validation failure, etc.). error.code is bad_request.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "bad_request",
"message": "Invalid request body",
"details": [
{
"field": "space_id",
"issue": "Invalid UUID"
}
]
}
}

Missing, invalid, or disabled token. error.code is unauthenticated.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "unauthenticated",
"message": "Bearer token is missing or invalid"
}
}

The token doesn’t have the scope this endpoint requires. error.code is forbidden.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "forbidden",
"message": "Token doesn't have the required scope"
}
}

Resource doesn’t exist or is outside the token’s space allow-list. The two cases share a response shape so a restricted token can’t probe for the existence of out-of-scope spaces. error.code is not_found.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "not_found",
"message": "Not found"
}
}

Rate limit exceeded. error.code is rate_limited. Wait the number of seconds in the Retry-After header before retrying. Quotas are per-token; see the “Rate limiting” section at the top of this document.

Media typeapplication/json

Canonical error envelope. code is stable across releases and intended for programmatic handling; message is human-readable. details only appears for validation failures and lists the offending fields.

object
error
required
object
code
required
string
Allowed values: unauthenticated forbidden not_found bad_request conflict rate_limited internal_error
message
required
string
details
Array<object>
object
field
string
issue
required
string

Example

{
"error": {
"code": "rate_limited",
"message": "Rate limit of 1000 units per 60s exceeded (this request cost 5)"
}
}
Retry-After
integer
Example
12

Seconds to wait before retrying.