# Get started


> From a token to a web page playing on a screen, in five requests. Everything here is copy-and-pasteable.
The goal of this page is a real outcome: a web page playing on one of your screens, put there entirely over HTTP. Five requests.

You need an [API token](/docs/developers/overview/api-tokens/) and a screen already paired.

```bash
export API_TOKEN=your-token-here
export API=https://api.screenlyapp.com/api/v4.1
```

## 1. Prove the token works

```bash
curl -s -H "Authorization: Token $API_TOKEN" "$API/screens" | jq '.[].name'
```

If you get a list of screen names, everything after this is detail. A `401` means the token is wrong; a `403` usually means the token belongs to an account without access to what you asked for.

## 2. Find the screen you want

Filters go in the query string, as `field=operator.value`, because the API is built on [PostgREST](https://postgrest.org/).

```bash
curl -s -H "Authorization: Token $API_TOKEN" \
  "$API/screens?name=eq.Reception&select=id,name,location"
```

`eq` is one of several. `like`, `in`, `gt`, `is` and the rest all work, which is why there is no endpoint per question.

> [!NOTE]
> A screen's **status** is not on the screen. Online and in-sync live on `/v4.1/screens/statuses`, keyed by `screen_id`. That separation is deliberate: the screen record is what you configured, the status record is what the device reported.

## 3. Add the content

```bash
curl -s -X POST -H "Authorization: Token $API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Prefer: return=representation" \
  -d '{"title": "Ops dashboard", "source_url": "https://example.com/dashboard"}' \
  "$API/assets"
```

`Prefer: return=representation` makes the API hand back the object it created, including its `id`. Without it you get a `201` and nothing to work with, and then have to go looking for what you just made.

Screenly works out what the URL is from what it serves: a page becomes a web asset, an image or video is fetched and stored.

## 4. Put it in a playlist

```bash
curl -s -X POST -H "Authorization: Token $API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Prefer: return=representation" \
  -d '{"playlist_id": "PLAYLIST_ID", "asset_id": "ASSET_ID", "duration": 30, "position": 0}' \
  "$API/playlist-items"
```

Note the hyphen in `playlist-items`. `position` is zero-based, so this puts it first.

## 5. Point the playlist at the screen

Assignment goes through [labels](/docs/labels/), and every screen has one matching its own name, created when it was paired.

```bash
# the screen's own label
curl -s -H "Authorization: Token $API_TOKEN" "$API/labels?human_name=eq.Reception"

# attach the playlist to it
curl -s -X POST -H "Authorization: Token $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"label_id": "LABEL_ID", "playlist_id": "PLAYLIST_ID"}' \
  "$API/labels/playlists"
```

The screen picks it up on its next check-in, usually within a minute or two.

## The one query worth learning

`select` embeds related records, which is the difference between one request and a hundred:

```bash
curl -s -H "Authorization: Token $API_TOKEN" \
  "$API/playlists?select=title,playlist_items(duration,position,asset_id)"
```

Ask for the nested data rather than looping over ids.

## Where to go next

[The full v4.1 schema](https://developer.screenly.io/api_v4_1/) has every endpoint, field, and filter.

[Versions](/docs/developers/api/reference/versions/) covers what is in v4.1 that is not in v4, and the v3 shutdown on 1 October 2026.