API reference
Every v1 endpoint, generated from the OpenAPI 3.1 spec. Read on the left, run live against your account on the right. Base URL https://postreef.com.
/v1/extractionsSubmit an extraction
Starts an extraction for a public video URL. Credits are debited up-front (a flat base fee plus download rates for the requested artifacts, plus per-second AI rates when a schema is set; use `POST /v1/probe` for the exact quote first) and refunded in full if the extraction fails. When the duration can't be determined up-front, a worst-case duration of 300s is debited and the difference is refunded on completion. If an identical extraction (same URL, schema and inputs) completed recently, the result is reused: the response has `cached: true` semantics on subsequent reads and `creditsDebited: 0`. Pass `force: true` to skip the cache and re-run. Rate limit: 6 requests per minute. At most 2 extractions run per account at once. Submissions past that limit are accepted with status `queued` (credits debited up-front) and dispatched automatically when a slot frees.
Parameters
| Field | Type | Description |
|---|---|---|
| Idempotency-Key (header) | string | Any unique string (e.g. a UUID). Retrying a submit with the same key and the same request body returns the original extraction with status 200 instead of creating a new one. Reusing a key with a *different* body returns 409 conflict. |
Request body
| Field | Type | Description |
|---|---|---|
| urlrequired | string | Public video URL (YouTube, Shorts, TikTok, Instagram Reels, …). |
| inputs | "transcript" | "comments" | "audio" | "video"[] | Which modalities feed the AI extraction. Defaults to ["transcript","comments"]. Determines the price. |
| schemaId | string | Id of a predefined schema (e.g. `Recipe`) or one of your saved schemas. Mutually exclusive with `schema`. |
| schema | object | Inline JSON Schema (max 100KB) describing the structured object you want back. Omit both `schema` and `schemaId` for a download-only run (no AI, no per-second AI charge). |
| prompt | string | Extraction guidance sent to the model alongside the schema. It steers how the video is read into the schema (source priority, what to omit, common pitfalls). For a predefined `schemaId` this overrides that schema's built-in prompt; for an inline `schema` it's the prompt that pairs with it. Max 20KB. Part of the cache key, so an edited prompt re-runs instead of reusing a cached result. Ignored on download-only runs. |
| auto | boolean | Auto mode: start with the cheapest inputs (text), judge the result, and only climb to audio/video if quality is below threshold. `inputs` becomes the ceiling the climb may reach. Requires `maxSpendCredits`. Charged the ceiling at submit, refunded down to the rung actually used. Auto runs reuse a recent result on the same URL, schema and model when its inputs fit inside your `inputs` ceiling (free, `creditsDebited: 0`); pass `force: true` to re-run instead. |
| force | boolean | Skip the result cache and run the full pipeline even when a recent identical extraction exists. The run is billed as fresh. Default false. |
| maxSpendCredits | number | Spend ceiling for an auto run, in credits. Required when `auto` is true. Must be at least the cost of the cheapest rung. |
| policy | "strict" | "fallback" | "best-effort" | What to do when a requested input isn't available for this video (e.g. no comments): `strict` fails the run, `fallback` (default) proceeds with reduced inputs, `best-effort` silently drops the missing input. |
| parts | "transcript" | "comments" | "audio" | "video"[] | Download-only runs (no schema): which artifacts to fetch. Omit for everything. Metadata, thumbnail and description are always included. Invalid alongside `schema`/`schemaId`. |
| webhookUrl | string | Per-run webhook override: an https URL (public hosts only) that receives `extraction.completed` / `extraction.failed` for this run in addition to your registered endpoints. Signed with your first registered endpoint's secret when one exists; unsigned otherwise. |
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"inputs": [
"transcript",
"comments"
],
"schemaId": "Recipe",
"webhookUrl": "https://example.com/hooks/postreef"
}Response fields
| Field | Type | Description |
|---|---|---|
| idrequired | string | Extraction id, used for polling, results and files. |
| statusrequired | "queued" | "pending" | "running" | "complete" | "failed" | |
| urlrequired | string | |
| creditsDebitedrequired | integer | Credits debited up-front. 0 when the result was served from a recent identical extraction. |
| createdAtrequired | string |
Responses
200 Idempotent replay: the Idempotency-Key matched a previous submit with the same body, so the original extraction is returned.
201 Extraction created and started.
{
"id": "run_8f3a2b1c",
"status": "pending",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"creditsDebited": 127,
"createdAt": "2026-06-11T10:15:00.000Z"
}400 Invalid request: missing/invalid url (`invalid_url`), unsupported platform (`unsupported_url`), invalid options or webhookUrl (`invalid_request`).
401 Missing or invalid API key.
402 Insufficient credits (`insufficient_credits`). `details.balance` and `details.cost` carry your current balance and the quoted cost in credits.
404 Referenced schemaId not found.
409 Idempotency-Key was already used with a different request body (`conflict`).
429 `rate_limited` (per-minute window exceeded, includes Retry-After).
/v1/extractionsList extractions
Lists your extractions, newest first, with cursor pagination. Pass `next_cursor` from a previous page as `cursor` to fetch the next page.
Parameters
| Field | Type | Description |
|---|---|---|
| limit (query) | integer | Page size, 1–100. |
| cursor (query) | string | Opaque cursor from the previous page's `next_cursor`. Do not construct it yourself. |
Response fields
| Field | Type | Description |
|---|---|---|
| datarequired | Extraction[] | |
| has_morerequired | boolean | |
| next_cursorrequired | string | null | Pass as `cursor` to fetch the next page. Null on the last page. |
Responses
200 One page of extractions.
{
"data": [
{
"id": "run_8f3a2b1c",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"status": "complete",
"createdAt": "2026-06-11T10:15:00.000Z",
"completedAt": "2026-06-11T10:16:42.000Z",
"creditsDebited": 127,
"cached": false,
"outcome": "ok"
}
],
"has_more": false,
"next_cursor": null
}400 Invalid limit or cursor.
401 Missing or invalid API key.
429 Rate limit exceeded.
/v1/extractSubmit an extraction (alias)
Exact alias of `POST /v1/extractions`, with the same request, responses, rate limit and Idempotency-Key support.
Request body
| Field | Type | Description |
|---|---|---|
| urlrequired | string | Public video URL (YouTube, Shorts, TikTok, Instagram Reels, …). |
| inputs | "transcript" | "comments" | "audio" | "video"[] | Which modalities feed the AI extraction. Defaults to ["transcript","comments"]. Determines the price. |
| schemaId | string | Id of a predefined schema (e.g. `Recipe`) or one of your saved schemas. Mutually exclusive with `schema`. |
| schema | object | Inline JSON Schema (max 100KB) describing the structured object you want back. Omit both `schema` and `schemaId` for a download-only run (no AI, no per-second AI charge). |
| prompt | string | Extraction guidance sent to the model alongside the schema. It steers how the video is read into the schema (source priority, what to omit, common pitfalls). For a predefined `schemaId` this overrides that schema's built-in prompt; for an inline `schema` it's the prompt that pairs with it. Max 20KB. Part of the cache key, so an edited prompt re-runs instead of reusing a cached result. Ignored on download-only runs. |
| auto | boolean | Auto mode: start with the cheapest inputs (text), judge the result, and only climb to audio/video if quality is below threshold. `inputs` becomes the ceiling the climb may reach. Requires `maxSpendCredits`. Charged the ceiling at submit, refunded down to the rung actually used. Auto runs reuse a recent result on the same URL, schema and model when its inputs fit inside your `inputs` ceiling (free, `creditsDebited: 0`); pass `force: true` to re-run instead. |
| force | boolean | Skip the result cache and run the full pipeline even when a recent identical extraction exists. The run is billed as fresh. Default false. |
| maxSpendCredits | number | Spend ceiling for an auto run, in credits. Required when `auto` is true. Must be at least the cost of the cheapest rung. |
| policy | "strict" | "fallback" | "best-effort" | What to do when a requested input isn't available for this video (e.g. no comments): `strict` fails the run, `fallback` (default) proceeds with reduced inputs, `best-effort` silently drops the missing input. |
| parts | "transcript" | "comments" | "audio" | "video"[] | Download-only runs (no schema): which artifacts to fetch. Omit for everything. Metadata, thumbnail and description are always included. Invalid alongside `schema`/`schemaId`. |
| webhookUrl | string | Per-run webhook override: an https URL (public hosts only) that receives `extraction.completed` / `extraction.failed` for this run in addition to your registered endpoints. Signed with your first registered endpoint's secret when one exists; unsigned otherwise. |
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"inputs": [
"transcript",
"comments"
],
"schemaId": "Recipe",
"webhookUrl": "https://example.com/hooks/postreef"
}Response fields
| Field | Type | Description |
|---|---|---|
| idrequired | string | Extraction id, used for polling, results and files. |
| statusrequired | "queued" | "pending" | "running" | "complete" | "failed" | |
| urlrequired | string | |
| creditsDebitedrequired | integer | Credits debited up-front. 0 when the result was served from a recent identical extraction. |
| createdAtrequired | string |
Responses
201 Extraction created and started.
/v1/extractions/{id}Get an extraction
Returns the current state of one extraction, including a coarse `phase` (`queued`, `processing`, `done`, `failed`). Poll this (or `/result`) until the status is terminal, or skip polling entirely with a webhook.
Parameters
| Field | Type | Description |
|---|---|---|
| id (path)required | string | Extraction id returned at submit time. |
Response fields
| Field | Type | Description |
|---|---|---|
| idrequired | string | |
| urlrequired | string | |
| statusrequired | "queued" | "pending" | "running" | "complete" | "failed" | |
| createdAtrequired | string | |
| completedAtrequired | string | null | |
| creditsDebitedrequired | integer | |
| cachedrequired | boolean | True when the result was reused from a recent identical extraction (no credits were debited). |
| outcome | "ok" | "no_match" | "uncertain" | "null" | Content-match verdict for an AI run. `ok` means the content matched the schema and `extraction` holds the data. `no_match` means the content is about something else, with a null `extraction`. `uncertain` means the inputs were too sparse to decide, also with a null `extraction`. Null on download-only runs and runs from before this field existed. |
| verdictReason | string | One-sentence explanation of the content-match verdict, present only when `outcome` is `no_match` or `uncertain`. |
| error | string | Present only when status is `failed`. |
| phaserequired | "queued" | "processing" | "done" | "failed" | Coarse progress phase derived from status. |
Responses
200 The extraction.
{
"id": "run_8f3a2b1c",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"status": "complete",
"createdAt": "2026-06-11T10:15:00.000Z",
"completedAt": "2026-06-11T10:16:42.000Z",
"creditsDebited": 127,
"cached": false,
"outcome": "ok",
"phase": "done"
}401 Missing or invalid API key.
404 No extraction with this id belongs to you.
429 Rate limit exceeded.
/v1/extractions/{id}/resultGet an extraction's result
Returns the final result. While the extraction is still queued or processing, responds **202** with `{ "status": "queued" | "processing" }` and keep polling. Once complete, responds 200 with the summary (metadata + available file names) and, when an AI schema was supplied, the structured `extraction` object conforming to it. A failed extraction responds 200 with `status: "failed"` and a human-readable `error`.
Parameters
| Field | Type | Description |
|---|---|---|
| id (path)required | string |
Responses
200 Terminal result (complete or failed).
{
"id": "run_8f3a2b1c",
"status": "complete",
"summary": {
"title": "Weeknight pasta in 15 minutes",
"uploader": "@kitchen",
"durationSeconds": 212,
"files": [
"video.mp4",
"transcript.txt",
"comments.json"
]
},
"extraction": {
"name": "Weeknight pasta",
"servings": 2
}
}202 Still running. The body is `{ "status": "queued" | "processing" }`.
{
"status": "processing"
}401 Missing or invalid API key.
404 No extraction with this id belongs to you.
429 Rate limit exceeded.
/v1/extractions/{id}/files/{name}Download an extraction artifact
Streams one artifact produced by a completed extraction: `video.mp4`, `audio.m4a`, `transcript.txt`, `comments.json`, `thumbnail.jpg`, `description.txt` or subtitle files. The available names are listed in the result's `summary.files`.
Parameters
| Field | Type | Description |
|---|---|---|
| id (path)required | string | |
| name (path)required | string | File name exactly as listed in `summary.files`. |
Responses
200 The file bytes; `Content-Type` matches the artifact (video/mp4, text/plain, application/json, …).
400 Invalid file name.
401 Missing or invalid API key.
404 Extraction or file not found.
429 Rate limit exceeded.
/v1/probeProbe a URL and quote the price
Inspects a video URL without starting an extraction: returns the title, duration, whether subtitles/comments exist, and the exact credit price for the inputs you intend to request. This is the canonical quote: submitting the same URL with the same inputs debits exactly this amount (or the worst-case ceiling when duration is unknown). Rate limit: 10 requests per minute. Probing is free.
Request body
| Field | Type | Description |
|---|---|---|
| urlrequired | string | |
| inputs | "transcript" | "comments" | "audio" | "video"[] | Inputs you intend to submit with (AI extraction quote). The quote depends on them. Defaults to ["transcript","comments"]. Mutually exclusive with `parts`. |
| parts | "transcript" | "comments" | "audio" | "video"[] | Quote a download-only run instead: base fee + download rates, no AI component. Mutually exclusive with `inputs`. |
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"inputs": [
"transcript",
"comments"
]
}Response fields
| Field | Type | Description |
|---|---|---|
| urlrequired | string | |
| durationSecrequired | number | null | Null when the platform hides the duration. Submitting then debits the 300s worst case, refunded down to actual on completion. |
| titlerequired | string | null | |
| hasSubtitlesrequired | boolean | |
| hasCommentsrequired | boolean | |
| pricerequired | object |
Responses
200 Probe result with price quote.
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"durationSec": 212,
"title": "Weeknight pasta in 15 minutes",
"hasSubtitles": true,
"hasComments": true,
"price": {
"credits": 636,
"usd": 0.0636,
"breakdown": {
"text": 0.0636,
"audio": 0,
"video": 0
}
}
}400 Invalid url, unsupported platform, or bad inputs.
401 Missing or invalid API key.
429 Rate limit exceeded.
502 The video could not be probed right now.
/v1/openapi.jsonThis document
Returns this OpenAPI 3.1 document. Public, no authentication required. Import it into Postman, Insomnia, or your codegen of choice.
Responses
200 The OpenAPI 3.1 document.
Code snippet
curl -X POST https://postreef.com/v1/extractions \
-H "x-api-key: vidx_••••••••••••••••" \
-H "Content-Type: application/json" \
-d '{
"url": "<video url>",
"inputs": [
"transcript",
"comments"
]
}'