{"openapi":"3.1.0","info":{"title":"PostReef API","version":"1.0.0","description":"Turn any public social-media video URL (YouTube, Shorts, TikTok, Instagram Reels and more) into structured data: metadata, transcript, comments, downloadable media, plus an optional typed JSON extraction conforming to a JSON Schema you provide.\n\nAuthenticate with an API key (`x-api-key` header, or `Authorization: Bearer <key>`). Submit an extraction, then either poll for the result or receive a webhook when it completes."},"servers":[{"url":"https://postreef.com"}],"security":[{"apiKey":[]},{"bearerAuth":[]}],"tags":[{"name":"Extractions","description":"Submit, list and read extractions."},{"name":"Probe","description":"Inspect a URL and get an exact price quote before submitting."}],"paths":{"/v1/extractions":{"post":{"operationId":"createExtraction","summary":"Submit an extraction","tags":["Extractions"],"description":"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.\n\nIf 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.\n\nRate 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":[{"name":"Idempotency-Key","in":"header","required":false,"description":"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.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitExtractionRequest"},"example":{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","inputs":["transcript","comments"],"schemaId":"Recipe","webhookUrl":"https://example.com/hooks/postreef"}}}},"responses":{"200":{"description":"Idempotent replay: the Idempotency-Key matched a previous submit with the same body, so the original extraction is returned.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitExtractionResponse"}}}},"201":{"description":"Extraction created and started.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitExtractionResponse"},"example":{"id":"run_8f3a2b1c","status":"pending","url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","creditsDebited":127,"createdAt":"2026-06-11T10:15:00.000Z"}}}},"400":{"description":"Invalid request: missing/invalid url (`invalid_url`), unsupported platform (`unsupported_url`), invalid options or webhookUrl (`invalid_request`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"Insufficient credits (`insufficient_credits`). `details.balance` and `details.cost` carry your current balance and the quoted cost in credits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Referenced schemaId not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Idempotency-Key was already used with a different request body (`conflict`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` (per-minute window exceeded, includes Retry-After).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"operationId":"listExtractions","summary":"List extractions","tags":["Extractions"],"description":"Lists your extractions, newest first, with cursor pagination. Pass `next_cursor` from a previous page as `cursor` to fetch the next page.","parameters":[{"name":"limit","in":"query","required":false,"description":"Page size, 1–100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from the previous page's `next_cursor`. Do not construct it yourself.","schema":{"type":"string"}}],"responses":{"200":{"description":"One page of extractions.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionList"},"example":{"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":{"description":"Invalid limit or cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/extract":{"post":{"operationId":"createExtractionAlias","summary":"Submit an extraction (alias)","tags":["Extractions"],"description":"Exact alias of `POST /v1/extractions`, with the same request, responses, rate limit and Idempotency-Key support.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitExtractionRequest"},"example":{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","inputs":["transcript","comments"],"schemaId":"Recipe","webhookUrl":"https://example.com/hooks/postreef"}}}},"responses":{"201":{"description":"Extraction created and started.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitExtractionResponse"}}}}}}},"/v1/extractions/{id}":{"get":{"operationId":"getExtraction","summary":"Get an extraction","tags":["Extractions"],"description":"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":[{"name":"id","in":"path","required":true,"description":"Extraction id returned at submit time.","schema":{"type":"string"}}],"responses":{"200":{"description":"The extraction.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionDetail"},"example":{"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":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No extraction with this id belongs to you.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/extractions/{id}/result":{"get":{"operationId":"getExtractionResult","summary":"Get an extraction's result","tags":["Extractions"],"description":"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":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Terminal result (complete or failed).","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionResult"},"example":{"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":{"description":"Still running. The body is `{ \"status\": \"queued\" | \"processing\" }`.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractionPending"},"example":{"status":"processing"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No extraction with this id belongs to you.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/extractions/{id}/files/{name}":{"get":{"operationId":"downloadExtractionFile","summary":"Download an extraction artifact","tags":["Extractions"],"description":"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":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"File name exactly as listed in `summary.files`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The file bytes; `Content-Type` matches the artifact (video/mp4, text/plain, application/json, …).","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid file name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Extraction or file not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/probe":{"post":{"operationId":"probeUrl","summary":"Probe a URL and quote the price","tags":["Probe"],"description":"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.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProbeRequest"},"example":{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","inputs":["transcript","comments"]}}}},"responses":{"200":{"description":"Probe result with price quote.","headers":{"X-RateLimit-Limit":{"description":"Request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp (seconds) when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProbeResponse"},"example":{"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":{"description":"Invalid url, unsupported platform, or bad inputs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The video could not be probed right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/v1/openapi.json":{"get":{"operationId":"getOpenApiSpec","summary":"This document","tags":["Probe"],"description":"Returns this OpenAPI 3.1 document. Public, no authentication required. Import it into Postman, Insomnia, or your codegen of choice.","security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"webhooks":{"extraction.completed":{"post":{"operationId":"webhookExtractionCompleted","summary":"Extraction completed","description":"Sent to your registered webhook endpoints (and any per-run `webhookUrl`) when an extraction finishes successfully. The body matches `GET /v1/extractions/{id}/result`. Respond with any 2xx within 10 seconds; failed deliveries are retried after 30s, 2m and 10m. Verify the `X-Vidextract-Signature` header; see the webhooks documentation.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookExtractionCompleted"}}}},"responses":{"200":{"description":"Return any 2xx to acknowledge receipt."}}}},"extraction.failed":{"post":{"operationId":"webhookExtractionFailed","summary":"Extraction failed","description":"Sent when an extraction fails. Debited credits are refunded in full before this event fires.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookExtractionFailed"}}}},"responses":{"200":{"description":"Return any 2xx to acknowledge receipt."}}}},"test":{"post":{"operationId":"webhookTest","summary":"Test event","description":"Sent on demand from the dashboard to verify an endpoint. Signed and delivered exactly like real events.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestEvent"}}}},"responses":{"200":{"description":"Return any 2xx to acknowledge receipt."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your API key, created in the dashboard."},"bearerAuth":{"type":"http","scheme":"bearer","description":"The same API key, sent as `Authorization: Bearer <key>`."}},"schemas":{"ErrorEnvelope":{"type":"object","description":"Every non-2xx response carries this envelope.","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthorized","invalid_request","invalid_url","unsupported_url","insufficient_credits","rate_limited","not_found","conflict","internal_error"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable explanation."},"details":{"type":"object","description":"Optional structured context, e.g. `balance` and `cost` on insufficient_credits.","additionalProperties":true}}}}},"ExtractionInput":{"type":"string","enum":["transcript","comments","audio","video"],"description":"An input modality for the AI extraction. `transcript` and `comments` are text; `audio` attaches the audio track; `video` attaches the full video and implies audio, so the separate audio modality is not billed when video is selected."},"SubmitExtractionRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Public video URL (YouTube, Shorts, TikTok, Instagram Reels, …)."},"inputs":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionInput"},"description":"Which modalities feed the AI extraction. Defaults to [\"transcript\",\"comments\"]. Determines the price."},"schemaId":{"type":"string","description":"Id of a predefined schema (e.g. `Recipe`) or one of your saved schemas. Mutually exclusive with `schema`."},"schema":{"type":"object","description":"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).","additionalProperties":true},"prompt":{"type":"string","description":"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":{"type":"boolean","description":"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":{"type":"boolean","description":"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":{"type":"number","description":"Spend ceiling for an auto run, in credits. Required when `auto` is true. Must be at least the cost of the cheapest rung."},"policy":{"type":"string","enum":["strict","fallback","best-effort"],"description":"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":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionInput"},"description":"Download-only runs (no schema): which artifacts to fetch. Omit for everything. Metadata, thumbnail and description are always included. Invalid alongside `schema`/`schemaId`."},"webhookUrl":{"type":"string","format":"uri","description":"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."}}},"SubmitExtractionResponse":{"type":"object","required":["id","status","url","creditsDebited","createdAt"],"properties":{"id":{"type":"string","description":"Extraction id, used for polling, results and files."},"status":{"$ref":"#/components/schemas/ExtractionStatus"},"url":{"type":"string"},"creditsDebited":{"type":"integer","description":"Credits debited up-front. 0 when the result was served from a recent identical extraction."},"createdAt":{"type":"string","format":"date-time"}}},"ExtractionStatus":{"type":"string","enum":["queued","pending","running","complete","failed"]},"Extraction":{"type":"object","required":["id","url","status","createdAt","completedAt","creditsDebited","cached"],"properties":{"id":{"type":"string"},"url":{"type":"string"},"status":{"$ref":"#/components/schemas/ExtractionStatus"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":["string","null"],"format":"date-time"},"creditsDebited":{"type":"integer"},"cached":{"type":"boolean","description":"True when the result was reused from a recent identical extraction (no credits were debited)."},"outcome":{"$ref":"#/components/schemas/ExtractionOutcome"},"verdictReason":{"type":"string","description":"One-sentence explanation of the content-match verdict, present only when `outcome` is `no_match` or `uncertain`."},"error":{"type":"string","description":"Present only when status is `failed`."}}},"ExtractionOutcome":{"type":["string","null"],"enum":["ok","no_match","uncertain",null],"description":"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."},"ExtractionDetail":{"allOf":[{"$ref":"#/components/schemas/Extraction"},{"type":"object","required":["phase"],"properties":{"phase":{"type":"string","enum":["queued","processing","done","failed"],"description":"Coarse progress phase derived from status."}}}]},"ExtractionList":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Extraction"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` to fetch the next page. Null on the last page."}}},"ExtractionSummary":{"type":"object","description":"Metadata and artifact listing for a completed extraction. Fields are null when the platform doesn't expose them.","properties":{"runId":{"type":"string"},"url":{"type":"string"},"title":{"type":["string","null"]},"uploader":{"type":["string","null"]},"duration":{"type":["number","null"],"description":"Seconds, as reported by the platform."},"description":{"type":["string","null"]},"thumbnail":{"type":["string","null"],"description":"Thumbnail URL on the source platform."},"webpage_url":{"type":["string","null"]},"view_count":{"type":["integer","null"]},"like_count":{"type":["integer","null"]},"comment_count":{"type":["integer","null"]},"upload_date":{"type":["string","null"],"description":"YYYYMMDD."},"durationSeconds":{"type":["number","null"],"description":"Measured duration of the downloaded media."},"videoFile":{"type":["string","null"]},"thumbnailFile":{"type":["string","null"]},"subtitleFiles":{"type":"array","items":{"type":"string"}},"audioFile":{"type":["string","null"]},"transcriptFile":{"type":["string","null"]},"commentsFile":{"type":["string","null"]},"descriptionFile":{"type":["string","null"]},"files":{"type":"array","items":{"type":"string"},"description":"Every downloadable artifact. Fetch one via `GET /v1/extractions/{id}/files/{name}`."},"startedAt":{"type":"integer","description":"Unix ms."},"finishedAt":{"type":"integer","description":"Unix ms."}}},"ExtractionResultComplete":{"type":"object","required":["id","status","summary","extraction"],"properties":{"id":{"type":"string"},"status":{"type":"string","const":"complete"},"summary":{"$ref":"#/components/schemas/ExtractionSummary"},"outcome":{"$ref":"#/components/schemas/ExtractionOutcome"},"verdictReason":{"type":"string","description":"One-sentence explanation of the content-match verdict, present only when `outcome` is `no_match` or `uncertain`."},"extraction":{"type":["object","null"],"description":"The structured object conforming to your JSON Schema. Null for download-only runs, and also null when `outcome` is `no_match` / `uncertain` (the content didn't match the schema).","additionalProperties":true}}},"ExtractionResultFailed":{"type":"object","required":["id","status","error"],"properties":{"id":{"type":"string"},"status":{"type":"string","const":"failed"},"error":{"type":"string","description":"Human-readable failure reason. Credits were refunded in full."}}},"ExtractionResult":{"oneOf":[{"$ref":"#/components/schemas/ExtractionResultComplete"},{"$ref":"#/components/schemas/ExtractionResultFailed"}]},"ExtractionPending":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["queued","processing"]}}},"ProbeRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"inputs":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionInput"},"description":"Inputs you intend to submit with (AI extraction quote). The quote depends on them. Defaults to [\"transcript\",\"comments\"]. Mutually exclusive with `parts`."},"parts":{"type":"array","items":{"$ref":"#/components/schemas/ExtractionInput"},"description":"Quote a download-only run instead: base fee + download rates, no AI component. Mutually exclusive with `inputs`."}}},"ProbeResponse":{"type":"object","required":["url","durationSec","title","hasSubtitles","hasComments","price"],"properties":{"url":{"type":"string"},"durationSec":{"type":["number","null"],"description":"Null when the platform hides the duration. Submitting then debits the 300s worst case, refunded down to actual on completion."},"title":{"type":["string","null"]},"hasSubtitles":{"type":"boolean"},"hasComments":{"type":"boolean"},"price":{"type":"object","required":["credits","usd","baseUsd","downloadUsd","downloadBreakdown","breakdown"],"properties":{"credits":{"type":"integer","description":"Total price in credits (1 credit = $0.0001)."},"usd":{"type":"number"},"baseUsd":{"type":"number","description":"Flat per-run base fee ($0.005) covering the probe, metadata, thumbnail and description. Cached results skip it."},"downloadUsd":{"type":"number","description":"Download charge across requested artifacts: video $0.00005/sec, audio $0.00002/sec, transcript $0.001 flat, comments $0.002 flat. Artifacts that turn out unavailable are refunded on completion."},"downloadBreakdown":{"type":"object","description":"Per-artifact USD; only requested artifacts appear.","properties":{"transcript":{"type":"number"},"comments":{"type":"number"},"audio":{"type":"number"},"video":{"type":"number"}}},"breakdown":{"type":"object","description":"AI component, per-modality USD: text $0.00005/sec, audio $0.0001/sec, video $0.0002/sec. All-zero for download-only quotes.","properties":{"text":{"type":"number"},"audio":{"type":"number"},"video":{"type":"number"}}}}}}},"WebhookExtractionCompleted":{"type":"object","required":["event","data"],"properties":{"event":{"type":"string","const":"extraction.completed"},"data":{"$ref":"#/components/schemas/ExtractionResultComplete"}}},"WebhookExtractionFailed":{"type":"object","required":["event","data"],"properties":{"event":{"type":"string","const":"extraction.failed"},"data":{"$ref":"#/components/schemas/ExtractionResultFailed"}}},"WebhookTestEvent":{"type":"object","required":["event","data"],"properties":{"event":{"type":"string","const":"test"},"data":{"type":"object","properties":{"message":{"type":"string"},"endpointId":{"type":"string"},"sentAt":{"type":"string","format":"date-time"}}}}}}}}