Create media
Starts a media upload (video, cover or image: the photo of an image post, JPEG only). With simple: true and up to 50 MB, send the whole file at once with PUT /v1/media/:id/content; otherwise upload it in parts and finish with POST /v1/media/:id/complete.
Authorization: Bearer praeko_… and X-Praeko-Contract: en. Always send both.Legacy Portuguese alias (still answered, marked as deprecated): /v1/midia.
Parameters
| Name | In | Description |
|---|---|---|
Idempotency-Key |
header | Idempotency key (1 to 255 visible characters, for example a UUID). Repeating the SAME request with the same key within 24 h returns the stored response (with Idempotent-Replayed: true) without running it again; a different request with the same key returns 422 idempotency_key_reuse; a request still in progress returns 409 idempotency_in_progress. 5xx errors are not stored. The key is scoped per workspace and per route. See Retry a request safely. |
Request body
JSON. Required fields must be sent; the others can be left out.
| Field | Type | Description |
|---|---|---|
type required |
string | Media type: video, cover or image (the photo of an image post). Values: video, cover, image. |
name required |
string | File name (stored with safe characters only). 1 to 200 characters. |
mime required |
string | File type. Video: video/mp4 or video/quicktime. Cover: image/png or image/jpeg. Image: image/jpeg only (Studio exports sRGB JPEG; the server does not convert). Values: video/mp4, video/quicktime, image/png, image/jpeg. |
size required |
integer | Size in bytes. Video up to 5 GiB; cover and image up to 20 MiB (Instagram takes photos up to 8 MB: that is checked when the post is created). 1 to 5368709120. |
simple (optional) |
boolean | true sends the whole file in one PUT /v1/media/:id/content (only up to 50 MB). Without it, the upload is done in parts. Default: false. |
sha256 (optional) |
string | SHA-256 hash of the file, if you want it stored. |
{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}Example
curl -X POST "https://praeko.app/v1/media" \
-H "Authorization: Bearer $PRAEKO_KEY" \
-H "X-Praeko-Contract: en" \
-H "Content-Type: application/json" \
-d '{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}'const res = await fetch("https://praeko.app/v1/media", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.PRAEKO_KEY}`,
"X-Praeko-Contract": "en",
"Content-Type": "application/json",
},
body: JSON.stringify({
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}),
});
console.log(res.status, await res.json());import { Praeko } from "@praeko/sdk";
const praeko = new Praeko({ url: "https://praeko.app", key: process.env.PRAEKO_KEY });
const resultado = await praeko.media.create({
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
});
console.log(resultado);import os
import requests
resp = requests.post(
"https://praeko.app/v1/media",
headers={
"Authorization": f"Bearer {os.environ['PRAEKO_KEY']}",
"X-Praeko-Contract": "en",
},
json={
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": True,
},
)
print(resp.status_code, resp.json())<?php
$corpo = <<<'JSON'
{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}
JSON;
$ch = curl_init('https://praeko.app/v1/media');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("PRAEKO_KEY"),
'X-Praeko-Contract: en',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $corpo,
]);
$resposta = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $resposta, PHP_EOL;
curl_close($ch);package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
body := strings.NewReader(`{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}`)
req, err := http.NewRequest("POST", "https://praeko.app/v1/media", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PRAEKO_KEY"))
req.Header.Set("X-Praeko-Contract", "en")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}require "net/http"
require "uri"
uri = URI("https://praeko.app/v1/media")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV['PRAEKO_KEY']}"
request["X-Praeko-Contract"] = "en"
request["Content-Type"] = "application/json"
request.body = <<~'JSON'
{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}
JSON
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.code, response.bodyusing System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://praeko.app/v1/media");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("PRAEKO_KEY"));
request.Headers.Add("X-Praeko-Contract", "en");
request.Content = new StringContent("""
{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
String body = """
{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://praeko.app/v1/media"))
.header("Authorization", "Bearer " + System.getenv("PRAEKO_KEY"))
.header("X-Praeko-Contract", "en")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}http POST "https://praeko.app/v1/media" \
"Authorization: Bearer $PRAEKO_KEY" \
"X-Praeko-Contract: en" \
"Content-Type: application/json" \
--raw='{
"type": "video",
"name": "day-06-the-failing-test.mp4",
"mime": "video/mp4",
"size": 48230114,
"simple": true
}'npm install @praeko/sdk (see SDK and CLI).Response
| Success status | When |
|---|---|
200 |
Success. |
{
"id": "01KA3VQ7M2T9XE4HB8N5RZ6C0D",
"state": "uploading",
"uploadHint": "PUT /v1/media/01KA3VQ7M2T9XE4HB8N5RZ6C0D/content"
}With simple: true the response is just id, state and uploadHint. Without it (upload in parts) it also includes minPartSize (5242880), maxPartSize (99614720) and suggestedPartSize (16777216), in bytes, and uploadHint points to PUT /v1/media/:id/parts/<n>.
Errors
| Status | code | When |
|---|---|---|
| 400 | empty_body |
The request body is empty. |
| 400 | invalid_json |
The request body is not a JSON object. |
| 413 | body_too_large |
The request body is larger than 1 MB. |
| 422 | invalid_field |
Invalid type, mime, name or size. |
| 422 | file_too_large |
simple: true with more than 50 MB. |
| 422 | invalid_image |
An image media item whose file is not a JPEG (checked on upload; for uploads in parts, on complete). |
| 422 | idempotency_key_reuse |
The Idempotency-Key was already used with a different request (different method, path, query or body). |
| 409 | idempotency_in_progress |
A request with the same Idempotency-Key is still in progress (retry in a moment). |
| 409 | idempotency_response_too_large |
The original response was larger than 256 KB and was not stored; fetch the resource with GET instead. |
Errors on any route
| Status | code | When |
|---|---|---|
| 401 | unauthorized |
The Authorization: Bearer … header is missing. |
| 401 | invalid_api_key |
The key does not exist. |
| 401 | revoked_api_key |
The key has been revoked. |
| 401 | expired_api_key |
The key has expired. |
| 403 | forbidden |
The key's scope is not enough for this route (see the scope badge). |
| 429 | rate_limited |
The per-minute request limit was exceeded. |
The body of every error is { "error": { "code", "message", "details"? } }. See all error codes.
Try it
The live test uses your key and your account. It stays off when you are not signed in.