Partner API v1
Create and monitor CTV campaign orders, upload creative and audiences, and retrieve delivery reports. Everything in this document is also what the partner portal uses.
AuthenticationRate limits & test modeWorkflowUploadsOrdersReportsErrorsWhat stays manual
Authentication
Base URL: https://topstreammedia.com/api/v1. Every request carries a partner API key as a bearer token. Keys are created and revoked by you in the portal; they are shown once and never emailed.
Authorization: Bearer tsm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json
A key only reaches orders and reports that belong to your partner account. Revoking a key or suspending a partner takes effect on the next request.
Rate limits & test mode
60 requests per minute per key. Responses include x-ratelimit-limit and x-ratelimit-remaining; over the limit you get 429 with retry-after: 60.
Test mode. Either create a test key (prefix tsm_test_) or send "test": true on an order. Test orders are validated and stored, appear in listings only with include_test=true, and are never forwarded, reviewed, or billed. No files are moved to Drive for test orders.
Workflow
- Upload files first (optional):
POST /uploadsreturns one upload URL per file. PUT each file's bytes to its URL. Keep the returnedupload_token. - Create the order:
POST /orderswith the campaign fields and theupload_token. You get back anorder_idlikeTS-20261027-K7QXwith statusreceived. - Top Stream reviews every live order personally before anything launches. Status moves
received → under_review → approved → live → completed(orpaused/cancelled). PollGET /orders/:idfor status. - Mid-flight changes:
PATCH /orders/:idwith the fields to change. Each change is stored as a numbered revision and sent to Top Stream for review. You may also submit a new order for the same campaign and reference it withexternal_campaign_id. - Reports: when a monthly report is published, it appears in
GET /reportswith headline metrics and a download link for the PDF.
Uploads
Declares files and returns direct-upload URLs. Up to 50 files per request; any size. File bytes never pass through the API — you PUT them straight to the storage URL.
{
"upload_token": null, // omit on first call; reuse to add files to the same batch
"files": [
{ "name": "spot-30.mp4", "size": 48211233, "type": "video/mp4", "group": "campaign" },
{ "name": "300x250.png", "size": 51233, "type": "image/png", "group": "retargeting" },
{ "name": "audience.csv", "size": 8250042, "type": "text/csv", "group": "audience" }
]
}
Response:
{
"upload_token": "A1b2…",
"uploads": [ { "name": "spot-30.mp4", "upload_url": "https://…", "method": "PUT" }, … ],
"expires_in": 3600
}
Then, for each file: PUT {upload_url} with header Content-Type: {type} and the raw bytes as the body. A 2xx response means the file is stored. Upload URLs expire after one hour.
group is one of campaign (video spots, customer data), retargeting (display images), audience (household/IP lists such as a GrōVault export).
Orders
Creates an order. Fields mirror the Top Stream order form.
| Field | Required | Notes |
|---|---|---|
| company_name | yes | Advertiser's company name. Creates/links an advertiser under your account. |
| contact_name | yes | First and last name. |
| yes | Advertiser or agency contact email. | |
| phone | no | At least 10 digits if sent. |
| campaign_name | yes | |
| start_date | yes | YYYY-MM-DD; at least 3 business days from today. |
| end_date | yes | YYYY-MM-DD; on or after start_date. |
| spot_lengths | no | Array or comma list of 15, 30, 60. |
| geography | yes | ZIP codes, DMAs, cities, geo-fence descriptions. Free text. |
| target_audience | yes | Who the buyer is. Free text. |
| platforms_inventory | yes | Apps, channels, programming, sports, shows. Free text. |
| audience_ref | no | Identifier or URL of a supplied audience (e.g. GrōVault audience id). Upload the file under group audience. |
| campaign_budget | yes | USD, positive number. Total CTV spend for the flight. |
| top_stream_cpm_rate | yes | USD. The CPM agreed with Top Stream for this order. |
| frequency_cap | no | Whole number. Caps are limits, not goals. |
| frequency_cap_periods | if cap set | Array of Daily, Weekly, Monthly, Campaign. |
| retargeting | no | true to add search & social retargeting. |
| retargeting_budget | if retargeting | USD, separate from campaign_budget. |
| tracking_pixels_needed | no | boolean |
| first_party_data | no | boolean — you are supplying first-party or target data. |
| campaign_notes | no | Landing page URL, offers, anything else. |
| external_campaign_id | no | Your own campaign id, returned on every read. Use it to tie several orders to one campaign. |
| upload_token | no | From /uploads; attaches the uploaded files. |
| test | no | true for a test order. |
POST /api/v1/orders
{
"company_name": "Precision Garage Door of Buffalo",
"contact_name": "Jane Smith",
"email": "jane@example.com",
"phone": "716-555-0100",
"campaign_name": "November garage door install",
"start_date": "2026-11-03",
"end_date": "2026-11-30",
"spot_lengths": [15, 30],
"geography": "ZIPs 14221, 14225, 14227, 14224, 14127; Buffalo DMA",
"target_audience": "Homeowners 36–64, HHI $75K+, homes 20+ years old",
"platforms_inventory": "Roku, Fire TV, Hulu, Peacock, Paramount+, YouTube TV; sports and news",
"audience_ref": "grovault:buffalo-ctv-2026-10-15",
"campaign_budget": 7500,
"top_stream_cpm_rate": "",
"frequency_cap": 10,
"frequency_cap_periods": ["Monthly"],
"retargeting": true,
"retargeting_budget": 500,
"external_campaign_id": "adc-88213",
"upload_token": "A1b2…"
}
Response 201 is the order object:
{
"order_id": "TS-20261027-K7QX",
"status": "received",
"is_test": false,
"revision": 1,
"source": "api",
"external_campaign_id": "adc-88213",
"advertiser": { "company_name": "…", "contact_name": "…", "email": "…", "phone": "…" },
"campaign": { "campaign_name": "…", "start_date": "2026-11-03", "end_date": "2026-11-30", "spot_lengths": "15, 30" },
"targeting": { "geography": "…", "target_audience": "…", "platforms_inventory": "…", "audience_ref": "…" },
"budget": { "campaign_budget": 7500, "top_stream_cpm_rate": "", "frequency_cap": 10, "frequency_cap_periods": ["Monthly"] },
"retargeting":{ "enabled": true, "budget": 500 },
"creative": { "tracking_pixels_needed": false, "first_party_data": false, "campaign_notes": null },
"files": [ { "id": "…", "group": "campaign", "name": "spot-30.mp4", "size": 48211233, "mime": "video/mp4" } ],
"revisions": [],
"created_at": "2026-10-27T19:12:03Z",
"updated_at": "2026-10-27T19:12:03Z"
}
Lists your orders, newest first. Returns { "orders": [...], "limit", "offset" }.
One order with files and revision history.
Mid-flight change. Send only the fields that change, plus an optional note and an optional upload_token for new files. Creates revision n+1, sets status to under_review (unless still received) and notifies Top Stream. Not allowed once an order is completed or cancelled. To pause, send {"note": "Pause from 2026-11-10"} together with any field change, or submit end_date; Top Stream confirms and sets the status.
PATCH /api/v1/orders/TS-20261027-K7QX
{ "campaign_budget": 9000, "end_date": "2026-12-07", "note": "Extend one week, add $1,500." }
Order status
| Status | Meaning |
|---|---|
| received | Stored; awaiting Top Stream review. |
| under_review | Being reviewed (also after a revision). |
| approved | Confirmed; scheduled for the flight dates. |
| live | Delivering. |
| paused | Delivery paused at your request or Top Stream's. |
| completed | Flight finished. |
| cancelled | Will not run. |
Reports
Published reports, newest first. Each has metrics with the headline numbers from the monthly CTV report:
{
"reports": [ {
"id": "…", "order_id": "TS-20261027-K7QX", "campaign_name": "…", "company_name": "…",
"period_start": "2026-11-01", "period_end": "2026-11-30", "title": "CTV report — November 2026",
"metrics": {
"impressions": 233704, "ctv_impressions": 228732, "ott_impressions": 4972,
"unique_reach": 17977, "frequency": 13, "vcr": 99.9, "hours_watched": 1945,
"website_visits": 1104, "retargeting_impressions": 116039, "retargeting_clicks": 413
},
"published_at": "2026-12-03T14:00:00Z"
} ]
}
Same object plus download_url, a link to the PDF valid for 10 minutes.
Definitions. unique_reach is distinct households served, with each exposure resolved to the streaming device so a household watching across several apps counts once. frequency is average impressions per reached household. Both are observed, not modeled. website_visits are observed visits from the CTV campaign. Reports are monthly and published after the period closes.
Errors
| Code | Meaning |
|---|---|
| 400 | Malformed JSON. |
| 401 | Missing, invalid or revoked API key. |
| 403 | Partner account suspended. |
| 404 | Unknown order or report. |
| 409 | Order can no longer be changed. |
| 422 | Validation failed. Body: { "error": "…", "details": [ { "field": "start_date", "message": "…" } ] } |
| 429 | Rate limit exceeded. |
| 500 | Internal error; retry with backoff. |
What stays manual
Top Stream is a managed service. The API covers ordering, file delivery, change requests, status and reporting. The following are handled by Top Stream on your behalf and are not API-controlled: campaign approval, pacing, pause and resume, creative trafficking into the platform, and line-item structure. Delivery data is monthly; there are no daily or impression-level feeds at this time.
Questions: tim@topstreammedia.com