Appearance
Coffee
Query campaign-level audience and engagement data scoped to your authorized networks. Two endpoints are available: a paginated JSON endpoint for app integration and a streaming CSV endpoint for bulk export.
GET /api/v1/coffee
Returns Coffee rows as paginated JSON.
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
from | ISO 8601 date or datetime | Yes | — | Start of the time window (inclusive). Date-only (e.g. 2026-04-01) is treated as UTC midnight. |
to | ISO 8601 date or datetime | Yes | — | End of the time window (exclusive). Date-only is treated as UTC midnight. |
site_id | integer or integer[] | No | — | Filter by one or more site IDs. Each value must be a positive integer. |
limit | integer | No | 1000 | Number of rows to return. Must be a positive integer between 1 and 5000. Values outside this range are rejected. |
sort | asc | desc | No | asc | Sort direction. Must be exactly asc or desc — any other value is rejected. |
after | ISO 8601 datetime | No | — | Cursor pagination. Returns rows strictly after this timestamp. Use the next_after value from the previous response. Takes priority over offset. Must be a valid ISO 8601 timestamp (not date-only). |
offset | integer | No | — | Offset pagination. Number of rows to skip. Must be a non-negative integer. Use for random page access. Ignored if after is provided. |
Date range limit
The from–to range cannot exceed 31 days.
Pagination modes
Two pagination modes are supported. Cursor mode is preferred for sequential traversal of large datasets.
Cursor-based (recommended for large datasets)
Pass next_after from the previous response as after= on the next request. When next_after is null, you have reached the last page.
Page 1: ?from=...&to=...&limit=5000
Page 2: ?from=...&to=...&limit=5000&after=<next_after from page 1>
Page 3: ?from=...&to=...&limit=5000&after=<next_after from page 2>Offset-based (random page access)
Use offset to jump directly to any page. Note that performance may degrade at large offsets.
Page 1: ?from=...&to=...&limit=5000&offset=0
Page 3: ?from=...&to=...&limit=5000&offset=10000Response 200
json
{
"status": true,
"message": "Success",
"data": {
"total": 84320,
"limit": 5000,
"next_after": "2026-04-02T08:44:21.000Z",
"count": 5000,
"rows": [
{
"POP_TYPE": "ACTIVE",
"PLAY_REASON": "SCHEDULED",
"CAMPAIGN_ID": 101,
"CAMPAIGN_NAME": "Spring Campaign",
"TARGET_STRATEGY_ID": 5,
"STRATEGY_NAME": "Daytime",
"MEDIA_PLAYER_NAME": "Player A",
"DETAILID": 9001,
"EVENT_ID": "evt-abc123",
"TITLE": "Promo 30s",
"DISPLAY_TITLE": "Spring Promo",
"PLAYOUT": 30.0,
"DT_START_SITE_TZ": "2026-04-02T08:00:00.000Z",
"DT_END_SITE_TZ": "2026-04-02T08:00:30.000Z",
"WATCHERID": 500,
"SITE_ID": 1,
"SITE_NAME": "Sydney CBD",
"NETWORK_ID": 7,
"NETWORK_NAME": "Acme Network",
"IS_IMPRESSION": 1,
"IS_VIEW": 1,
"IS_WATCHED": 0,
"ATTENTION": 82.50,
"WATCH_TIME": 14.25
}
]
}
}Last page
When next_after is null and count is less than limit, you have reached the last page.
Client loop example
js
let after = null;
let allRows = [];
do {
const params = new URLSearchParams({ from, to, limit: 5000 });
if (after) params.set('after', after);
const { data } = await fetch(`/api/v1/coffee?${params}`, {
headers: { Authorization: `Bearer ${token}` }
}).then(r => r.json());
allRows.push(...data.rows);
after = data.next_after;
} while (after !== null);Errors
| Code | Description |
|---|---|
400 | Missing from/to; invalid ISO 8601 value; range exceeds 31 days; from is not before to; limit out of range; invalid offset, sort, site_id, or after value |
401 | Invalid or expired token; X-Network-Name header value is not authorized by the token; site_id does not belong to the caller's networks |
403 | Token does not have access to any networks |
500 | Internal server error |
GET /api/v1/coffee/download
Streams Coffee rows as a CSV file. Designed for bulk data export — memory usage on the server is constant regardless of result size.
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
from | ISO 8601 date or datetime | Yes | — | Start of the time window (inclusive). Date-only (e.g. 2026-04-01) is treated as UTC midnight. |
to | ISO 8601 date or datetime | Yes | — | End of the time window (exclusive). Date-only is treated as UTC midnight. |
site_id | integer or integer[] | No | — | Filter by one or more site IDs. Each value must be a positive integer. |
limit | integer | No | — | Maximum rows to include. Must be a positive integer between 1 and 5000. If omitted, all matching rows are streamed. |
offset | integer | No | 0 | Number of rows to skip. Must be a non-negative integer. |
Unsupported parameters
after and sort are not supported for CSV download — the endpoint returns 400 if either is provided.
Postman
Postman displays CSV as raw text. To save the file, click Save Response → Save to a file after the request completes. For large exports, use curl instead.
curl example
API Base URL: https://prod-gcp.services.api.viana.ai
bash
curl -H "Authorization: Bearer <token>" \
"<API_BASE_URL>/api/v1/coffee/download?from=2026-04-01&to=2026-04-07" \
-o coffee.csvResponse
Returns a text/csv file download with filename coffee_YYYYMMDD_YYYYMMDD.csv. The first row is always the header row containing all available field names — even if there are no matching data rows.
Field reference
The CSV export includes the following fields:
| Column | Type | Description |
|---|---|---|
POP_TYPE | string | Population type |
PLAY_REASON | string | Reason for playback |
CAMPAIGN_ID | number | Campaign identifier |
CAMPAIGN_NAME | string | Campaign name |
TARGET_STRATEGY_ID | number | Targeting strategy ID |
STRATEGY_NAME | string | Targeting strategy name |
MEDIA_PLAYER_NAME | string | Media player name |
DETAILID | number | Detail record ID |
EVENT_ID | string | Event identifier |
TITLE | string | Content title |
DISPLAY_TITLE | string | Display title |
PLAYOUT | float | Playout duration (seconds) |
DT_START_SITE_TZ | timestamp | Playout start time (site timezone) |
DT_END_SITE_TZ | timestamp | Playout end time (site timezone) |
WATCHERID | number | Watcher identifier |
DELTA_ID | array | Delta IDs |
SITE_TIMESTAMP_SITE_TZ | timestamp | Site timestamp (site timezone) |
WATCH_TIMESTAMP_SITE_TZ_DETAILS | array | Watch start timestamps |
WATCH_TIMESTAMP_END_SITE_TZ_DETAILS | array | Watch end timestamps |
DETECTION_TIMESTAMP_SITE_TZ | timestamp | Detection start (site timezone) |
DETECTION_TIMESTAMP_END_SITE_TZ | timestamp | Detection end (site timezone) |
REID | string | Re-identification token |
START_TIME | array | Start time details |
GENDER | string | Detected gender |
AGE | string | Detected age |
AGE_CATEGORY | string | Age category |
GENDER_AGE_CATEGORY | string | Combined gender+age category |
MOOD | string | Detected mood |
DISTANCE | array | Distance measurements |
ATTENTION | number | Attention score (0–100) |
DURATION | float | Detection duration (seconds) |
WATCH_TIME | number | Total watch time (seconds) |
IS_IMPRESSION | number | 1 if impression, 0 otherwise |
DURATION_IMPRESSION | number | Impression duration (seconds) |
IS_GLANCE | number | 1 if glance, 0 otherwise |
DURATION_GLANCE | number | Glance duration (seconds) |
IS_VIEW | number | 1 if view, 0 otherwise |
DURATION_VIEW | number | View duration (seconds) |
IS_VIEW_THROUGH | number | 1 if view-through, 0 otherwise |
DURATION_VIEW_THROUGH | number | View-through duration (seconds) |
IS_WATCHED | number | 1 if watched, 0 otherwise |
NETWORK_ID | number | Network identifier |
NETWORK_NAME | string | Network name |
SITE_ID | number | Site identifier |
SITE_NAME | string | Site name |
LOCATION_ID | number | Location identifier |
LOCATION_NAME | string | Location name |
DEVICE_ID | number | Device identifier |
DEVICE_NAME | string | Device name |
FEEDER_ID | number | Feeder identifier |
FEEDER_NAME | string | Feeder name |
SUBSCRIPTION_NAME | string | Subscription name |
SITE_TZ | string | Site timezone |
DEVICE_TZ | string | Device timezone |
TIME_RANGE_SITE_TZ | string | Time range label (site timezone) |
MONTH | string | Month label |
DT_ONLY_SITE_TZ | date | Date only (site timezone) |
HOUR_SITE_TZ | string | Hour label (site timezone) |
DAY_SITE_TZ | string | Day label (site timezone) |
IS_BUSINESSHOUR | boolean | True if within business hours |
SILVER_STAGE_TIMESTAMP | timestamp | Silver stage processing time |
STREAM_STAGE_TIMESTAMP | timestamp | Stream stage processing time |
SF_DELTA_ID | string | Snowflake delta ID |
Errors
| Code | Description |
|---|---|
400 | Missing from/to; invalid ISO 8601 value; range exceeds 31 days; from is not before to; limit out of range; invalid offset or site_id; after or sort provided |
401 | Invalid or expired token; X-Network-Name header value is not authorized by the token; site_id does not belong to the caller's networks |
403 | Token does not have access to any networks |
500 | Internal server error |