Documentation
Base URL: https://api.example.com. All responses are JSON.
Quick start
- Create an account at /register. You get 100 free points.
- Open API keys and create a key. Copy it right away: it is shown only once.
- Make a request:
curl https://api.example.com/v1/movie/550 \
-H "Authorization: Bearer YOUR_API_KEY"Response:
{
"success": true,
"data": {
"id": 550,
"name": "Fight Club",
"type": "movie",
"url": "https://stream.example.com/movie/550.m3u8",
"headers": {
"Origin": "https://example.com",
"Referer": "https://example.com/"
}
},
"usage": {
"cost": 5,
"remaining_points": 95
}
}
Authentication
Send your key in the Authorization header as a Bearer token. Keys start with kine_live_.
Authorization: Bearer kine_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are not accepted in the URL, because URLs end up in logs. Keep keys on your server. If a key leaks, revoke it on the API keys page; it stops working immediately.
Costs
| Request | Cost |
|---|---|
| Normal: Movie, TV | 5 points |
| Premium: Movie, TV (automatically determined by ID) | 10 points |
Premium discovery (GET /v1/premium), Watch history, Search history, Account balance | Free |
| Invalid input, wrong scope, bad key, source not found | Free (refunded) |
Points are reserved before upstream playback sources are resolved. If the title does not exist or the upstream source provider fails, points are refunded automatically and the response shows your balance. Each successful response includes usage.cost and usage.remaining_points.
Movie Playback Source
/v1/movie/{id}movie:read5 or 10 pointsid is a positive whole number (the TMDB movie ID).
curl https://api.example.com/v1/movie/27205 \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"success": true,
"data": {
"id": 27205,
"name": "Inception",
"type": "movie",
"url": "https://stream.example.com/movie/27205.m3u8",
"headers": {
"Origin": "https://example.com",
"Referer": "https://example.com/"
}
},
"usage": { "cost": 5, "remaining_points": 95 }
}
TV Playback Source
/v1/tv/{id}tv:read5 or 10 points/v1/tv/{id}/{season}/{episode}tv:read5 or 10 pointsRequest playback for a specific episode by passing season and episode in the URL.
curl https://api.example.com/v1/tv/1399/2/5 \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"success": true,
"data": {
"id": 1399,
"name": "Game of Thrones",
"type": "tv",
"season": 2,
"episode": 5,
"url": "https://stream.example.com/tv/1399.m3u8",
"headers": {
"Origin": "https://example.com",
"Referer": "https://example.com/"
}
},
"usage": { "cost": 5, "remaining_points": 95 }
}
Search History
/v1/search/historywatch:writeFreecurl -X POST https://api.example.com/v1/search/history \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "inception"}'
Nothing is saved if the user turned search history off in Settings. The response then says "saved": false.
Watch history
/v1/watchwatch:writeFreeCall it as playback progresses. One row is kept per movie or episode, and later calls update it. Progress of 90% or more (when duration is sent) marks the title as watched.
| Field | Type | Notes |
|---|---|---|
video_id | integer | Required. |
video_type | string | Required: movie or tv. |
video_name | string | Optional, shown in the dashboard. |
season, episode | integer | Required for tv. |
progress | integer | Seconds watched. |
duration | integer | Total seconds. Optional, used for the percentage. |
watched | boolean | Optional. Force the watched flag. |
curl -X POST https://api.example.com/v1/watch \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_id": 1399, "video_type": "tv", "video_name": "Game of Thrones",
"season": 1, "episode": 2, "progress": 600, "duration": 3000}'
Account balance
/v1/accountany keyFree{
"success": true,
"data": {
"points_balance": 95,
"costs": { "normal": 5, "premium": 10 },
"key": { "name": "My app", "prefix": "kine_live_ab12", "scopes": ["movie:read"] }
}
}
Other public endpoints
/v1/packagesno auth/healthno auth/versionno authErrors
Errors always look like this:
{
"success": false,
"error": {
"code": "INSUFFICIENT_POINTS",
"message": "Not enough points for this request",
"required": 5,
"remaining": 2
}
}
| HTTP | Code | Meaning |
|---|---|---|
| 401 | MISSING_API_KEY | No Bearer token was sent. |
| 401 | INVALID_API_KEY | The key does not exist. |
| 401 | API_KEY_REVOKED | The key was revoked. |
| 402 | INSUFFICIENT_POINTS | Balance is below the request cost. required and remaining are included. Nothing is charged. |
| 403 | INSUFFICIENT_SCOPE | The key lacks the scope this endpoint needs. |
| 403 | ACCOUNT_SUSPENDED | The account is suspended. |
| 404 | NOT_FOUND | No title with that ID. Points are returned. |
| 422 | VALIDATION_ERROR | A parameter is missing or malformed. The field property names it. |
| 429 | RATE_LIMITED | Too many requests. Wait for Retry-After seconds. |
| 502 | UPSTREAM_ERROR | The data provider failed. Points are returned. |
| 500 | INTERNAL_ERROR | Something went wrong on our side. Quote the X-Request-ID header if you contact support. |
Rate limits
Each key can make 60 requests per minute. Every response carries:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 57 X-RateLimit-Reset: 1767225600 (unix time when the window resets)
When you go over, you get 429 with a Retry-After header. Requests rejected by the rate limiter never cost points.
Scopes
| Scope | Allows |
|---|---|
movie:read | GET /v1/movie/{id} |
tv:read | GET /v1/tv/{id}, GET /v1/tv/{id}/{season}/{episode} |
premium:read | GET /v1/premium (discovery list) |
watch:write | POST /v1/watch, POST /v1/search/history |
Give each app a key with only the scopes it needs.
Payment webhooks
Payment providers notify the server at POST /v1/webhooks/payments. The request is accepted only if its signature is valid, so this endpoint is not for client use. Points are added only after the provider confirms the payment, never because the browser says so. Delivering the same event twice adds points once.
