Introduction
The Spot API lets you programmatically manage your digital signage network — control players, manage content, query analytics, and receive real-time events via webhooks.
Authentication
All endpoints require a Bearer token. Create API keys in Settings → API Keys.
curl -H "Authorization: Bearer {YOUR_API_KEY}" https://yourapp.com/api/v1/players
Scopes
Each key has one or more scopes that control access:
| Scope | Description |
|---|---|
read:players |
List players and groups |
write:players |
Send commands to players |
read:content |
Read layouts, playlists, assets |
write:content |
Create/modify content |
read:analytics |
Access analytics and reports |
webhooks |
Manage webhook registrations |
Rate limits
| Plan | Requests/min |
|---|---|
| Free | 60 |
| Pro | 300 |
| Business | 1,000 |
Rate limit status is returned in every response via X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers.
Errors
The API uses standard HTTP status codes. Error responses follow this shape:
{ "message": "Descriptive error message" }
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_API_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Create and manage API keys in Settings → API Keys.
Analytics
Query playback, uptime, and impression data for your network.
Summary
requires authentication
High-level snapshot: active players, plays today, and average uptime.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/summary" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/analytics/summary"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/analytics/summary';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/analytics/summary'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Playback events
requires authentication
Returns aggregated playback data for the specified date range.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/playback?from=2026-01-01T00%3A00%3A00Z&to=2026-01-31T23%3A59%3A59Z&player_id=1&layout_id=1&granularity=day" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-05-01T18:20:08\",
\"to\": \"2052-05-24\",
\"player_id\": 16,
\"layout_id\": 16,
\"granularity\": \"hour\"
}"
const url = new URL(
"http://localhost/api/v1/analytics/playback"
);
const params = {
"from": "2026-01-01T00:00:00Z",
"to": "2026-01-31T23:59:59Z",
"player_id": "1",
"layout_id": "1",
"granularity": "day",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24",
"player_id": 16,
"layout_id": 16,
"granularity": "hour"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/analytics/playback';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'from' => '2026-01-01T00:00:00Z',
'to' => '2026-01-31T23:59:59Z',
'player_id' => '1',
'layout_id' => '1',
'granularity' => 'day',
],
'json' => [
'from' => '2026-05-01T18:20:08',
'to' => '2052-05-24',
'player_id' => 16,
'layout_id' => 16,
'granularity' => 'hour',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/analytics/playback'
payload = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24",
"player_id": 16,
"layout_id": 16,
"granularity": "hour"
}
params = {
'from': '2026-01-01T00:00:00Z',
'to': '2026-01-31T23:59:59Z',
'player_id': '1',
'layout_id': '1',
'granularity': 'day',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export playback data
requires authentication
Generates a CSV export of playback events. Returns a signed download URL valid for 1 hour.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/playback/export?from=2026-01-01T00%3A00%3A00Z&to=2026-01-31T23%3A59%3A59Z" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-05-01T18:20:08\",
\"to\": \"2052-05-24\"
}"
const url = new URL(
"http://localhost/api/v1/analytics/playback/export"
);
const params = {
"from": "2026-01-01T00:00:00Z",
"to": "2026-01-31T23:59:59Z",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/analytics/playback/export';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'from' => '2026-01-01T00:00:00Z',
'to' => '2026-01-31T23:59:59Z',
],
'json' => [
'from' => '2026-05-01T18:20:08',
'to' => '2052-05-24',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/analytics/playback/export'
payload = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24"
}
params = {
'from': '2026-01-01T00:00:00Z',
'to': '2026-01-31T23:59:59Z',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Player uptime
requires authentication
Returns online/offline percentage per player for the date range.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/players/uptime?from=2026-01-01T00%3A00%3A00Z&to=2026-01-31T23%3A59%3A59Z" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-05-01T18:20:08\",
\"to\": \"2052-05-24\"
}"
const url = new URL(
"http://localhost/api/v1/analytics/players/uptime"
);
const params = {
"from": "2026-01-01T00:00:00Z",
"to": "2026-01-31T23:59:59Z",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/analytics/players/uptime';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'from' => '2026-01-01T00:00:00Z',
'to' => '2026-01-31T23:59:59Z',
],
'json' => [
'from' => '2026-05-01T18:20:08',
'to' => '2052-05-24',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/analytics/players/uptime'
payload = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24"
}
params = {
'from': '2026-01-01T00:00:00Z',
'to': '2026-01-31T23:59:59Z',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Impressions
requires authentication
Returns ad impression counts and revenue breakdown.
Example request:
curl --request GET \
--get "http://localhost/api/v1/analytics/impressions?from=2026-01-01T00%3A00%3A00Z&to=2026-01-31T23%3A59%3A59Z&campaign_id=1" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"from\": \"2026-05-01T18:20:08\",
\"to\": \"2052-05-24\",
\"campaign_id\": 16
}"
const url = new URL(
"http://localhost/api/v1/analytics/impressions"
);
const params = {
"from": "2026-01-01T00:00:00Z",
"to": "2026-01-31T23:59:59Z",
"campaign_id": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24",
"campaign_id": 16
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/analytics/impressions';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'from' => '2026-01-01T00:00:00Z',
'to' => '2026-01-31T23:59:59Z',
'campaign_id' => '1',
],
'json' => [
'from' => '2026-05-01T18:20:08',
'to' => '2052-05-24',
'campaign_id' => 16,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/analytics/impressions'
payload = {
"from": "2026-05-01T18:20:08",
"to": "2052-05-24",
"campaign_id": 16
}
params = {
'from': '2026-01-01T00:00:00Z',
'to': '2026-01-31T23:59:59Z',
'campaign_id': '1',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Assets
List assets and generate signed upload URLs.
List assets
requires authentication
Returns all assets for the authenticated team.
Example request:
curl --request GET \
--get "http://localhost/api/v1/assets?type=image&folder_id=1" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/assets"
);
const params = {
"type": "image",
"folder_id": "1",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/assets';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'type' => 'image',
'folder_id' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/assets'
params = {
'type': 'image',
'folder_id': '1',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get upload URL
requires authentication
Returns a signed URL for direct upload to storage. Upload the file via
HTTP PUT to the upload_url, then reference asset_key when confirming
the upload in your workflow.
Example request:
curl --request POST \
"http://localhost/api/v1/assets/upload-url" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"filename\": \"promo-banner.jpg\",
\"mime_type\": \"image\\/jpeg\"
}"
const url = new URL(
"http://localhost/api/v1/assets/upload-url"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"filename": "promo-banner.jpg",
"mime_type": "image\/jpeg"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/assets/upload-url';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'filename' => 'promo-banner.jpg',
'mime_type' => 'image/jpeg',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/assets/upload-url'
payload = {
"filename": "promo-banner.jpg",
"mime_type": "image\/jpeg"
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete asset
requires authentication
Permanently deletes an asset. This cannot be undone.
Example request:
curl --request DELETE \
"http://localhost/api/v1/assets/16" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/assets/16"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/assets/16';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/assets/16'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Layouts
Read layouts and assign them to players or groups.
List layouts
requires authentication
Returns all layouts for the authenticated team.
Example request:
curl --request GET \
--get "http://localhost/api/v1/layouts" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/layouts"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/layouts';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/layouts'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Assign layout
requires authentication
Assigns a layout to one or more players or player groups.
Example request:
curl --request PATCH \
"http://localhost/api/v1/layouts/16/assign" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"player_ids\": [
1,
2
],
\"group_ids\": [
1
]
}"
const url = new URL(
"http://localhost/api/v1/layouts/16/assign"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"player_ids": [
1,
2
],
"group_ids": [
1
]
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/layouts/16/assign';
$response = $client->patch(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'player_ids' => [
1,
2,
],
'group_ids' => [
1,
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/layouts/16/assign'
payload = {
"player_ids": [
1,
2
],
"group_ids": [
1
]
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Player Groups
Manage groups and dispatch bulk commands.
List player groups
requires authentication
Returns all player groups with player counts.
Example request:
curl --request GET \
--get "http://localhost/api/v1/player-groups" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/player-groups"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/player-groups';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/player-groups'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send command to group
requires authentication
Dispatches a command to every player in the group.
Example request:
curl --request POST \
"http://localhost/api/v1/player-groups/16/commands" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"command\": \"reboot\"
}"
const url = new URL(
"http://localhost/api/v1/player-groups/16/commands"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"command": "reboot"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/player-groups/16/commands';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'command' => 'reboot',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/player-groups/16/commands'
payload = {
"command": "reboot"
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Players
Manage and monitor your display network.
List players
requires authentication
Returns all players belonging to your team with their current status.
Example request:
curl --request GET \
--get "http://localhost/api/v1/players?status=online" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/players"
);
const params = {
"status": "online",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/players';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'status' => 'online',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/players'
params = {
'status': 'online',
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get player
requires authentication
Returns a single player with full detail including current content assignment.
Example request:
curl --request GET \
--get "http://localhost/api/v1/players/16" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/players/16"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/players/16';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/players/16'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get latest screenshot
requires authentication
Returns the URL of the most recent screenshot captured from this player.
Example request:
curl --request GET \
--get "http://localhost/api/v1/players/16/screenshot" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/players/16/screenshot"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/players/16/screenshot';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/players/16/screenshot'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send command
requires authentication
Dispatches a command to a player via WebSocket. Commands are fire-and-forget;
track acknowledgement via the command_id returned.
Example request:
curl --request POST \
"http://localhost/api/v1/players/16/commands" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"command\": \"reboot\"
}"
const url = new URL(
"http://localhost/api/v1/players/16/commands"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"command": "reboot"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/players/16/commands';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'command' => 'reboot',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/players/16/commands'
payload = {
"command": "reboot"
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Playlists
Create and manage playlists.
List playlists
requires authentication
Returns all playlists for the authenticated team.
Example request:
curl --request GET \
--get "http://localhost/api/v1/playlists" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/playlists"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/playlists';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/playlists'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create playlist
requires authentication
Creates a new empty playlist.
Example request:
curl --request POST \
"http://localhost/api/v1/playlists" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Summer Campaign\",
\"description\": \"Q3 promotions\"
}"
const url = new URL(
"http://localhost/api/v1/playlists"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Summer Campaign",
"description": "Q3 promotions"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/playlists';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'name' => 'Summer Campaign',
'description' => 'Q3 promotions',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/playlists'
payload = {
"name": "Summer Campaign",
"description": "Q3 promotions"
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update playlist items
requires authentication
Replaces the items in a playlist with a new ordered list of asset IDs.
Example request:
curl --request PATCH \
"http://localhost/api/v1/playlists/16/items" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"items\": [
{
\"asset_id\": 1,
\"duration\": 10
}
]
}"
const url = new URL(
"http://localhost/api/v1/playlists/16/items"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"items": [
{
"asset_id": 1,
"duration": 10
}
]
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/playlists/16/items';
$response = $client->patch(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
],
null,
[
'stdClass' => [
'asset_id' => [
1,
],
'duration' => [
10,
],
],
],
[
'items' => [
$o[0],
],
],
[]
),
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/playlists/16/items'
payload = {
"items": [
{
"asset_id": 1,
"duration": 10
}
]
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Webhooks
Register endpoints to receive push notifications for events on your network.
Verifying webhook signatures
Every webhook delivery includes an X-Spot-Signature header. Verify it:
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $request->header('X-Spot-Signature'))) {
abort(401);
}
List webhooks
requires authentication
Returns all registered webhooks. The secret is never returned after creation.
Example request:
curl --request GET \
--get "http://localhost/api/v1/webhooks" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/webhooks"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/webhooks';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/webhooks'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-api-version: 1.2
access-control-allow-origin: *
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create webhook
requires authentication
Registers a new webhook endpoint. The secret is shown only once — store it securely.
Example request:
curl --request POST \
"http://localhost/api/v1/webhooks" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"url\": \"https:\\/\\/example.com\\/webhooks\\/spot\",
\"events\": [
\"player.online\",
\"player.offline\"
]
}"
const url = new URL(
"http://localhost/api/v1/webhooks"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "https:\/\/example.com\/webhooks\/spot",
"events": [
"player.online",
"player.offline"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/webhooks';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'url' => 'https://example.com/webhooks/spot',
'events' => [
'player.online',
'player.offline',
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/webhooks'
payload = {
"url": "https:\/\/example.com\/webhooks\/spot",
"events": [
"player.online",
"player.offline"
]
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete webhook
requires authentication
Permanently removes a webhook registration.
Example request:
curl --request DELETE \
"http://localhost/api/v1/webhooks/16" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"http://localhost/api/v1/webhooks/16"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'http://localhost/api/v1/webhooks/16';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'http://localhost/api/v1/webhooks/16'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.