{"openapi":"3.0.0","info":{"title":"TinyFish Fetch API","version":"1.0.0","description":"Fetch URLs and extract clean page content.","contact":{"name":"TinyFish Support","email":"support@tinyfish.ai"}},"servers":[{"url":"https://api.fetch.tinyfish.ai"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authentication. Get your key from the API Keys page."}},"schemas":{"Monitor":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"schedule_cron":{"type":"string","example":"0 9 * * *"},"status":{"type":"string","enum":["creating","active","paused","failed","cancelled"]},"last_error":{"type":"string","nullable":true},"webhook_url":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["fetch"]},"config":{"type":"object","properties":{"url":{"type":"string"},"format":{"type":"string","enum":["markdown","html","json"]},"links":{"type":"boolean"},"image_links":{"type":"boolean"},"ttl":{"type":"integer"}},"required":["url","format","links","image_links","ttl"]}},"required":["id","name","schedule_cron","status","last_error","webhook_url","created_at","updated_at","type","config"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"schedule_cron":{"type":"string","example":"0 9 * * *"},"status":{"type":"string","enum":["creating","active","paused","failed","cancelled"]},"last_error":{"type":"string","nullable":true},"webhook_url":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["search"]},"config":{"type":"object","properties":{"query":{"type":"string"},"recency_minutes":{"type":"integer","nullable":true},"result_limit":{"type":"integer"}},"required":["query","recency_minutes","result_limit"]}},"required":["id","name","schedule_cron","status","last_error","webhook_url","created_at","updated_at","type","config"]}],"description":"A Page (fetch) or Topic (search) Monitor"},"MonitorRun":{"anyOf":[{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"Original requested URL","example":"https://example.com"},"final_url":{"type":"string","nullable":true,"description":"Final URL after redirects","example":"https://www.example.com"},"title":{"type":"string","nullable":true,"description":"Page title","example":"Example Domain"},"description":{"type":"string","nullable":true,"description":"Page meta description","example":"This domain is for use in illustrative examples."},"language":{"type":"string","nullable":true,"description":"Detected language code","example":"en"},"format":{"type":"string","enum":["markdown","html","json"],"description":"Format of the extracted text","example":"markdown"},"text":{"anyOf":[{"type":"string"},{},{"nullable":true}],"description":"Extracted content (string for markdown/html, object for json). Null if extraction failed."},"author":{"type":"string","nullable":true,"description":"Page author if available","example":"John Doe"},"published_date":{"type":"string","nullable":true,"description":"Published date if available","example":"2024-01-15"},"links":{"type":"array","items":{"type":"string"},"description":"Extracted links (only if links=true in request)"},"image_links":{"type":"array","items":{"type":"string"},"description":"Extracted image links (only if image_links=true in request)"},"iframe_links":{"type":"array","items":{"type":"string"},"description":"Extracted <iframe src> URLs, absolute and deduplicated (only if iframe_links=true in request)"},"page_metadata":{"type":"object","additionalProperties":{"nullable":true},"description":"Page-head metadata (only if page_metadata=true in request): canonical, favicon, robots, generator, viewport, keywords, plus nested og/twitter/article objects keyed by tag suffix and remaining named meta tags under `other`. Tags that legitimately repeat (og:image, og:locale:alternate, article:tag, article:author) collect into arrays. Keys are omitted when the page does not declare them; the whole field is omitted when the page exposes none.","example":{"canonical":"https://example.com","robots":"max-image-preview:large","og":{"title":"Example Domain","image":["https://example.com/og.png"]},"twitter":{"card":"summary_large_image"}}},"latency_ms":{"type":"number","nullable":true,"description":"Fetch latency for this URL in milliseconds if available from tf-fetch","example":1183.4},"not_modified":{"type":"boolean","description":"Present and `true` only when the origin returned 304 Not Modified for your `if_none_match`/`if_modified_since`; omitted otherwise.","example":true},"etag":{"type":"string","description":"The origin's current ETag validator, present only when the origin sent one and you set `include_etag_and_last_modified: true`; omitted otherwise. Store it and replay it as `if_none_match`.","example":"W/\"abc123\""},"last_modified":{"type":"string","description":"The origin's current Last-Modified validator, present only when the origin sent one and you set `include_etag_and_last_modified: true`; omitted otherwise. Store it and replay it as `if_modified_since`.","example":"Wed, 21 Oct 2015 07:28:00 GMT"},"unmatched_selectors":{"type":"array","items":{"type":"string"},"description":"Partial-miss report for `include_selectors`: the entries that matched no elements on this page, present only when some (but not all) entries matched — the result still carries the matched content. Omitted when every entry matched. A total miss is not a result at all: it is reported as a `selector_not_matched` entry in `errors[]`.","example":["aside.related"]}},"required":["url","final_url","title","description","language","format","text","author","published_date"]},"description":"Successfully fetched URLs"},"errors":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"URL that failed to fetch","example":"https://invalid.example.com"},"error":{"type":"string","description":"Error code describing the failure. Possible values: `target_http_error` (target server returned a non-2xx HTTP status other than 404/410 — see `status` field), `page_not_found` (target returned HTTP 404 or 410), `target_unreachable` (connection refused, TLS failure, or network error), `timeout` (request timed out), `proxy_error` (proxy tunnel failure), `bot_blocked` (bot-challenge page detected), `empty_content` (page loaded but no extractable text), `login_required` (target redirected to a login or account wall; the page is not served to anonymous sessions, so retrying without credentials will not help), `content_too_large` (document exceeds the 20MB size limit for generic documents — not retried in the browser; PDF and CSV have their own 50MB budgets), `invalid_url` (malformed URL or SSRF-blocked address), `invalid_redirect_url` (redirect target rejected before fetch), `conditional_unsupported` (conditional requests are supported on the fast path only; this URL requires browser rendering), `selector_not_matched` (no elements matching any `include_selectors` entry remained after `exclude_selectors` was applied — a total miss; carries `unmatched_selectors` and `candidate_selectors` retry hints. A partial miss is NOT an error: it is reported via the result's `unmatched_selectors`), `selector_unsupported` (`include_selectors`/`exclude_selectors` sent for a URL that resolves to a direct PDF/CSV download — no HTML to scope).","example":"target_http_error"},"status":{"type":"integer","description":"Upstream HTTP status code. Only present when `error` is `target_http_error` or `page_not_found`.","example":404},"unmatched_selectors":{"type":"array","items":{"type":"string"},"description":"The `include_selectors` entries that matched no elements on the fetched page. Only present when `error` is `selector_not_matched`.","example":["article","#content"]},"candidate_selectors":{"type":"array","items":{"type":"string"},"description":"Cheap retry hints (max 10), only present when `error` is `selector_not_matched`: landmark tags present on the page (e.g. `main`, `article`, `nav`) plus `#id` selectors of content-heavy elements. Retry the fetch with `include_selectors` drawn from this list.","example":["main","nav","#content"]}},"required":["url","error"],"description":"A URL that failed to fetch"},"description":"URLs that failed to fetch"},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Present only when errors is non-empty; include it when reporting failed URLs.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"},"id":{"type":"string"},"is_baseline":{"type":"boolean","description":"True for the first run; nothing to compare."}},"required":["results","errors","id","is_baseline"]},{"type":"object","properties":{"id":{"type":"string"},"request_id":{"type":"string"},"query":{"type":"string"},"status_code":{"type":"integer"},"result_count":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"position":{"type":"integer","description":"Position in search results","example":1},"site_name":{"type":"string","description":"Site name","example":"example.com"},"snippet":{"type":"string","description":"Text snippet from the result","example":"This is an example snippet..."},"title":{"type":"string","description":"Page title","example":"Example Page"},"url":{"type":"string","format":"uri","description":"Result URL","example":"https://example.com/page"},"thumbnail_url":{"type":"string","format":"uri","description":"Thumbnail image URL, present only when include_thumbnail=true and a thumbnail is available","example":"https://example.com/thumb.jpg"},"fetch":{"nullable":true,"description":"Fetched page content, present only when fetch param is supplied"},"fetch_error":{"nullable":true,"description":"Fetch error details when fetch was requested. Contains details on failure; may be null when fetching succeeds."},"date":{"type":"string","description":"Publication date, present for news and some web results"},"publisher":{"type":"string","description":"Publisher name, present for news results when domain_type=news"},"authors":{"type":"array","items":{"type":"string"},"description":"Author list, present for academic results when domain_type=research_paper"},"venue":{"type":"string","description":"Journal or conference name, present when domain_type=research_paper"},"year":{"type":"integer","description":"Publication year, present when domain_type=research_paper"},"cited_by_count":{"type":"integer","description":"Citation count, present when domain_type=research_paper"},"pdf_url":{"type":"string","description":"PDF URL, present when domain_type=research_paper and a PDF is available"}},"required":["position","site_name","snippet","title","url"],"description":"A single search result"}},"new_result_positions":{"type":"array","items":{"type":"integer"},"description":"Positions of results not seen in earlier runs"},"created_at":{"type":"string","format":"date-time"}},"required":["id","request_id","query","status_code","result_count","results","new_result_positions","created_at"]}],"description":"Fetch result for Page Monitors, Search for Topic"},"Dataset":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"spec":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","minLength":1},"columns":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1},"label":{"type":"string","minLength":1,"maxLength":200},"type":{"type":"string","enum":["string","number","boolean","url","date"]},"is_primary_key":{"type":"boolean"},"retrieval_hint":{"type":"string","maxLength":2000},"format":{"type":"string","maxLength":2000},"examples":{"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"maxItems":3},"is_unique":{"type":"boolean"}},"required":["name","type","is_primary_key"]},"minItems":1,"maxItems":64},"keep_when":{"type":"object","properties":{"column":{"type":"string","minLength":1}},"required":["column"]}},"required":["name","description","columns"]},"status":{"type":"string","enum":["empty","populating","ready","failed","cancelled"],"description":"empty: no rows yet; populating: a run is adding rows; ready: rows stand; failed or cancelled: how the latest run ended"},"target_rows":{"type":"integer","description":"Rows a populate fills to"},"row_count":{"type":"integer"},"run":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","running","done","cancelled","failed"]},"started_at":{"type":"string","format":"date-time"},"finished_at":{"type":"string","format":"date-time"},"error":{"type":"string","description":"Why the run failed"}},"required":["id","status","started_at"],"description":"The latest run, however it ended; null when the dataset never ran"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","spec","status","target_rows","row_count","run","created_at"],"description":"A dataset and the state of its rows"}},"parameters":{}},"paths":{"/":{"post":{"operationId":"fetchUrls","summary":"Fetch and extract content from URLs","description":"Fetches web pages, renders JavaScript-heavy pages when needed, and returns clean extracted content in your preferred format. Submit up to 10 URLs, get back structured content. Per-URL failures appear in `errors[]` and do not fail the entire request.\n\n**Per-URL error codes** (in `errors[].error`):\n- `target_http_error` — target server returned a non-2xx HTTP status other than 404/410; the raw status code is in `errors[].status`\n- `page_not_found` — target URL returned HTTP 404 or 410; the raw status code is in `errors[].status`\n- `target_unreachable` — connection refused, TLS failure, DNS failure, or other network error\n- `timeout` — request timed out\n- `proxy_error` — proxy tunnel failure\n- `bot_blocked` — bot-challenge page detected (Cloudflare, etc.)\n- `empty_content` — page loaded but no extractable text was found\n- `login_required` — the target redirected to a login or account wall; retrying without credentials will not help\n- `content_too_large` — the document exceeds the 20MB size limit for generic documents (PDF and CSV have their own 50MB budgets); not retried in the browser\n- `invalid_url` — malformed URL or SSRF-blocked address\n- `invalid_redirect_url` — redirect target rejected before fetch\n- `conditional_unsupported` — conditional requests (`if_none_match` / `if_modified_since`) are supported on the fast path only; this URL requires browser rendering\n- `selector_not_matched` — no elements matching any `include_selectors` entry remained after `exclude_selectors` was applied; the error carries `unmatched_selectors` plus `candidate_selectors` retry hints (a partial miss is not an error — it's reported on the result's `unmatched_selectors`)\n- `selector_unsupported` — `include_selectors` / `exclude_selectors` sent for a URL that resolves to a direct PDF/CSV download (no HTML to scope)","security":[{"ApiKeyAuth":[]}],"requestBody":{"description":"URLs to fetch and extraction options","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1,"maxItems":10,"description":"Array of URLs to fetch (1-10). All URLs are fetched in parallel. Each URL is processed independently — if one fails, others still return successfully. Errors are reported per-URL in the errors array.","example":["https://example.com","https://example.org"]},"purpose":{"type":"string","minLength":1,"maxLength":2000,"description":"Why these URLs are being fetched — the underlying goal or task the content will be used for. Used to better tailor fetching and extraction to your intent.","example":"Compare pricing tiers across vendors for a procurement report"},"format":{"type":"string","enum":["markdown","html","json"],"default":"markdown","description":"Output format for extracted content. \"markdown\" (default) is ideal for LLM consumption. \"html\" returns cleaned semantic HTML. \"json\" returns a structured document tree.","example":"markdown"},"links":{"type":"boolean","default":false,"description":"Extract all outbound links (<a href>) from each page. Useful for discovering related pages or navigating to specific content. Links are returned as absolute URLs in the links array of each result.","example":false},"image_links":{"type":"boolean","default":false,"description":"Extract all image URLs (<img src>) from each page. Useful for finding visual content or media assets. Image links are returned as absolute URLs in the image_links array of each result.","example":false},"iframe_links":{"type":"boolean","default":false,"description":"Extract the src URLs of <iframe> elements on each page. Useful when a page embeds its main content in a frame (job boards, booking widgets, document viewers): fetch the returned URL to read the embedded content. Iframe links are returned as absolute, deduplicated URLs in the iframe_links array of each result. Script-injected iframes are only present when the page was rendered in a browser.","example":false},"page_metadata":{"type":"boolean","default":false,"description":"Return page-head metadata for each page in the page_metadata object of each result: canonical URL, favicon, robots directive, generator, viewport, keywords, all Open Graph (og), Twitter card (twitter), and article tags, and remaining named meta tags under `other`. Useful for SEO/technical audits and link-preview generation.","example":false},"ttl":{"type":"integer","minimum":0,"description":"Caller freshness tolerance in seconds for the cached entry. Omit (default) for unlimited tolerance — any cached entry is acceptable. Set to 0 to prefer a live fetch; a cached entry is still served if the origin's Cache-Control: max-age covers its age, or the host is in the small allowlist of operator-pinned never-expire domains. Set to N > 0 to accept a cached entry whose age is below N; the upstream Cache-Control: max-age and the never-expire allowlist may extend (never shorten) this tolerance.","example":0},"per_url_timeout_ms":{"type":"integer","minimum":1,"maximum":110000,"description":"Wall-clock timeout budget in milliseconds applied independently to each URL. If one URL exceeds this budget, it returns a per-URL timeout error while other URLs in the same request continue.","example":45000},"if_none_match":{"type":"string","minLength":1,"description":"ETag validator from a prior fetch of this URL, forwarded verbatim as the If-None-Match header on the origin request. Only valid with a single URL — combining with a batch of URLs returns a 400. tf-fetch does not persist validators; the caller owns replaying them.","example":"W/\"abc123\""},"if_modified_since":{"type":"string","minLength":1,"description":"Last-Modified validator from a prior fetch of this URL, forwarded verbatim as the If-Modified-Since header on the origin request. Only valid with a single URL — combining with a batch of URLs returns a 400. tf-fetch does not persist validators; the caller owns replaying them.","example":"Wed, 21 Oct 2015 07:28:00 GMT"},"include_etag_and_last_modified":{"type":"boolean","description":"Opt-in to receiving `etag` / `last_modified` validators (and `not_modified` detection) on each result. Defaults to false — tf-fetch omits these fields unless requested. Independent of `if_none_match` / `if_modified_since`: works with a single URL or a batch.","example":true},"include_selectors":{"type":"array","items":{"type":"string","minLength":1,"maxLength":1000},"minItems":1,"maxItems":20,"description":"Array of CSS selectors (1-20 entries, each 1-1000 characters) that scope extracted content (`text`, `links`, `image_links`) to elements matching ANY entry, concatenated in document order. Tag selectors cover semantic sections (`main`, `article`, `nav`); entries may themselves use CSS comma-grouping. Selected content is returned verbatim in the requested format (scripts/styles stripped) — automatic boilerplate removal is bypassed. Page-level metadata (`title`, `description`, `language`, `author`, `published_date`) still comes from the full document. When some entries match and others do not, the URL still succeeds and the misses are reported in the result's `unmatched_selectors`. When no entry matches anything, that URL fails with the per-URL error code `selector_not_matched` (carrying `unmatched_selectors` and `candidate_selectors` retry hints) — never a silent full-page fallback. URLs that resolve to direct PDF/CSV downloads fail with `selector_unsupported`. Invalid CSS selector syntax is rejected with a 422. Applied post-fetch: caching and routing are unchanged.","example":["article"]},"exclude_selectors":{"type":"array","items":{"type":"string","minLength":1,"maxLength":1000},"minItems":1,"maxItems":20,"description":"Array of CSS selectors (1-20 entries, each 1-1000 characters) for elements to remove before extraction — applied before `include_selectors` scopes what remains, so it also prunes inside selected regions. Entries may themselves use CSS comma-grouping. Entries that match nothing are a no-op, never an error, but URLs that resolve to direct PDF/CSV downloads fail with `selector_unsupported`. Invalid CSS selector syntax is rejected with a 422. Applied post-fetch: caching and routing are unchanged.","example":[".comments",".newsletter-signup"]}},"required":["urls"]}}}},"responses":{"200":{"description":"Fetch completed. Check `errors[]` for any per-URL failures.","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"Original requested URL","example":"https://example.com"},"final_url":{"type":"string","nullable":true,"description":"Final URL after redirects","example":"https://www.example.com"},"title":{"type":"string","nullable":true,"description":"Page title","example":"Example Domain"},"description":{"type":"string","nullable":true,"description":"Page meta description","example":"This domain is for use in illustrative examples."},"language":{"type":"string","nullable":true,"description":"Detected language code","example":"en"},"format":{"type":"string","enum":["markdown","html","json"],"description":"Format of the extracted text","example":"markdown"},"text":{"anyOf":[{"type":"string"},{},{"nullable":true}],"description":"Extracted content (string for markdown/html, object for json). Null if extraction failed."},"author":{"type":"string","nullable":true,"description":"Page author if available","example":"John Doe"},"published_date":{"type":"string","nullable":true,"description":"Published date if available","example":"2024-01-15"},"links":{"type":"array","items":{"type":"string"},"description":"Extracted links (only if links=true in request)"},"image_links":{"type":"array","items":{"type":"string"},"description":"Extracted image links (only if image_links=true in request)"},"iframe_links":{"type":"array","items":{"type":"string"},"description":"Extracted <iframe src> URLs, absolute and deduplicated (only if iframe_links=true in request)"},"page_metadata":{"type":"object","additionalProperties":{"nullable":true},"description":"Page-head metadata (only if page_metadata=true in request): canonical, favicon, robots, generator, viewport, keywords, plus nested og/twitter/article objects keyed by tag suffix and remaining named meta tags under `other`. Tags that legitimately repeat (og:image, og:locale:alternate, article:tag, article:author) collect into arrays. Keys are omitted when the page does not declare them; the whole field is omitted when the page exposes none.","example":{"canonical":"https://example.com","robots":"max-image-preview:large","og":{"title":"Example Domain","image":["https://example.com/og.png"]},"twitter":{"card":"summary_large_image"}}},"latency_ms":{"type":"number","nullable":true,"description":"Fetch latency for this URL in milliseconds if available from tf-fetch","example":1183.4},"not_modified":{"type":"boolean","description":"Present and `true` only when the origin returned 304 Not Modified for your `if_none_match`/`if_modified_since`; omitted otherwise.","example":true},"etag":{"type":"string","description":"The origin's current ETag validator, present only when the origin sent one and you set `include_etag_and_last_modified: true`; omitted otherwise. Store it and replay it as `if_none_match`.","example":"W/\"abc123\""},"last_modified":{"type":"string","description":"The origin's current Last-Modified validator, present only when the origin sent one and you set `include_etag_and_last_modified: true`; omitted otherwise. Store it and replay it as `if_modified_since`.","example":"Wed, 21 Oct 2015 07:28:00 GMT"},"unmatched_selectors":{"type":"array","items":{"type":"string"},"description":"Partial-miss report for `include_selectors`: the entries that matched no elements on this page, present only when some (but not all) entries matched — the result still carries the matched content. Omitted when every entry matched. A total miss is not a result at all: it is reported as a `selector_not_matched` entry in `errors[]`.","example":["aside.related"]}},"required":["url","final_url","title","description","language","format","text","author","published_date"]},"description":"Successfully fetched URLs"},"errors":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"URL that failed to fetch","example":"https://invalid.example.com"},"error":{"type":"string","description":"Error code describing the failure. Possible values: `target_http_error` (target server returned a non-2xx HTTP status other than 404/410 — see `status` field), `page_not_found` (target returned HTTP 404 or 410), `target_unreachable` (connection refused, TLS failure, or network error), `timeout` (request timed out), `proxy_error` (proxy tunnel failure), `bot_blocked` (bot-challenge page detected), `empty_content` (page loaded but no extractable text), `login_required` (target redirected to a login or account wall; the page is not served to anonymous sessions, so retrying without credentials will not help), `content_too_large` (document exceeds the 20MB size limit for generic documents — not retried in the browser; PDF and CSV have their own 50MB budgets), `invalid_url` (malformed URL or SSRF-blocked address), `invalid_redirect_url` (redirect target rejected before fetch), `conditional_unsupported` (conditional requests are supported on the fast path only; this URL requires browser rendering), `selector_not_matched` (no elements matching any `include_selectors` entry remained after `exclude_selectors` was applied — a total miss; carries `unmatched_selectors` and `candidate_selectors` retry hints. A partial miss is NOT an error: it is reported via the result's `unmatched_selectors`), `selector_unsupported` (`include_selectors`/`exclude_selectors` sent for a URL that resolves to a direct PDF/CSV download — no HTML to scope).","example":"target_http_error"},"status":{"type":"integer","description":"Upstream HTTP status code. Only present when `error` is `target_http_error` or `page_not_found`.","example":404},"unmatched_selectors":{"type":"array","items":{"type":"string"},"description":"The `include_selectors` entries that matched no elements on the fetched page. Only present when `error` is `selector_not_matched`.","example":["article","#content"]},"candidate_selectors":{"type":"array","items":{"type":"string"},"description":"Cheap retry hints (max 10), only present when `error` is `selector_not_matched`: landmark tags present on the page (e.g. `main`, `article`, `nav`) plus `#id` selectors of content-heavy elements. Retry the fetch with `include_selectors` drawn from this list.","example":["main","nav","#content"]}},"required":["url","error"],"description":"A URL that failed to fetch"},"description":"URLs that failed to fetch"},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Present only when errors is non-empty; include it when reporting failed URLs.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["results","errors"],"description":"Fetch response with results and errors"}}}},"400":{"description":"Invalid request — missing `urls`, too many URLs (max 10), bad parameter value, or `if_none_match`/`if_modified_since` combined with a batch of URLs","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"402":{"description":"Payment required - daily free Fetch allowance used and the wallet balance is zero or below (`INSUFFICIENT_CREDITS`), or used on a product that does not charge the wallet (`FREE_ALLOWANCE_EXHAUSTED`)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}}}}},"/usage":{"get":{"operationId":"listFetchUsage","summary":"List fetch usage","description":"List fetch usage records for the authenticated user. Returns paginated results with URL, format, status, and metadata for each fetch operation. A workflow key reads only records its team's workflow keys made, never its creator's own.","security":[{"ApiKeyAuth":[]}],"parameters":[{"schema":{"type":"string","format":"date-time"},"required":false,"name":"start_after","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"end_before","in":"query"},{"schema":{"type":"string","enum":["completed","failed"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000,"default":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","enum":["me","team"],"description":"Whose records: me (default for a personal key) or team, made by the team's workflow keys (the only choice for a workflow key)","example":"team"},"required":false,"description":"Whose records: me (default for a personal key) or team, made by the team's workflow keys (the only choice for a workflow key)","name":"owner","in":"query"}],"responses":{"200":{"description":"Fetch usage retrieved successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"final_url":{"type":"string"},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"language":{"type":"string","nullable":true},"author":{"type":"string","nullable":true},"published_date":{"type":"string","nullable":true},"format":{"type":"string","enum":["markdown","html","json"]},"status":{"type":"string","enum":["completed","failed"]},"request_origin":{"type":"string"},"request_id":{"type":"string","nullable":true},"text_length":{"type":"integer","nullable":true},"links_count":{"type":"integer"},"image_links_count":{"type":"integer"},"latency_ms":{"type":"number","nullable":true},"not_modified":{"type":"boolean","description":"Present and `true` only when that fetch was a conditional request that returned 304 Not Modified; omitted otherwise.","example":true},"etag":{"type":"string","description":"The origin's ETag validator captured at fetch time, present only when `include_etag_and_last_modified` was set on that request and the origin returned one; omitted otherwise.","example":"W/\"abc123\""},"last_modified":{"type":"string","description":"The origin's Last-Modified validator captured at fetch time, present only when `include_etag_and_last_modified` was set on that request and the origin returned one; omitted otherwise.","example":"Wed, 21 Oct 2015 07:28:00 GMT"},"created_at":{"type":"string","format":"date-time"},"error":{"type":"string","nullable":true}},"required":["id","url","final_url","title","description","language","author","published_date","format","status","request_origin","request_id","text_length","links_count","image_links_count","latency_ms","created_at","error"],"description":"A single fetch usage record"}},"total":{"type":"integer","description":"Records matching the filters. A lower bound for a team list (a workflow key, or `owner=team`); page with `has_more`."},"limit":{"type":"integer"},"page":{"type":"integer"},"total_pages":{"type":"integer","description":"Pages implied by `total`; a lower bound wherever `total` is one."},"has_more":{"type":"boolean"}},"required":["items","total","limit","page","total_pages","has_more"],"description":"Paginated fetch usage response"},"example":{"items":[{"id":"fr-abc123","url":"https://example.com","final_url":"https://example.com/page","title":"Example Page","description":"An example page","language":"en","author":null,"published_date":null,"format":"markdown","status":"completed","request_origin":"api","request_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","text_length":4096,"links_count":12,"image_links_count":3,"latency_ms":250,"created_at":"2026-01-14T10:30:00.000Z","error":null}],"total":1,"limit":100,"page":1,"total_pages":1,"has_more":false}}}},"400":{"description":"Invalid query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","INVALID_INPUT","RATE_LIMIT_EXCEEDED","DAILY_LIMIT_EXCEEDED","INTERNAL_ERROR","RETRY_REQUIRED","UNAUTHORIZED","VAULT_RECONNECT_REQUIRED","FORBIDDEN","NOT_FOUND","FEATURE_NOT_AVAILABLE","SERVICE_BUSY","TIMEOUT","INSUFFICIENT_CREDITS","FREE_ALLOWANCE_EXHAUSTED","CONTENT_POLICY_VIOLATION","MAX_STEPS_EXCEEDED","SITE_BLOCKED","TASK_FAILED","CANCELLED"],"description":"Machine-readable error code","example":"INVALID_INPUT"},"message":{"type":"string","description":"Human-readable error message","example":"Field \"url\" is required and must be a string"},"details":{"nullable":true,"description":"Additional error details (validation errors, etc.)"}},"required":["code","message"]},"request_id":{"type":"string","description":"Request correlation ID, also returned as the X-Request-ID response header. Include it when reporting issues.","example":"8f9dba20-e37b-4749-a919-2269e28b4a2c"}},"required":["error"],"description":"Standard error response format"}}}}}}}}}