{
  "info": {
    "_postman_id": "1e61a02d-35fd-4ed0-924b-vidsmaller-v1",
    "name": "VidSmaller Video Compression API",
    "description": "Public upload-limit and size-estimate endpoints plus authenticated target-size video compression jobs. Import this collection, set `apiKey` for job requests, and see https://vidsmaller.com/api-docs.\n\nThe public requests need no account. Authenticated compression requests spend VidSmaller credits. Creating a job from a public URL or an uploaded file is intentionally a manual action.\n\nWebhook requests are signed with `x-vidsmaller-timestamp` and `x-vidsmaller-signature`. Verify HMAC-SHA256 over `timestamp + '.' + rawBody` using the webhook secret shown when the API key is created.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "noauth"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://vidsmaller.com",
      "type": "string"
    },
    {
      "key": "apiKey",
      "value": "",
      "type": "secret"
    },
    {
      "key": "jobId",
      "value": "",
      "type": "string"
    },
    {
      "key": "uploadUrl",
      "value": "",
      "type": "string"
    },
    {
      "key": "uploadToken",
      "value": "",
      "type": "secret"
    },
    {
      "key": "uploadContentType",
      "value": "video/mp4",
      "type": "string"
    },
    {
      "key": "uploadContentLength",
      "value": "1048576",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Public data — no API key",
      "description": "Free read-only endpoints, limited to 600 requests per hour per IP.",
      "item": [
        {
          "name": "List platform upload limits",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/platforms",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "platforms"]
            },
            "description": "Returns sourced platform upload limits, safe compression targets, source URLs, and verification dates."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Returns the v1 platform list', () => {",
                  "  pm.expect(body.ok).to.eql(true);",
                  "  pm.expect(body.api).to.eql('v1');",
                  "  pm.expect(body.platforms).to.be.an('array').that.is.not.empty;",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Estimate upload fit — GET",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/estimate?sizeMb=150&durationSeconds=596&platform=discord",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "estimate"],
              "query": [
                { "key": "sizeMb", "value": "150", "description": "Source size in MiB" },
                { "key": "durationSeconds", "value": "596" },
                { "key": "platform", "value": "discord" }
              ]
            },
            "description": "Checks whether a 150 MiB, 596-second video fits Discord and returns the safe target plus quality guidance."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Recommends compression to Discord safe target', () => {",
                  "  pm.expect(body.ok).to.eql(true);",
                  "  pm.expect(body.verdict).to.eql('compress_recommended');",
                  "  pm.expect(body.target.platform).to.eql('discord');",
                  "  pm.expect(body.target.safeTargetMb).to.be.a('number');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Estimate custom target — POST",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json", "type": "text" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"sizeMb\": 500,\n  \"durationSeconds\": 900,\n  \"targetMb\": 100,\n  \"settings\": {\n    \"mode\": \"target_size\",\n    \"targetSizeMb\": 100,\n    \"codec\": \"libx264\",\n    \"maxResolution\": \"1920:1080\"\n  }\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/estimate",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "estimate"]
            },
            "description": "Estimates a 500 MiB source compressed to a custom 100 MiB ceiling."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Uses the explicit target', () => {",
                  "  pm.expect(body.ok).to.eql(true);",
                  "  pm.expect(body.target.targetSource).to.eql('explicit');",
                  "  pm.expect(body.target.safeTargetMb).to.eql(100);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Authenticated compression jobs",
      "description": "Set the `apiKey` collection variable before using these requests. Creating a job spends account credits.",
      "auth": {
        "type": "bearer",
        "bearer": [
          { "key": "token", "value": "{{apiKey}}", "type": "string" }
        ]
      },
      "item": [
        {
          "name": "1A. Create signed upload URL",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json", "type": "text" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"filename\": \"input.mp4\",\n  \"file_size\": 1048576,\n  \"content_type\": \"video/mp4\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/video/uploads",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "video", "uploads"]
            },
            "description": "Change filename, file_size, and content_type to match the local video exactly. The test script stores the signed URL and upload token."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 201', () => pm.response.to.have.status(201));",
                  "const body = pm.response.json();",
                  "if (body.ok) {",
                  "  pm.collectionVariables.set('uploadUrl', body.upload.url);",
                  "  pm.collectionVariables.set('uploadToken', body.upload_token);",
                  "  pm.collectionVariables.set('uploadContentType', body.upload.headers['content-type']);",
                  "  pm.collectionVariables.set('uploadContentLength', body.upload.headers['content-length']);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "1B. PUT local file to signed URL",
          "request": {
            "auth": { "type": "noauth" },
            "method": "PUT",
            "header": [
              { "key": "Content-Type", "value": "{{uploadContentType}}", "type": "text" },
              { "key": "Content-Length", "value": "{{uploadContentLength}}", "type": "text" }
            ],
            "body": {
              "mode": "file",
              "file": { "src": "/path/to/input.mp4" }
            },
            "url": {
              "raw": "{{uploadUrl}}",
              "host": ["{{uploadUrl}}"]
            },
            "description": "Choose the exact file described in step 1A before sending. The signed upload URL expires after one hour."
          },
          "response": []
        },
        {
          "name": "1C. Create job from uploaded file",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json", "type": "text" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"upload_token\": \"{{uploadToken}}\",\n  \"target_size_mb\": 25,\n  \"codec\": \"h264\",\n  \"max_resolution\": \"1080p\",\n  \"output_format\": \"mp4\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/video/jobs",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "video", "jobs"]
            },
            "description": "Creates a target-size job after the signed PUT upload completes. This request spends credits."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 201', () => pm.response.to.have.status(201));",
                  "const body = pm.response.json();",
                  "if (body.ok) pm.collectionVariables.set('jobId', body.job.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "2. Create job from public URL",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json", "type": "text" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"source_url\": \"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_1MB.mp4\",\n  \"duration_seconds\": 10,\n  \"target_size_mb\": 0.8,\n  \"codec\": \"h264\",\n  \"max_resolution\": \"720p\",\n  \"output_format\": \"mp4\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/video/jobs",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "video", "jobs"]
            },
            "description": "Creates a job from a publicly reachable direct video URL. This request spends credits. Replace the sample URL and target before sending."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 201', () => pm.response.to.have.status(201));",
                  "const body = pm.response.json();",
                  "if (body.ok) pm.collectionVariables.set('jobId', body.job.id);"
                ]
              }
            }
          ]
        },
        {
          "name": "3. Get job status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/video/jobs/{{jobId}}",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "video", "jobs", "{{jobId}}"]
            },
            "description": "Poll until status is completed, failed, or expired. A completed job contains an authenticated download_url."
          },
          "response": [],
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('HTTP 200', () => pm.response.to.have.status(200));",
                  "const body = pm.response.json();",
                  "pm.test('Returns a job', () => pm.expect(body.job.id).to.eql(pm.collectionVariables.get('jobId')));"
                ]
              }
            }
          ]
        },
        {
          "name": "4. Download completed result",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/video/jobs/{{jobId}}/download",
              "host": ["{{baseUrl}}"],
              "path": ["api", "v1", "video", "jobs", "{{jobId}}", "download"]
            },
            "description": "Returns a 302 redirect to a short-lived signed result URL. The same API key that created the job is required."
          },
          "response": []
        }
      ]
    }
  ]
}
