guides

Set the program resolution and frame rate

Two calls: read what a studio is set to composite and encode at, and change it.

Which host. These endpoints are on dev only for now: https://api-dev.traxstreaming.live/v1 with a dev key. This page will say so when they reach production.

  1. GET /v1/studios/{id}/output — the format in three parts.
  2. PATCH /v1/studios/{id}/output — set the resolution and/or frame rate.

Read the GET first, and read all of it. The response deliberately does not collapse into one number, because one number would be wrong for most studios.

Three answers, not one

{
  "studioId": "…",
  "selected":  { "width": 2560, "height": 1440, "fpsNum": 60, "fpsDen": 1, "fps": 60 },
  "effective": { "width": 1920, "height": 1080, "fpsNum": 60, "fpsDen": 1, "fps": 60 },
  "caps": {
    "maxWidth": 1920,
    "maxHeight": 1080,
    "cappedBy": "twitch",
    "supported": [
      { "width": 1280, "height": 720,  "available": true,  "status": "available" },
      { "width": 1920, "height": 1080, "available": true,  "status": "available" },
      { "width": 2560, "height": 1440, "available": true,  "status": "available" },
      { "width": 3840, "height": 2160, "available": false, "status": "coming_soon" }
    ],
    "supportedFrameRates": [ { "num": 60000, "den": 1001, "fps": 59.94 }, "…" ]
  },
  "codec": "h264",
  "targetBitrateKbps": 6000,
  "appliesToNextGoLive": true
}

selected is what the operator picked. effective is what the next go-live will actually run. They differ here because Twitch ingests 1080p, and this studio has a Twitch destination enabled — so a 1440p selection airs at 1080p. caps.cappedBy names the platform responsible; it is an empty string when nothing is capping, which is the field to check rather than comparing maxWidth against the ladder top.

Show a user effective when answering "what am I streaming at". selected is their intent and can legitimately be higher than what airs today.

The cap is a fact about the studio's destination set right now, not a rule. Disable Twitch and the same selection airs at 1440p with no further write.

Frame rate is a rational

fpsNum / fpsDen, not a decimal. 59.94 is exactly 60000/1001, and rounding it to 59.94 or 60 produces drift the encoder cannot correct. The fps field is the decimal rendering for display; there is no way to write it, and sending one is a 400.

The accepted rates:

num / den reads as
24000 / 1001 23.976
24 / 1 24
25 / 1 25
30000 / 1001 29.97
30 / 1 30
50 / 1 50
60000 / 1001 59.94
60 / 1 60

Read them from caps.supportedFrameRates rather than hardcoding this table.

Setting it

curl -X PATCH https://api-dev.traxstreaming.live/v1/studios/$STUDIO/output \
  -H "Authorization: Bearer $TRAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"width":1920,"height":1080,"fpsNum":60000,"fpsDen":1001}'

A PATCH: send only what you are changing. A resolution-only body leaves the frame rate exactly where it was, which is what lets a phone drop a studio to 720p without disturbing a 59.94 that someone chose deliberately.

The pairs are pairs. width and height move together and fpsNum and fpsDen move together; sending half of either is a 400 naming the field. Half a resolution is not a resolution, and a numerator with no denominator is not a frame rate.

The response is the whole re-resolved view — the same shape GET returns — so you never have to model the cap chain yourself to know what your change did.

It rejects; it does not clamp

An off-ladder resolution is a 400 that names the field and lists what is allowed:

{
  "error": {
    "code": "invalid_request",
    "message": "width: 1600x900 is not a supported program resolution; supported: 1280x720, 1920x1080, 2560x1440"
  }
}

This is the point of the endpoint. Offer caps.supported in your picker and you will never see this error; construct a resolution yourself and you will find out immediately rather than at the next go-live.

Coming soon resolutions

Each rung in caps.supported carries an available flag and a status string. A rung with "available": false ("status": "coming_soon") is part of the ladder but not offered yet — 4K (3840x2160) reads as coming soon today. Render it in your picker greyed out rather than hiding it, and do not let it be selected.

Selecting a coming-soon resolution is rejected the same way an off-ladder one is — a 400 that names the reason:

{
  "error": {
    "code": "invalid_request",
    "message": "width: 4K (3840x2160) is coming soon and not yet available"
  }
}

While 4K is coming soon it is excluded from the destination soft-cap too, so caps.maxWidth tops out at 2560x1440 even with only 4K-capable destinations (e.g. YouTube) enabled. When 4K ships, its rung flips to "available": true and the cap lifts with no change to your integration.

The one thing reported rather than enforced is the destination cap. Setting 1440p on a Twitch-only studio succeeds, and the response tells you it will air at 1080p. Refusing it would delete the operator's intent to punish a condition that lifts the moment they disable Twitch. (This is distinct from the coming-soon gate above, which IS enforced on write — a coming-soon resolution is a roadmap gap, not a per-destination condition.)

When it takes effect

appliesToNextGoLive is always true, and it is in the payload rather than only in this page because it is easy to get wrong. The encoder does not renegotiate resolution or frame rate mid-session: a studio that is live right now keeps the format it started with until it goes offline and live again.

Patching a live studio is safe — it persists and does not disturb the session on air — but it will not change what viewers are watching until the next go-live. If your UI can be open during a broadcast, say so there.

Scopes

studios:read to read, studios:write to change. Both are in the default scope set for a new key.

The format rides the studio scopes rather than having a pair of its own because changing it does not move what is on air — it takes effect at the next go-live, the same blast radius renaming a studio has. That is a different power from canvas:write, which puts a tile in front of every viewer the instant it lands.

  • Control the canvas from your app — the canvas read reports the same format as output, in the same shape. The canvas coordinate space is a fixed 1920x1080 for every studio and is not the broadcast resolution; scale your miniature by the canvas and label the stream by the output.