{
  "openapi": "3.1.0",
  "info": {
    "title": "SocialCrawl API: Web Scraping",
    "version": "1.0.0",
    "description": "Unified social media data API - one API key, one consistent response format, 69 platforms, 679 endpoints. Power AI agents with clean social data.\n\nThis document covers the Web Scraping endpoints only (22 operations). The full contract for every platform is at https://www.socialcrawl.dev/v1/openapi.json, and every per-platform document is listed at https://www.socialcrawl.dev/openapi/index.json.",
    "contact": {
      "name": "SocialCrawl",
      "url": "https://www.socialcrawl.dev",
      "email": "socialcrawl@ridio.io"
    }
  },
  "servers": [
    {
      "url": "https://www.socialcrawl.dev/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key authentication. Send your key in the `x-api-key` request header on every call. Create and manage keys in the dashboard."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "The same API key sent as `Authorization: Bearer <key>`, the default in most agents and HTTP tools. Same key, same rules; when both headers are sent, `x-api-key` wins."
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "description": "Unified error response envelope. Every 4xx/5xx response returns this shape; `error.type` is a machine-readable code from the API's error catalog.",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ],
            "example": false,
            "description": "Always false on an error response."
          },
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "MISSING_API_KEY",
                  "INVALID_API_KEY",
                  "INSUFFICIENT_CREDITS",
                  "INVALID_REQUEST",
                  "ENDPOINT_NOT_FOUND",
                  "RESOURCE_NOT_FOUND",
                  "CONCURRENCY_LIMIT",
                  "UPSTREAM_ERROR",
                  "SERVICE_UNAVAILABLE",
                  "INTERNAL_ERROR",
                  "METHOD_NOT_ALLOWED",
                  "IDEMPOTENCY_KEY_CONFLICT",
                  "IDEMPOTENCY_IN_PROGRESS",
                  "IDEMPOTENCY_REPLAY_UNAVAILABLE",
                  "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                  "COHORT_MEMBER_LIMIT_EXCEEDED",
                  "COHORT_LIMIT_EXCEEDED",
                  "COHORT_IDENTITY_PLATFORM_UNSUPPORTED",
                  "COHORT_IDENTITY_CONFLICT",
                  "COHORT_QUERY_NOT_CANCELLABLE",
                  "COHORT_QUERY_NOT_READY",
                  "COHORT_RESULT_TOO_LARGE",
                  "PAYLOAD_TOO_LARGE",
                  "RATE_LIMITED",
                  "KEY_BUDGET_EXCEEDED",
                  "RUN_NOT_FOUND",
                  "RUN_STATE_CONFLICT",
                  "RUN_EXPIRED",
                  "NEEDS_ASYNC",
                  "CURSOR_EXPIRED",
                  "SCHEDULE_NOT_FOUND",
                  "STREAM_NOT_FOUND",
                  "STREAM_STATE_CONFLICT"
                ],
                "example": "INVALID_REQUEST",
                "description": "Machine-readable error code. The per-status `x-error-codes` list on each response narrows which codes that status can carry."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation of the error."
              },
              "status": {
                "type": "integer",
                "description": "HTTP status code, echoed in the body."
              },
              "doc_url": {
                "type": "string",
                "description": "Link to the docs page for this error code."
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether retrying the identical request can succeed. True for rate limits, concurrency limits, idempotency contention and transient upstream or server failures; false for every other code, including both 402s."
              },
              "details": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true,
                "description": "Optional structured context. Omitted on ordinary errors. Both 402s carry `reason` (`balance_too_low` or `key_budget_reached`), `retry_will_succeed: false`, a one-sentence `next_step`, and `top_up_url` or `key_settings_url`; the GET endpoints add `credits_required`; a metered endpoint adds `pricing` (`\"ceiling\"` when the hold is the most the call can cost and the unused part is refunded, `\"exact\"` when the call is charged what it holds; measured on recent production calls), `credits_typical` (on a ceiling, what a call like this is usually charged), and, when a cheaper shape of the same request exists, `lower_cost_with`, `lower_cost_options` (`param`, `value` or null to leave it out, and `credits_required` with that change) and `credits_required_cheapest`. A `max_credits` page that switched to a cheaper form to fit (`threads/search` with `expand=false`) and still found the balance short adds `balance_fallback`: the `param` and `value` it switched to, and `credits_required` for the call as sent. A page-cap rejection carries `reason: \"page_limit\"` with `max_page`, `requested_page` and `narrow_with`. An `INVALID_REQUEST` that sends no member of a required one-of group carries `fields` (the parameters of that group) and a one-sentence `next_step`. Other endpoints use it for their own taxonomies, such as the comment-lookup not-found reasons."
              },
              "status_url": {
                "type": "string",
                "format": "uri",
                "description": "Present on 5xx responses: the public status page, filtered to the platform when one is known."
              },
              "incident": {
                "type": "object",
                "description": "Present on a 5xx response when an open public incident explains the failure.",
                "properties": {
                  "id": {
                    "type": "string",
                    "example": "k3v9w2m8q1zt"
                  },
                  "title": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "example": "investigating"
                  },
                  "impact": {
                    "type": "string",
                    "example": "major"
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  }
                },
                "required": [
                  "id",
                  "title",
                  "status",
                  "impact",
                  "url"
                ]
              }
            },
            "required": [
              "type",
              "message",
              "status",
              "doc_url"
            ]
          },
          "credits_used": {
            "type": "integer",
            "description": "Net credits charged for this request. Error paths deduct-then-refund, so this is 0 in almost every case; a partial-coverage composite may keep the succeeded-leg cost."
          },
          "credits_remaining": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Credits left after this request when established. Some unauthenticated errors use null; on a prebilling route whose attempted balance lookup fails, this field is omitted instead of fabricated."
          },
          "request_id": {
            "type": "string",
            "description": "Unique request identifier - matches the X-Request-Id header. Quote it in support requests."
          }
        },
        "required": [
          "success",
          "error",
          "credits_used",
          "request_id"
        ]
      },
      "MonitorAlertRule": {
        "description": "A numeric condition evaluated against a monitor run and, for change operators, a previous comparable run.",
        "properties": {
          "metric": {
            "description": "Dot path to a numeric key in the recipe's own result, for example `coverage` or `summary.total`. On a numbers-only monitor (created with `track`) the metric must be one of `track.metrics`, and an `items[].<path>` metric is evaluated once per row: a fired alert then carries `row_id`. The special metric `rows_new` (numbers-only monitors only; it needs an `items[].` metric in `track.metrics` and is not itself tracked) counts the list rows this run has that the previous run did not: `{\"metric\":\"rows_new\",\"op\":\"gt\",\"value\":0}` alerts on any new post or video. It accepts only `gt` and `gte`, and is skipped on the first run. \"New\" means absent from the previous successful run's stored rows (capped at `max_rows`, default 100), so a row that drops off and returns later counts as new again. An empty-page run is refunded and is not a baseline, so on a sparse feed the first run that holds rows has no baseline and stays silent: the first new post after a quiet period may not alert. Use a daily cadence or leave `max_rows` headroom.",
            "type": "string"
          },
          "op": {
            "enum": [
              "gt",
              "lt",
              "gte",
              "lte",
              "abs_change_gt",
              "pct_change_gt",
              "pct_change_lt"
            ],
            "type": "string"
          },
          "value": {
            "type": "number"
          },
          "window": {
            "description": "Accepted and stored, but ignored: a change op always compares against the immediately preceding comparable run, whatever the value.",
            "enum": [
              "1d",
              "1w"
            ],
            "type": "string"
          }
        },
        "required": [
          "metric",
          "op",
          "value"
        ],
        "type": "object"
      },
      "EventEnvelope": {
        "description": "One event, identical in the feed and in every webhook batch. `id` is stable for one fact, so deduplicate on it.",
        "properties": {
          "id": {
            "description": "Deterministic event id: the same fact never mints a second event.",
            "example": "evt_k3m7q2xwcbvd4nph6trz5ycgda",
            "type": "string"
          },
          "type": {
            "description": "Dot-delimited lower-case type. Types written today: `discover.progress`, `discover.accounts_ready`, `discover.completed`, `discover.partial`, `discover.paused`, `discover.failed`, `discover.new_rows`, `discover.new_accounts`, `discover.new_items`, `discover.run_skipped`, `discover.run_failed`, `endpoint.disabled`, `mention.created`, `mention.deleted`, `stream.baseline.established`, `stream.coverage_gap`, `stream.paused`, `stream.resumed`, `stream.deleted`, `stream.budget.warning`, `stream.health`.",
            "example": "discover.completed",
            "type": "string"
          },
          "api_version": {
            "example": "2026-10-01",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "cursor": {
            "description": "The feed position right after this event. Send it as `cursor` to read what came after; an event id works too.",
            "example": "c_MS4xMi4xNzkyNDA0OTA0",
            "type": "string"
          },
          "subject": {
            "description": "The object the event is about, for example `{ kind: \"discover_run\", id: \"run_…\" }`.",
            "properties": {
              "id": {
                "type": "string"
              },
              "kind": {
                "type": "string"
              },
              "metadata": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "kind",
              "id"
            ],
            "type": [
              "object",
              "null"
            ]
          },
          "data": {
            "description": "The event's facts. Discover events carry counts and ids, never keywords or row text.",
            "type": "object"
          },
          "links": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Absolute URLs to the subject, such as `run` and `results` (for example `https://www.socialcrawl.dev/v1/prism/discover/runs/run_...`), the same in the feed and in webhook batches. Events served before 26/10/2026 carried relative paths.",
            "type": "object"
          }
        },
        "required": [
          "id",
          "type",
          "api_version",
          "created_at",
          "cursor",
          "subject",
          "data",
          "links"
        ],
        "type": "object"
      },
      "WebhookEndpoint": {
        "description": "A registered receiver. The signing secret is never part of it: it is returned once, by create and by rotate-secret.",
        "properties": {
          "id": {
            "example": "we_V1StGXR8Z5jdHi6BmyT",
            "type": "string"
          },
          "url": {
            "example": "https://example.com/hooks/socialcrawl",
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "types": {
            "description": "Subscribed event types: exact types, `ns.*` wildcards, or `*` for every type.",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "subject_ids": {
            "description": "Only events about these subjects (a run or schedule id); null for every subject.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "status": {
            "enum": [
              "enabled",
              "disabled"
            ],
            "type": "string"
          },
          "disabled_reason": {
            "description": "`gone` (the receiver answered 410), `failing` (5 consecutive days of failed deliveries), `key_revoked` (the creating key was revoked or deleted) or `user` (set with PATCH). Null while enabled.",
            "enum": [
              "gone",
              "failing",
              "key_revoked",
              "user",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "failing_since": {
            "description": "Start of the current run of failed deliveries; null while healthy.",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "api_version": {
            "example": "2026-10-01",
            "type": "string"
          },
          "secret_rotation": {
            "description": "Set during a rotation: until `previous_secret_expires_at` every delivery is signed with both secrets.",
            "properties": {
              "previous_secret_expires_at": {
                "format": "date-time",
                "type": "string"
              }
            },
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          },
          "next": {
            "description": "Ready relative URLs for the next calls.",
            "properties": {
              "attempts_url": {
                "type": "string"
              },
              "events_url": {
                "type": "string"
              },
              "rotate_secret_url": {
                "type": "string"
              },
              "self_url": {
                "type": "string"
              },
              "test_url": {
                "type": "string"
              }
            },
            "type": "object"
          }
        },
        "required": [
          "id",
          "url",
          "types",
          "status",
          "api_version",
          "created_at"
        ],
        "type": "object"
      },
      "WebhookAttempt": {
        "description": "One delivery attempt. `response_excerpt` is at most 1 KB of your receiver's answer.",
        "properties": {
          "id": {
            "type": "string"
          },
          "delivery_id": {
            "description": "The batch's `webhook-id`, the same on every retry.",
            "type": "string"
          },
          "test": {
            "type": "boolean"
          },
          "status": {
            "description": "Your receiver's HTTP status; null for a timeout, a refused host, a network error or a secret that could not be opened.",
            "type": [
              "integer",
              "null"
            ]
          },
          "outcome": {
            "enum": [
              "delivered",
              "failed",
              "gone",
              "retry_after",
              "timeout",
              "redirect",
              "refused_host",
              "secret_unavailable",
              "network_error"
            ],
            "type": "string"
          },
          "duration_ms": {
            "type": [
              "integer",
              "null"
            ]
          },
          "response_excerpt": {
            "type": [
              "string",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "delivery_id",
          "test",
          "status",
          "outcome",
          "created_at"
        ],
        "type": "object"
      },
      "EventBatch": {
        "description": "What every delivery carries, also for one event: at most 100 events or 256 KB. Each inner event keeps its own `id` and `cursor`; deduplicate on the inner ids. A test send marks its event `\"test\": true` with `\"cursor\": null`.",
        "properties": {
          "id": {
            "example": "bat_V1StGXR8Z5jdHi6BmyT",
            "type": "string"
          },
          "type": {
            "const": "event.batch",
            "type": "string"
          },
          "data": {
            "properties": {
              "events": {
                "items": {
                  "$ref": "#/components/schemas/EventEnvelope"
                },
                "maxItems": 100,
                "type": "array"
              }
            },
            "required": [
              "events"
            ],
            "type": "object"
          }
        },
        "required": [
          "id",
          "type",
          "data"
        ],
        "type": "object"
      },
      "ListeningRuleAst": {
        "description": "A rule as a tree of `and`, `or`, `not`, `term`, `phrase` and `field` nodes, as the dry run returns it in `data.stream.groups[].ast`.",
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "and": {
                "minItems": 1,
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ListeningRuleAst"
                }
              }
            },
            "required": [
              "and"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "or": {
                "minItems": 1,
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ListeningRuleAst"
                }
              }
            },
            "required": [
              "or"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "not": {
                "$ref": "#/components/schemas/ListeningRuleAst"
              }
            },
            "required": [
              "not"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "term": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "term"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "phrase": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "phrase"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "field": {
                "type": "string",
                "enum": [
                  "lang",
                  "country",
                  "platform",
                  "site",
                  "community"
                ]
              },
              "value": {
                "type": "string",
                "minLength": 1
              }
            },
            "required": [
              "field",
              "value"
            ],
            "additionalProperties": false
          }
        ]
      }
    }
  },
  "tags": [
    {
      "name": "web",
      "description": "Web endpoints"
    }
  ],
  "paths": {
    "/web/scrape": {
      "get": {
        "summary": "Scrape a web page",
        "description": "Fetches a public web page and returns clean content, metadata, and optional media in the unified WebPage schema. When the page scrapers cannot open a social platform profile or post (a LinkedIn profile, company page or post; an Instagram, Facebook, TikTok, X or Threads profile or post; a Reddit post, subreddit or user page; a YouTube video or channel), the page is read through that platform's own endpoint, for example /v1/linkedin/profile, at that endpoint's price: 5 credits for LinkedIn, 1 for the others. `page.native` then carries the endpoint (`endpoint`), what it charged (`credits_used`) and its full structured record (`data`). `page.content.markdown`, `html` and `raw_html` are written from that record rather than taken from the site's HTML, and `_warnings` says so. Only markdown, html and rawHtml requests are read this way. A page the scrapers can read keeps the scrape and its price.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_scrape",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 1,
        "x-credit-cost-min": 1,
        "x-credit-cost-max": 5,
        "x-credit-cost-formula": "1 credit for a standard scrape. `proxy=auto`, `proxy=enhanced` and `pdf_parse=true` hold 5 credits and settle to what the fetch actually used, so a page a plain request could read still costs 1. Nothing else moves the price: screenshots, tag filters, `wait_for` and a mobile viewport are included. A page that comes back as the site's own block or error page (HTTP 400 or more), a CAPTCHA, a sign-in wall or an empty shell is returned with `page_state` saying so and a `_warnings` line, and is not charged. When the page scrapers cannot open a LinkedIn, Instagram, Facebook, Reddit, TikTok, YouTube, X or Threads profile or post, the page is read through that platform's own endpoint and costs that endpoint's price instead: 5 credits for a LinkedIn profile, company page or post, 1 credit for the others. The scrape's own charge is refunded to 0, so you pay the platform price once, and Activity Logs show the platform call as its own row",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Public URL to fetch.",
            "schema": {
              "type": "string"
            },
            "example": "https://example.com"
          },
          {
            "name": "formats",
            "in": "query",
            "required": false,
            "description": "Comma-separated output formats such as markdown,screenshot. A social platform URL is read through its own endpoint only for markdown, html and rawHtml.",
            "schema": {
              "type": "string"
            },
            "example": "markdown"
          },
          {
            "name": "only_main_content",
            "in": "query",
            "required": false,
            "description": "Strip nav, headers, footers and sidebars and keep the article body. Default true; send false when you need the whole page, including the chrome. A markdown-only scrape of a site the primary scraper cannot fetch is served by a second extractor, which returns the whole page.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "wait_for",
            "in": "query",
            "required": false,
            "description": "Milliseconds to wait after load before capturing, for pages that render their content in JavaScript. Leave unset unless the page comes back empty or half-built.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "mobile",
            "in": "query",
            "required": false,
            "description": "Render in a mobile viewport with a mobile user agent. Use it when the site serves a different layout to phones.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "required": false,
            "description": "Hard cap on the page load in milliseconds, 1 to 30000. A page that exceeds it fails and is refunded rather than returning a partial capture.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "max_age",
            "in": "query",
            "required": false,
            "description": "Accept an upstream-cached copy up to this many milliseconds old, which is faster and still one credit. Default 172800000 (48 hours), or 0 (a live fetch) with `proxy=auto` or `proxy=enhanced`; send 0 to force a live fetch. A cached copy of a page that failed (HTTP 400 or more) is never served: it is fetched again, live.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "location_country",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code to fetch from, e.g. 'us' or 'de'. Use it for geo-varying pages such as pricing or availability.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "screenshot_full_page",
            "in": "query",
            "required": false,
            "description": "When a screenshot format is requested, capture the entire scrollable page instead of the visible viewport.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "include_tags",
            "in": "query",
            "required": false,
            "description": "CSV of CSS selectors or HTML tags to keep, e.g. 'article,main'. Everything outside them is dropped. Use it to pin extraction to a known container.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exclude_tags",
            "in": "query",
            "required": false,
            "description": "CSV of CSS selectors or HTML tags to drop, e.g. 'nav,footer,.cookie-banner'. Applied after include_tags.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "proxy",
            "in": "query",
            "required": false,
            "description": "Proxy tier: basic (the default), auto (a plain request first, then a stronger one if the site blocks it), or enhanced. Use auto when a plain scrape returns a block or CAPTCHA page. auto and enhanced read the page live unless you set max_age.",
            "schema": {
              "type": "string",
              "enum": [
                "basic",
                "auto",
                "enhanced"
              ]
            }
          },
          {
            "name": "pdf_parse",
            "in": "query",
            "required": false,
            "description": "Parse the target as a PDF and return its extracted text. Send it when the URL points at a PDF rather than an HTML page.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "block_ads",
            "in": "query",
            "required": false,
            "description": "Block ad and tracker requests, which is faster and quieter. Default true.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "remove_base64_images",
            "in": "query",
            "required": false,
            "description": "Drop inline base64-encoded images from the returned content instead of carrying them in the payload. Default true.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "Send `respond-async` to run this GET in the background instead of waiting on the socket (or add `?async=true` to the query). The answer is 202 with `data.job_id`, a `Location` header and `Retry-After`; read the result free at `GET /v1/prism/jobs/{job_id}`, where `results[0].response` is exactly what this call returns. Nothing is held at submit: the call is validated now and billed as this call when it runs. Use it for calls that can outlast your client's timeout (MCP and many HTTP clients stop at 30 s). `Idempotency-Key` returns the same job on a retry.",
            "schema": {
              "type": "string"
            },
            "example": "respond-async"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/scrape"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "page": {
                          "type": "object",
                          "description": "Canonical web page object with identity, content, media, extraction, and fetch provenance fields.",
                          "properties": {
                            "url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Requested URL."
                            },
                            "final_url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Final resolved URL after redirects."
                            },
                            "status_code": {
                              "type": [
                                "number",
                                "null"
                              ],
                              "description": "HTTP status code observed while fetching the page."
                            },
                            "scrape_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Opaque scrape identifier for follow-up interactions."
                            },
                            "fetched_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Fetch timestamp when reported."
                            },
                            "published_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "When the page was published: an ISO 8601 instant, or a YYYY-MM-DD date when only the day is printed. From the page's own article:published_time meta tag on a scrape, and from the news date or that tag on a search row. Null when the page states none."
                            },
                            "content": {
                              "type": "object",
                              "description": "Extracted page content.",
                              "properties": {
                                "markdown": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Markdown body."
                                },
                                "html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "HTML body."
                                },
                                "raw_html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Raw HTML body."
                                },
                                "summary": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Generated summary."
                                }
                              }
                            },
                            "media": {
                              "type": "object",
                              "description": "Short-lived media URLs returned for the page.",
                              "properties": {
                                "screenshot_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Screenshot URL when requested."
                                },
                                "audio_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Audio URL."
                                },
                                "video_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Video URL."
                                }
                              }
                            },
                            "extraction": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "description": "Structured extraction result when requested. On `web/extract` every value is checked against the page text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null and is listed in `extraction_check.removed`."
                            },
                            "extraction_check": {
                              "type": "object",
                              "description": "`web/extract` only. How each extracted value was checked against the page text. A value is null in `extraction` when it was a placeholder or the page does not state it; `removed` lists those fields with the value the extractor gave, so nothing is hidden. A value kept without a check (the check was unavailable, or the page is too long to check whole) is named in `unverified` and in `_warnings`.",
                              "properties": {
                                "checked": {
                                  "type": "integer",
                                  "description": "Non-null values examined."
                                },
                                "found_in_text": {
                                  "type": "integer",
                                  "description": "Values kept because they appear word for word on the page."
                                },
                                "confirmed": {
                                  "type": "integer",
                                  "description": "Values kept because a check found the page states them, for example in another language."
                                },
                                "removed": {
                                  "type": "array",
                                  "description": "Fields set to null, with the value the extractor gave.",
                                  "items": {
                                    "type": "object",
                                    "description": "One field set to null.",
                                    "properties": {
                                      "field": {
                                        "type": "string",
                                        "description": "Path of the field, e.g. `faq[2].answer`."
                                      },
                                      "value": {
                                        "description": "The value the extractor gave."
                                      },
                                      "reason": {
                                        "type": "string",
                                        "enum": [
                                          "placeholder",
                                          "not_on_page"
                                        ],
                                        "description": "`placeholder`: the value holds no answer, either a marker such as 'Not specified' or '/', or a bare link path such as `/sub/info.php` in a field that does not ask for a link. `not_on_page`: the page does not state it."
                                      }
                                    }
                                  }
                                },
                                "unverified": {
                                  "type": "array",
                                  "description": "Fields returned as extracted without a check.",
                                  "items": {
                                    "type": "string",
                                    "description": "Path of the field."
                                  }
                                }
                              }
                            },
                            "fetch": {
                              "type": "object",
                              "description": "Vendor-neutral fetch provenance.",
                              "properties": {
                                "cache_state": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache state when reported."
                                },
                                "cached_at": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache timestamp when reported."
                                },
                                "proxy_tier": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Proxy tier used for the fetch."
                                }
                              }
                            }
                          }
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "page"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/scrape",
                  "data": {
                    "page": {
                      "url": "https://example.com",
                      "final_url": "https://example.com/",
                      "status_code": 200,
                      "scrape_id": "01a0fca4-f04b-7369-9cd6-64e0f4d82214",
                      "fetched_at": null,
                      "content": {
                        "markdown": "Thisdomainisforuseindocumentationexampleswithoutneedingpermission.Thisisnotaservice,avoidrelyingonitfortestingandmonitoringpurposes.\n\nهذا النطاق مُخصص للاستخدام في أمثلة التوثيق دون الحاجة إلى إذن. هذه ليست خدمة، يُرجى تجنب الاعتماد عليها لأغراض الاختبار والمراقبة.\n\n该域名仅用于文档示例，无需获得许可。这并非一项服务，请勿将其用于测试和监控目的。\n\nL’usagedecedomaineestréservéàdesexemplesdedocumentation,sansautorisationpréalable.Ilnes’agitpasd’unservice;sonutilisationàdesfinsdetestoudesurveillanceestàéviter.\n\nДанныйдоменпредназначендляиспользованиявпримерахдокументациибезнеобходимостиполученияпредварительногоразрешения.Этонесервис;нерекомендуетсяегоиспользованиедлятестированияимониторинга.\n\nEstedominioestádestinadoalusoenejemplosdedocumentaciónsinnecesidaddepermiso.Estonoesunservicio,evitarutilizarlopararealizarpruebasomonitoreos.\n\n [Learn more](https://iana.org/help/example-domains)",
                        "html": null,
                        "raw_html": null,
                        "summary": null
                      },
                      "media": {
                        "screenshot_url": null,
                        "audio_url": null,
                        "video_url": null
                      },
                      "extraction": null,
                      "answer": null,
                      "highlights": null,
                      "change_tracking": null,
                      "page_count": null,
                      "total_page_count": null,
                      "fetch": {
                        "cache_state": "hit",
                        "cached_at": "2026-10-02T12:40:55.009Z",
                        "proxy_tier": null
                      },
                      "page_state": {
                        "value": "content",
                        "confidence": 0.85,
                        "skipped_judge": false
                      }
                    }
                  },
                  "credits_used": 1,
                  "request_id": "req_example000000",
                  "cached": false,
                  "credits_remaining": 9999
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/search": {
      "get": {
        "summary": "Search the web",
        "description": "Searches web, news, and image sources and returns a single normalized list of web page results.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_search",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 2,
        "x-credit-cost-min": 2,
        "x-credit-cost-max": 120,
        "x-credit-cost-formula": "2 credits per 10 results, so `limit` up to 10 costs 2, 11-20 costs 4, and the 100 maximum costs 20. `include_content=true` adds 1 credit per result, because it scrapes each result page as well as reading the SERP row - that is what takes a 100-result content search to the 120-credit ceiling. The hold settles down to the work actually done",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "Search query.",
            "schema": {
              "type": "string"
            },
            "example": "social media data api"
          },
          {
            "name": "sources",
            "in": "query",
            "required": false,
            "description": "Comma-separated sources: web,news,images.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "categories",
            "in": "query",
            "required": false,
            "description": "CSV of source categories to restrict the search to, e.g. 'github,research'. Omit for an unrestricted web search.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per source, from 1 to 100.",
            "schema": {
              "type": "integer"
            },
            "example": 10
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code the search is run from, e.g. 'US' (default) or 'GB'. Changes which results rank.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location",
            "in": "query",
            "required": false,
            "description": "Free-text locality to bias results towards, e.g. 'London, United Kingdom'. Finer-grained than country.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "time_range",
            "in": "query",
            "required": false,
            "description": "Recency window as a Google `tbs` value: 'qdr:d' past day, 'qdr:w' past week, 'qdr:m' past month, 'qdr:y' past year.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by_date",
            "in": "query",
            "required": false,
            "description": "Sort newest-first instead of by relevance. Combines with time_range rather than replacing it.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "include_domains",
            "in": "query",
            "required": false,
            "description": "CSV of domains to restrict results to, e.g. 'nytimes.com,ft.com'. Cannot be combined with exclude_domains - sending both is a 400 before billing.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exclude_domains",
            "in": "query",
            "required": false,
            "description": "CSV of domains to drop from the results. Cannot be combined with include_domains.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_content",
            "in": "query",
            "required": false,
            "description": "Also scrape each result and return its page content as markdown, not just the SERP row. This is what makes the call expensive: it scrapes one page per result, which is why the range runs to 120 credits.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "max_age_s",
            "in": "query",
            "required": false,
            "description": "The oldest cached copy you accept, in seconds. A copy that old or younger is served for 0 credits, with its age in `age_s` and the `X-Cache-Age` header. Without it a copy up to 120 seconds old is served. A lower value fetches fresh (billed) when the copy is older, and 0 always fetches live, like `Cache-Control: no-cache`.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 120
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "Send `respond-async` to run this GET in the background instead of waiting on the socket (or add `?async=true` to the query). The answer is 202 with `data.job_id`, a `Location` header and `Retry-After`; read the result free at `GET /v1/prism/jobs/{job_id}`, where `results[0].response` is exactly what this call returns. Nothing is held at submit: the call is validated now and billed as this call when it runs. Use it for calls that can outlast your client's timeout (MCP and many HTTP clients stop at 30 s). `Idempotency-Key` returns the same job on a retry.",
            "schema": {
              "type": "string"
            },
            "example": "respond-async"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/search"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "Canonical web page wrapper",
                            "properties": {
                              "page": {
                                "type": "object",
                                "description": "Canonical web page object with identity, content, media, extraction, and fetch provenance fields.",
                                "properties": {
                                  "url": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Requested URL."
                                  },
                                  "final_url": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Final resolved URL after redirects."
                                  },
                                  "status_code": {
                                    "type": [
                                      "number",
                                      "null"
                                    ],
                                    "description": "HTTP status code observed while fetching the page."
                                  },
                                  "scrape_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Opaque scrape identifier for follow-up interactions."
                                  },
                                  "fetched_at": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Fetch timestamp when reported."
                                  },
                                  "published_at": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "When the page was published: an ISO 8601 instant, or a YYYY-MM-DD date when only the day is printed. From the page's own article:published_time meta tag on a scrape, and from the news date or that tag on a search row. Null when the page states none."
                                  },
                                  "content": {
                                    "type": "object",
                                    "description": "Extracted page content.",
                                    "properties": {
                                      "markdown": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Markdown body."
                                      },
                                      "html": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "HTML body."
                                      },
                                      "raw_html": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Raw HTML body."
                                      },
                                      "summary": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Generated summary."
                                      }
                                    }
                                  },
                                  "media": {
                                    "type": "object",
                                    "description": "Short-lived media URLs returned for the page.",
                                    "properties": {
                                      "screenshot_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Screenshot URL when requested."
                                      },
                                      "audio_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Audio URL."
                                      },
                                      "video_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Video URL."
                                      }
                                    }
                                  },
                                  "extraction": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "description": "Structured extraction result when requested. On `web/extract` every value is checked against the page text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null and is listed in `extraction_check.removed`."
                                  },
                                  "extraction_check": {
                                    "type": "object",
                                    "description": "`web/extract` only. How each extracted value was checked against the page text. A value is null in `extraction` when it was a placeholder or the page does not state it; `removed` lists those fields with the value the extractor gave, so nothing is hidden. A value kept without a check (the check was unavailable, or the page is too long to check whole) is named in `unverified` and in `_warnings`.",
                                    "properties": {
                                      "checked": {
                                        "type": "integer",
                                        "description": "Non-null values examined."
                                      },
                                      "found_in_text": {
                                        "type": "integer",
                                        "description": "Values kept because they appear word for word on the page."
                                      },
                                      "confirmed": {
                                        "type": "integer",
                                        "description": "Values kept because a check found the page states them, for example in another language."
                                      },
                                      "removed": {
                                        "type": "array",
                                        "description": "Fields set to null, with the value the extractor gave.",
                                        "items": {
                                          "type": "object",
                                          "description": "One field set to null.",
                                          "properties": {
                                            "field": {
                                              "type": "string",
                                              "description": "Path of the field, e.g. `faq[2].answer`."
                                            },
                                            "value": {
                                              "description": "The value the extractor gave."
                                            },
                                            "reason": {
                                              "type": "string",
                                              "enum": [
                                                "placeholder",
                                                "not_on_page"
                                              ],
                                              "description": "`placeholder`: the value holds no answer, either a marker such as 'Not specified' or '/', or a bare link path such as `/sub/info.php` in a field that does not ask for a link. `not_on_page`: the page does not state it."
                                            }
                                          }
                                        }
                                      },
                                      "unverified": {
                                        "type": "array",
                                        "description": "Fields returned as extracted without a check.",
                                        "items": {
                                          "type": "string",
                                          "description": "Path of the field."
                                        }
                                      }
                                    }
                                  },
                                  "fetch": {
                                    "type": "object",
                                    "description": "Vendor-neutral fetch provenance.",
                                    "properties": {
                                      "cache_state": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Upstream cache state when reported."
                                      },
                                      "cached_at": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Upstream cache timestamp when reported."
                                      },
                                      "proxy_tier": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Proxy tier used for the fetch."
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque cursor for the next page. Pass it back as a query parameter on endpoints that support pagination. Present only when the upstream reports more results."
                        },
                        "total": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Total number of matching results, when the upstream provides a count. Omitted otherwise."
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/map": {
      "get": {
        "summary": "Map URLs on a site",
        "description": "Discovers URLs for a public site and returns them as a normalized WebPageList.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_map",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 1,
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Site URL to map.",
            "schema": {
              "type": "string"
            },
            "example": "https://example.com"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Optional path or keyword filter.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum URLs to return, up to 5000.",
            "schema": {
              "type": "integer"
            },
            "example": 100
          },
          {
            "name": "sitemap",
            "in": "query",
            "required": false,
            "description": "How to use the site's sitemap.xml: 'include' (default) to merge it with crawled links, 'only' to trust it alone, 'skip' to ignore it.",
            "schema": {
              "type": "string",
              "enum": [
                "include",
                "skip",
                "only"
              ]
            }
          },
          {
            "name": "include_subdomains",
            "in": "query",
            "required": false,
            "description": "Follow links onto subdomains of the root URL. Default true.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ignore_query_parameters",
            "in": "query",
            "required": false,
            "description": "Treat URLs differing only by query string as one URL, which keeps paginated and tracking-tagged duplicates out of the result. Default true.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "fresh",
            "in": "query",
            "required": false,
            "description": "Bypass the upstream cache and re-discover the site now. Slower; use it when the map is known to be stale.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "goal",
            "in": "query",
            "required": false,
            "description": "Optional. What you are looking for on the site, in your own words. The map still returns every URL, ranked, with skipped_urls listing the ones that look unlikely to hold it (each with p). Without this param the map is unchanged.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "max_age_s",
            "in": "query",
            "required": false,
            "description": "The oldest cached copy you accept, in seconds. A copy that old or younger is served for 0 credits, with its age in `age_s` and the `X-Cache-Age` header. Without it a copy up to 120 seconds old is served. A lower value fetches fresh (billed) when the copy is older, and 0 always fetches live, like `Cache-Control: no-cache`.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 120
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "Send `respond-async` to run this GET in the background instead of waiting on the socket (or add `?async=true` to the query). The answer is 202 with `data.job_id`, a `Location` header and `Retry-After`; read the result free at `GET /v1/prism/jobs/{job_id}`, where `results[0].response` is exactly what this call returns. Nothing is held at submit: the call is validated now and billed as this call when it runs. Use it for calls that can outlast your client's timeout (MCP and many HTTP clients stop at 30 s). `Idempotency-Key` returns the same job on a retry.",
            "schema": {
              "type": "string"
            },
            "example": "respond-async"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/map"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "Canonical web page wrapper",
                            "properties": {
                              "page": {
                                "type": "object",
                                "description": "Canonical web page object with identity, content, media, extraction, and fetch provenance fields.",
                                "properties": {
                                  "url": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Requested URL."
                                  },
                                  "final_url": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Final resolved URL after redirects."
                                  },
                                  "status_code": {
                                    "type": [
                                      "number",
                                      "null"
                                    ],
                                    "description": "HTTP status code observed while fetching the page."
                                  },
                                  "scrape_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Opaque scrape identifier for follow-up interactions."
                                  },
                                  "fetched_at": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Fetch timestamp when reported."
                                  },
                                  "published_at": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "When the page was published: an ISO 8601 instant, or a YYYY-MM-DD date when only the day is printed. From the page's own article:published_time meta tag on a scrape, and from the news date or that tag on a search row. Null when the page states none."
                                  },
                                  "content": {
                                    "type": "object",
                                    "description": "Extracted page content.",
                                    "properties": {
                                      "markdown": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Markdown body."
                                      },
                                      "html": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "HTML body."
                                      },
                                      "raw_html": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Raw HTML body."
                                      },
                                      "summary": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Generated summary."
                                      }
                                    }
                                  },
                                  "media": {
                                    "type": "object",
                                    "description": "Short-lived media URLs returned for the page.",
                                    "properties": {
                                      "screenshot_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Screenshot URL when requested."
                                      },
                                      "audio_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Audio URL."
                                      },
                                      "video_url": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Video URL."
                                      }
                                    }
                                  },
                                  "extraction": {
                                    "type": [
                                      "object",
                                      "null"
                                    ],
                                    "description": "Structured extraction result when requested. On `web/extract` every value is checked against the page text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null and is listed in `extraction_check.removed`."
                                  },
                                  "extraction_check": {
                                    "type": "object",
                                    "description": "`web/extract` only. How each extracted value was checked against the page text. A value is null in `extraction` when it was a placeholder or the page does not state it; `removed` lists those fields with the value the extractor gave, so nothing is hidden. A value kept without a check (the check was unavailable, or the page is too long to check whole) is named in `unverified` and in `_warnings`.",
                                    "properties": {
                                      "checked": {
                                        "type": "integer",
                                        "description": "Non-null values examined."
                                      },
                                      "found_in_text": {
                                        "type": "integer",
                                        "description": "Values kept because they appear word for word on the page."
                                      },
                                      "confirmed": {
                                        "type": "integer",
                                        "description": "Values kept because a check found the page states them, for example in another language."
                                      },
                                      "removed": {
                                        "type": "array",
                                        "description": "Fields set to null, with the value the extractor gave.",
                                        "items": {
                                          "type": "object",
                                          "description": "One field set to null.",
                                          "properties": {
                                            "field": {
                                              "type": "string",
                                              "description": "Path of the field, e.g. `faq[2].answer`."
                                            },
                                            "value": {
                                              "description": "The value the extractor gave."
                                            },
                                            "reason": {
                                              "type": "string",
                                              "enum": [
                                                "placeholder",
                                                "not_on_page"
                                              ],
                                              "description": "`placeholder`: the value holds no answer, either a marker such as 'Not specified' or '/', or a bare link path such as `/sub/info.php` in a field that does not ask for a link. `not_on_page`: the page does not state it."
                                            }
                                          }
                                        }
                                      },
                                      "unverified": {
                                        "type": "array",
                                        "description": "Fields returned as extracted without a check.",
                                        "items": {
                                          "type": "string",
                                          "description": "Path of the field."
                                        }
                                      }
                                    }
                                  },
                                  "fetch": {
                                    "type": "object",
                                    "description": "Vendor-neutral fetch provenance.",
                                    "properties": {
                                      "cache_state": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Upstream cache state when reported."
                                      },
                                      "cached_at": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Upstream cache timestamp when reported."
                                      },
                                      "proxy_tier": {
                                        "type": [
                                          "string",
                                          "null"
                                        ],
                                        "description": "Proxy tier used for the fetch."
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque cursor for the next page. Pass it back as a query parameter on endpoints that support pagination. Present only when the upstream reports more results."
                        },
                        "total": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Total number of matching results, when the upstream provides a count. Omitted otherwise."
                        },
                        "judged_urls": {
                          "type": "array",
                          "description": "Present when you sent `goal`. The URLs worth scraping for your goal, best first: those judged likely to hold it, and at least the top two. Every URL is still returned in `items`.",
                          "items": {
                            "type": "object",
                            "description": "One URL.",
                            "properties": {
                              "url": {
                                "type": "string",
                                "description": "The page URL."
                              },
                              "title": {
                                "type": "string",
                                "description": "The page title, when the map carried one."
                              }
                            }
                          }
                        },
                        "skipped_urls": {
                          "type": "array",
                          "description": "Present when you sent `goal`. The URLs that look unlikely to hold what you are looking for, each with the probability it does. They are still in `items`. Empty when the judgment could not run, so nothing is hidden.",
                          "items": {
                            "type": "object",
                            "description": "One skipped URL.",
                            "properties": {
                              "url": {
                                "type": "string",
                                "description": "The page URL."
                              },
                              "p": {
                                "type": "number",
                                "description": "Probability the page holds your goal, from 0 to 1."
                              }
                            }
                          }
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/map",
                  "data": {
                    "items": [
                      {
                        "page": {
                          "url": "https://example.com/",
                          "final_url": "https://example.com/",
                          "status_code": null,
                          "scrape_id": null,
                          "fetched_at": null,
                          "title": "Example Domain",
                          "description": null,
                          "content": {
                            "markdown": null,
                            "html": null,
                            "raw_html": null,
                            "summary": null
                          },
                          "media": {
                            "screenshot_url": null,
                            "audio_url": null,
                            "video_url": null
                          },
                          "extraction": null,
                          "answer": null,
                          "highlights": null,
                          "change_tracking": null,
                          "page_count": null,
                          "total_page_count": null,
                          "fetch": {
                            "cache_state": null,
                            "cached_at": null,
                            "proxy_tier": null
                          }
                        }
                      },
                      {
                        "page": {
                          "url": "https://www.example.com/",
                          "final_url": "https://www.example.com/",
                          "status_code": null,
                          "scrape_id": null,
                          "fetched_at": null,
                          "title": "Example Domain",
                          "description": null,
                          "content": {
                            "markdown": null,
                            "html": null,
                            "raw_html": null,
                            "summary": null
                          },
                          "media": {
                            "screenshot_url": null,
                            "audio_url": null,
                            "video_url": null
                          },
                          "extraction": null,
                          "answer": null,
                          "highlights": null,
                          "change_tracking": null,
                          "page_count": null,
                          "total_page_count": null,
                          "fetch": {
                            "cache_state": null,
                            "cached_at": null,
                            "proxy_tier": null
                          }
                        }
                      }
                    ],
                    "total": 3,
                    "dropped": 0
                  },
                  "credits_used": 1,
                  "request_id": "req_example000000",
                  "cached": false,
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false,
                    "page_size": 3
                  },
                  "credits_remaining": 9999
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/extract": {
      "get": {
        "summary": "Extract structured data from a web page",
        "description": "Fetches one web page and returns structured extraction output under the WebPage extraction field. Every extracted value is checked against the page's own text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null, and `page.extraction_check.removed` lists each such field with the value it had. A value stated in another language than the one you asked for is kept. Fields that could not be checked are kept as extracted and named in `page.extraction_check.unverified` and `_warnings`. Write the schema or prompt for what the page shows: a field the page never mentions will be null.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_extract",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "advanced",
        "x-credit-cost": 5,
        "x-credit-cost-min": 5,
        "x-credit-cost-max": 5,
        "x-credit-cost-formula": "A flat 5 credits. It runs through the metered pricer for consistency with the rest of the web surface, but nothing in your query changes the charge",
        "x-socialcrawl-oneOf": [
          [
            "schema",
            "prompt"
          ]
        ],
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Public URL to extract from.",
            "schema": {
              "type": "string"
            },
            "example": "https://example.com/pricing"
          },
          {
            "name": "schema",
            "in": "query",
            "required": false,
            "description": "JSON object schema describing the data to return. (one of: schema, prompt; at least one required)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "prompt",
            "in": "query",
            "required": false,
            "description": "Plain-language extraction instruction. (one of: schema, prompt; at least one required)",
            "schema": {
              "type": "string"
            },
            "example": "Extract plan names and prices."
          },
          {
            "name": "only_main_content",
            "in": "query",
            "required": false,
            "description": "Strip nav, headers, footers and sidebars and keep the article body. Default true; send false when you need the whole page, including the chrome.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "required": false,
            "description": "Hard cap on each page load in milliseconds, 1 to 30000. A page that exceeds it fails and is refunded rather than returning a partial capture.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "max_age",
            "in": "query",
            "required": false,
            "description": "Accept an upstream-cached copy up to this many milliseconds old, which is faster and costs the same. Default 172800000 (48 hours); send 0 to force a live fetch.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "proxy",
            "in": "query",
            "required": false,
            "description": "Proxy tier: basic, auto, or enhanced. Raise it only when a site blocks the default tier - enhanced is slower.",
            "schema": {
              "type": "string",
              "enum": [
                "basic",
                "auto",
                "enhanced"
              ]
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          },
          {
            "name": "Prefer",
            "in": "header",
            "required": false,
            "description": "Send `respond-async` to run this GET in the background instead of waiting on the socket (or add `?async=true` to the query). The answer is 202 with `data.job_id`, a `Location` header and `Retry-After`; read the result free at `GET /v1/prism/jobs/{job_id}`, where `results[0].response` is exactly what this call returns. Nothing is held at submit: the call is validated now and billed as this call when it runs. Use it for calls that can outlast your client's timeout (MCP and many HTTP clients stop at 30 s). `Idempotency-Key` returns the same job on a retry.",
            "schema": {
              "type": "string"
            },
            "example": "respond-async"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/extract"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "page": {
                          "type": "object",
                          "description": "Canonical web page object with identity, content, media, extraction, and fetch provenance fields.",
                          "properties": {
                            "url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Requested URL."
                            },
                            "final_url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Final resolved URL after redirects."
                            },
                            "status_code": {
                              "type": [
                                "number",
                                "null"
                              ],
                              "description": "HTTP status code observed while fetching the page."
                            },
                            "scrape_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Opaque scrape identifier for follow-up interactions."
                            },
                            "fetched_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Fetch timestamp when reported."
                            },
                            "published_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "When the page was published: an ISO 8601 instant, or a YYYY-MM-DD date when only the day is printed. From the page's own article:published_time meta tag on a scrape, and from the news date or that tag on a search row. Null when the page states none."
                            },
                            "content": {
                              "type": "object",
                              "description": "Extracted page content.",
                              "properties": {
                                "markdown": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Markdown body."
                                },
                                "html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "HTML body."
                                },
                                "raw_html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Raw HTML body."
                                },
                                "summary": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Generated summary."
                                }
                              }
                            },
                            "media": {
                              "type": "object",
                              "description": "Short-lived media URLs returned for the page.",
                              "properties": {
                                "screenshot_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Screenshot URL when requested."
                                },
                                "audio_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Audio URL."
                                },
                                "video_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Video URL."
                                }
                              }
                            },
                            "extraction": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "description": "Structured extraction result when requested. On `web/extract` every value is checked against the page text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null and is listed in `extraction_check.removed`."
                            },
                            "extraction_check": {
                              "type": "object",
                              "description": "`web/extract` only. How each extracted value was checked against the page text. A value is null in `extraction` when it was a placeholder or the page does not state it; `removed` lists those fields with the value the extractor gave, so nothing is hidden. A value kept without a check (the check was unavailable, or the page is too long to check whole) is named in `unverified` and in `_warnings`.",
                              "properties": {
                                "checked": {
                                  "type": "integer",
                                  "description": "Non-null values examined."
                                },
                                "found_in_text": {
                                  "type": "integer",
                                  "description": "Values kept because they appear word for word on the page."
                                },
                                "confirmed": {
                                  "type": "integer",
                                  "description": "Values kept because a check found the page states them, for example in another language."
                                },
                                "removed": {
                                  "type": "array",
                                  "description": "Fields set to null, with the value the extractor gave.",
                                  "items": {
                                    "type": "object",
                                    "description": "One field set to null.",
                                    "properties": {
                                      "field": {
                                        "type": "string",
                                        "description": "Path of the field, e.g. `faq[2].answer`."
                                      },
                                      "value": {
                                        "description": "The value the extractor gave."
                                      },
                                      "reason": {
                                        "type": "string",
                                        "enum": [
                                          "placeholder",
                                          "not_on_page"
                                        ],
                                        "description": "`placeholder`: the value holds no answer, either a marker such as 'Not specified' or '/', or a bare link path such as `/sub/info.php` in a field that does not ask for a link. `not_on_page`: the page does not state it."
                                      }
                                    }
                                  }
                                },
                                "unverified": {
                                  "type": "array",
                                  "description": "Fields returned as extracted without a check.",
                                  "items": {
                                    "type": "string",
                                    "description": "Path of the field."
                                  }
                                }
                              }
                            },
                            "fetch": {
                              "type": "object",
                              "description": "Vendor-neutral fetch provenance.",
                              "properties": {
                                "cache_state": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache state when reported."
                                },
                                "cached_at": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache timestamp when reported."
                                },
                                "proxy_tier": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Proxy tier used for the fetch."
                                }
                              }
                            }
                          }
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "page"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/extract",
                  "data": {
                    "page": {
                      "url": "https://example.com/pricing",
                      "final_url": "https://example.com/pricing",
                      "status_code": 404,
                      "scrape_id": "01a0fca4-fd91-722e-9e06-e3ef59ae1f08",
                      "fetched_at": null,
                      "content": {
                        "markdown": null,
                        "html": null,
                        "raw_html": null,
                        "summary": null
                      },
                      "media": {
                        "screenshot_url": null,
                        "audio_url": null,
                        "video_url": null
                      },
                      "extraction": {
                        "plans": [
                          {
                            "planName": "Example Domain 1",
                            "price": 0
                          },
                          {
                            "planName": "Example Domain 2",
                            "price": 0
                          }
                        ]
                      },
                      "answer": null,
                      "highlights": null,
                      "change_tracking": null,
                      "page_count": null,
                      "total_page_count": null,
                      "fetch": {
                        "cache_state": "hit",
                        "cached_at": "2026-10-02T12:32:15.056Z",
                        "proxy_tier": null
                      }
                    }
                  },
                  "credits_used": 5,
                  "request_id": "req_example000000",
                  "cached": false,
                  "credits_remaining": 9999
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/crawl": {
      "post": {
        "summary": "Start an async web crawl",
        "description": "Starts an async crawl job and returns a SocialCrawl job id (job_...). Poll or cancel it via GET/DELETE /v1/web/jobs/{job_id}; polling is the authoritative status.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_crawl",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 1,
        "x-credit-cost-min": 1,
        "x-credit-cost-max": 10000,
        "x-credit-cost-formula": "1 credit per page crawled. Submitting holds `limit` credits up front (default limit 10, max 10,000) and the unused portion is refunded when the job settles",
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "A repeat of an earlier submit with the same `Idempotency-Key`: the job it started.",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/crawl"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/crawl",
                  "data": {
                    "job_id": "job_9f3k2n8d1",
                    "kind": "crawl",
                    "resource": "crawl",
                    "status": "queued",
                    "progress": null,
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 10,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 10,
                  "credits_remaining": 990,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/crawl"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/crawl",
                  "data": {
                    "job_id": "job_9f3k2n8d1",
                    "kind": "crawl",
                    "resource": "crawl",
                    "status": "queued",
                    "progress": null,
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 10,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 10,
                  "credits_remaining": 990,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/crawl. Required: url.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer"
                  },
                  "max_depth": {
                    "type": "integer"
                  },
                  "allow_backward_links": {
                    "type": "boolean"
                  },
                  "allow_external_links": {
                    "type": "boolean"
                  },
                  "include_paths": {
                    "type": "string"
                  },
                  "exclude_paths": {
                    "type": "string"
                  },
                  "webhook_url": {
                    "type": "string"
                  },
                  "formats": {
                    "type": "string"
                  },
                  "goal": {
                    "type": "string"
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      }
    },
    "/web/batch-scrape": {
      "post": {
        "summary": "Start an async batch scrape",
        "description": "Starts an async batch scrape job for multiple URLs and returns a SocialCrawl job id (job_...). Poll or cancel it via GET/DELETE /v1/web/jobs/{job_id}.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_batch_scrape",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 1,
        "x-credit-cost-formula": "1 credit per URL submitted, held up front and refunded down to the URLs actually scraped when the job settles",
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "A repeat of an earlier submit with the same `Idempotency-Key`: the job it started.",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/batch-scrape"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/batch-scrape",
                  "data": {
                    "job_id": "job_7t2m4c9x5",
                    "kind": "batch_scrape",
                    "resource": "batch-scrape",
                    "status": "queued",
                    "progress": null,
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 2,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 2,
                  "credits_remaining": 998,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "202": {
            "description": "Accepted",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/batch-scrape"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/batch-scrape",
                  "data": {
                    "job_id": "job_7t2m4c9x5",
                    "kind": "batch_scrape",
                    "resource": "batch-scrape",
                    "status": "queued",
                    "progress": null,
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 2,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 2,
                  "credits_remaining": 998,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/batch-scrape. Required: urls.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "ignore_invalid_urls": {
                    "type": "boolean"
                  },
                  "formats": {
                    "type": "string"
                  },
                  "only_main_content": {
                    "type": "boolean"
                  },
                  "proxy": {
                    "type": "string",
                    "enum": [
                      "basic",
                      "auto",
                      "enhanced"
                    ]
                  },
                  "webhook_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "urls"
                ]
              }
            }
          }
        }
      }
    },
    "/web/jobs": {
      "get": {
        "summary": "List async web jobs",
        "description": "Lists async crawl, batch scrape, and agent jobs for the API key.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_jobs",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, capped at 100.",
            "schema": {
              "type": "integer"
            },
            "example": 20
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Previous response cursor.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/jobs"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                            "properties": {
                              "job_id": {
                                "type": "string",
                                "description": "The job id (`job_...`)."
                              },
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "crawl",
                                  "batch_scrape",
                                  "agent",
                                  "review_history"
                                ],
                                "description": "What the job runs."
                              },
                              "resource": {
                                "type": "string",
                                "description": "The endpoint that started it, e.g. `crawl`."
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "queued",
                                  "running",
                                  "polling",
                                  "succeeded",
                                  "failed",
                                  "cancelled",
                                  "expired"
                                ],
                                "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                              },
                              "progress": {
                                "description": "Progress as the job reports it, or null."
                              },
                              "invalid_urls": {
                                "description": "URLs the batch rejected, or null."
                              },
                              "result_ref": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Where a large result is stored, or null."
                              },
                              "result": {
                                "description": "The job's output once it has one, or null."
                              },
                              "error": {
                                "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                              },
                              "credits_hold": {
                                "type": "integer",
                                "description": "Credits held when the job started."
                              },
                              "credits_charged": {
                                "type": "integer",
                                "description": "Credits charged so far."
                              },
                              "created_at": {
                                "type": "string",
                                "description": "When the job was created, ISO 8601."
                              },
                              "updated_at": {
                                "type": "string",
                                "description": "When the job last changed, ISO 8601."
                              },
                              "expires_at": {
                                "type": "string",
                                "description": "When the job record expires, ISO 8601."
                              }
                            },
                            "required": [
                              "job_id",
                              "kind",
                              "resource",
                              "status",
                              "progress",
                              "invalid_urls",
                              "result_ref",
                              "result",
                              "error",
                              "credits_hold",
                              "credits_charged",
                              "created_at",
                              "updated_at",
                              "expires_at"
                            ]
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` for the next page."
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/jobs",
                  "data": {
                    "items": [
                      {
                        "job_id": "job_9f3k2n8d1",
                        "kind": "crawl",
                        "resource": "crawl",
                        "status": "succeeded",
                        "progress": {
                          "completed": 10,
                          "total": 10
                        },
                        "invalid_urls": null,
                        "result_ref": "web-jobs/job_9f3k2n8d1.json",
                        "result": null,
                        "error": null,
                        "credits_hold": 10,
                        "credits_charged": 10,
                        "created_at": "2026-07-09T09:30:00.000Z",
                        "updated_at": "2026-07-09T09:34:12.000Z",
                        "expires_at": "2026-07-16T09:30:00.000Z"
                      }
                    ],
                    "has_more": false,
                    "next_cursor": null,
                    "dropped": 0
                  },
                  "credits_used": 0,
                  "credits_remaining": 990,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false,
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false,
                    "page_size": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/jobs/{job_id}": {
      "get": {
        "summary": "Get an async web job",
        "description": "Returns the current status, billing settlement, progress, and any available result metadata for one async web job: crawl, batch scrape, or agent. Append /errors to the same path to list per-page failures and robots-blocked URLs (crawl and batch scrape jobs).",
        "tags": [
          "web"
        ],
        "operationId": "get_web_jobs_job_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "Job id (job_...) returned by POST /v1/web/crawl, POST /v1/web/batch-scrape, or POST /v1/web/agent as data.job_id.",
            "schema": {
              "type": "string"
            },
            "example": "job_9f3k2n8d1"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/jobs/{job_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/jobs/job_9f3k2n8d1",
                  "data": {
                    "job_id": "job_9f3k2n8d1",
                    "kind": "crawl",
                    "resource": "crawl",
                    "status": "succeeded",
                    "progress": {
                      "completed": 10,
                      "total": 10
                    },
                    "invalid_urls": null,
                    "result_ref": "web-jobs/job_9f3k2n8d1.json",
                    "result": null,
                    "error": null,
                    "credits_hold": 10,
                    "credits_charged": 10,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:34:12.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 990,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Cancel an async web job",
        "description": "Cancels one async web job of any kind (crawl, batch scrape, or agent), closes the upstream work when possible, and refunds any unsettled held credits.",
        "tags": [
          "web"
        ],
        "operationId": "delete_web_jobs_job_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "description": "Job id (job_...) returned by POST /v1/web/crawl, POST /v1/web/batch-scrape, or POST /v1/web/agent as data.job_id.",
            "schema": {
              "type": "string"
            },
            "example": "job_9f3k2n8d1"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/jobs/{job_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/jobs/job_9f3k2n8d1",
                  "data": {
                    "job_id": "job_9f3k2n8d1",
                    "kind": "crawl",
                    "resource": "crawl",
                    "status": "cancelled",
                    "progress": {
                      "completed": 3,
                      "total": 10
                    },
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 10,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:31:40.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/agent": {
      "post": {
        "summary": "Start an async web agent job",
        "description": "Runs a model-neutral browser agent task and returns a SocialCrawl async job id (job_...). Poll it via GET /v1/web/jobs/{job_id}.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_agent",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "premium",
        "x-credit-cost": 25,
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/agent"
                    },
                    "data": {
                      "type": "object",
                      "description": "An async web job. Poll `GET /v1/web/jobs/{job_id}` until `status` is final.",
                      "properties": {
                        "job_id": {
                          "type": "string",
                          "description": "The job id (`job_...`)."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "crawl",
                            "batch_scrape",
                            "agent",
                            "review_history"
                          ],
                          "description": "What the job runs."
                        },
                        "resource": {
                          "type": "string",
                          "description": "The endpoint that started it, e.g. `crawl`."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "running",
                            "polling",
                            "succeeded",
                            "failed",
                            "cancelled",
                            "expired"
                          ],
                          "description": "`queued`, `running` and `polling` are in progress; the rest are final."
                        },
                        "progress": {
                          "description": "Progress as the job reports it, or null."
                        },
                        "invalid_urls": {
                          "description": "URLs the batch rejected, or null."
                        },
                        "result_ref": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Where a large result is stored, or null."
                        },
                        "result": {
                          "description": "The job's output once it has one, or null."
                        },
                        "error": {
                          "description": "Why the job failed, e.g. `{ \"code\": ... }`, or null."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when the job started."
                        },
                        "credits_charged": {
                          "type": "integer",
                          "description": "Credits charged so far."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the job was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the job last changed, ISO 8601."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When the job record expires, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "job_id",
                        "kind",
                        "resource",
                        "status",
                        "progress",
                        "invalid_urls",
                        "result_ref",
                        "result",
                        "error",
                        "credits_hold",
                        "credits_charged",
                        "created_at",
                        "updated_at",
                        "expires_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/agent",
                  "data": {
                    "job_id": "job_2q8w6r4v3",
                    "kind": "agent",
                    "resource": "agent",
                    "status": "queued",
                    "progress": null,
                    "invalid_urls": null,
                    "result_ref": null,
                    "result": null,
                    "error": null,
                    "credits_hold": 25,
                    "credits_charged": 0,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z",
                    "expires_at": "2026-07-16T09:30:00.000Z"
                  },
                  "credits_used": 25,
                  "credits_remaining": 975,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/agent. Required: url, prompt.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "prompt": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string",
                    "enum": [
                      "spark-1-mini",
                      "spark-1-pro"
                    ]
                  }
                },
                "required": [
                  "url",
                  "prompt"
                ]
              }
            }
          }
        }
      }
    },
    "/web/monitors": {
      "post": {
        "summary": "Create a web monitor",
        "description": "Creates an upstream-backed monitor. Creation is free; every scheduled check bills its upstream cost plus 1 credit for orchestration, and keeps billing on the cadence you set until the monitor is paused or deleted. The response returns credits_booked, the upstream cost of a single check, so budget (credits_booked + 1) x checks per month before leaving one running.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_monitors",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "x-credit-cost-formula": "0 credits to create. Each scheduled check then bills its upstream cost plus 1 credit for orchestration, repeating on `cadence_minutes` until you pause or delete the monitor (a 5-minute cadence is 288 checks a day)",
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors"
                    },
                    "data": {
                      "type": "object",
                      "description": "A web monitor.",
                      "properties": {
                        "monitor_id": {
                          "type": "string",
                          "description": "The monitor id."
                        },
                        "url": {
                          "type": "string",
                          "description": "The URL it watches."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "scrape",
                            "search",
                            "map"
                          ],
                          "description": "How each check reads the URL."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "paused",
                            "deleted",
                            "error"
                          ],
                          "description": "Whether the monitor is running."
                        },
                        "params": {
                          "description": "The request each check sends."
                        },
                        "cadence_minutes": {
                          "type": "integer",
                          "description": "Minutes between checks."
                        },
                        "credits_booked": {
                          "type": "integer",
                          "description": "Credits booked per check."
                        },
                        "consecutive_failures": {
                          "type": "integer",
                          "description": "Failed checks in a row."
                        },
                        "last_check_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it last checked, ISO 8601, or null."
                        },
                        "next_check_at": {
                          "type": "string",
                          "description": "When it checks next, ISO 8601."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the monitor was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the monitor last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "monitor_id",
                        "url",
                        "mode",
                        "status",
                        "params",
                        "cadence_minutes",
                        "credits_booked",
                        "consecutive_failures",
                        "last_check_at",
                        "next_check_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors",
                  "data": {
                    "monitor_id": "wm_5d1p8s3k7",
                    "url": "https://example.com",
                    "mode": "scrape",
                    "status": "active",
                    "params": {
                      "url": "https://example.com",
                      "name": "Example monitor"
                    },
                    "cadence_minutes": 15,
                    "credits_booked": 1,
                    "consecutive_failures": 0,
                    "last_check_at": null,
                    "next_check_at": "2026-07-09T09:45:00.000Z",
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/monitors. Required: url.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "scrape",
                      "search"
                    ]
                  },
                  "cadence_minutes": {
                    "type": "integer"
                  },
                  "schedule_text": {
                    "type": "string"
                  },
                  "schedule_cron": {
                    "type": "string"
                  },
                  "timezone": {
                    "type": "string"
                  },
                  "webhook_url": {
                    "type": "string"
                  },
                  "query": {
                    "type": "string"
                  },
                  "goal": {
                    "type": "string"
                  },
                  "retention_days": {
                    "type": "integer"
                  },
                  "judge_enabled": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      },
      "get": {
        "summary": "List web monitors",
        "description": "Lists web monitors created by the current API key.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_monitors",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Pagination cursor.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer"
            },
            "example": 20
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "A web monitor.",
                            "properties": {
                              "monitor_id": {
                                "type": "string",
                                "description": "The monitor id."
                              },
                              "url": {
                                "type": "string",
                                "description": "The URL it watches."
                              },
                              "mode": {
                                "type": "string",
                                "enum": [
                                  "scrape",
                                  "search",
                                  "map"
                                ],
                                "description": "How each check reads the URL."
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "active",
                                  "paused",
                                  "deleted",
                                  "error"
                                ],
                                "description": "Whether the monitor is running."
                              },
                              "params": {
                                "description": "The request each check sends."
                              },
                              "cadence_minutes": {
                                "type": "integer",
                                "description": "Minutes between checks."
                              },
                              "credits_booked": {
                                "type": "integer",
                                "description": "Credits booked per check."
                              },
                              "consecutive_failures": {
                                "type": "integer",
                                "description": "Failed checks in a row."
                              },
                              "last_check_at": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "When it last checked, ISO 8601, or null."
                              },
                              "next_check_at": {
                                "type": "string",
                                "description": "When it checks next, ISO 8601."
                              },
                              "created_at": {
                                "type": "string",
                                "description": "When the monitor was created, ISO 8601."
                              },
                              "updated_at": {
                                "type": "string",
                                "description": "When the monitor last changed, ISO 8601."
                              }
                            },
                            "required": [
                              "monitor_id",
                              "url",
                              "mode",
                              "status",
                              "params",
                              "cadence_minutes",
                              "credits_booked",
                              "consecutive_failures",
                              "last_check_at",
                              "next_check_at",
                              "created_at",
                              "updated_at"
                            ]
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` for the next page."
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors",
                  "data": {
                    "items": [
                      {
                        "monitor_id": "wm_5d1p8s3k7",
                        "url": "https://example.com",
                        "mode": "scrape",
                        "status": "active",
                        "params": {
                          "url": "https://example.com"
                        },
                        "cadence_minutes": 15,
                        "credits_booked": 1,
                        "consecutive_failures": 0,
                        "last_check_at": "2026-07-09T09:15:00.000Z",
                        "next_check_at": "2026-07-09T09:45:00.000Z",
                        "created_at": "2026-07-08T18:00:00.000Z",
                        "updated_at": "2026-07-09T09:15:00.000Z"
                      }
                    ],
                    "has_more": false,
                    "next_cursor": null,
                    "dropped": 0
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false,
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false,
                    "page_size": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/monitors/{monitor_id}": {
      "get": {
        "summary": "Get a web monitor",
        "description": "Returns configuration, cadence, status, and billing metadata for one web monitor.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_monitors_monitor_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "monitor_id",
            "in": "path",
            "required": true,
            "description": "Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id.",
            "schema": {
              "type": "string"
            },
            "example": "wm_5d1p8s3k7"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors/{monitor_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "A web monitor.",
                      "properties": {
                        "monitor_id": {
                          "type": "string",
                          "description": "The monitor id."
                        },
                        "url": {
                          "type": "string",
                          "description": "The URL it watches."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "scrape",
                            "search",
                            "map"
                          ],
                          "description": "How each check reads the URL."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "paused",
                            "deleted",
                            "error"
                          ],
                          "description": "Whether the monitor is running."
                        },
                        "params": {
                          "description": "The request each check sends."
                        },
                        "cadence_minutes": {
                          "type": "integer",
                          "description": "Minutes between checks."
                        },
                        "credits_booked": {
                          "type": "integer",
                          "description": "Credits booked per check."
                        },
                        "consecutive_failures": {
                          "type": "integer",
                          "description": "Failed checks in a row."
                        },
                        "last_check_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it last checked, ISO 8601, or null."
                        },
                        "next_check_at": {
                          "type": "string",
                          "description": "When it checks next, ISO 8601."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the monitor was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the monitor last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "monitor_id",
                        "url",
                        "mode",
                        "status",
                        "params",
                        "cadence_minutes",
                        "credits_booked",
                        "consecutive_failures",
                        "last_check_at",
                        "next_check_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors/wm_5d1p8s3k7",
                  "data": {
                    "monitor_id": "wm_5d1p8s3k7",
                    "url": "https://example.com",
                    "mode": "scrape",
                    "status": "active",
                    "params": {
                      "url": "https://example.com"
                    },
                    "cadence_minutes": 15,
                    "credits_booked": 1,
                    "consecutive_failures": 0,
                    "last_check_at": "2026-07-09T09:15:00.000Z",
                    "next_check_at": "2026-07-09T09:45:00.000Z",
                    "created_at": "2026-07-08T18:00:00.000Z",
                    "updated_at": "2026-07-09T09:15:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a web monitor",
        "description": "Updates one web monitor's status or cadence while preserving its check history and billing metadata.",
        "tags": [
          "web"
        ],
        "operationId": "patch_web_monitors_monitor_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "monitor_id",
            "in": "path",
            "required": true,
            "description": "Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id.",
            "schema": {
              "type": "string"
            },
            "example": "wm_5d1p8s3k7"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors/{monitor_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "A web monitor.",
                      "properties": {
                        "monitor_id": {
                          "type": "string",
                          "description": "The monitor id."
                        },
                        "url": {
                          "type": "string",
                          "description": "The URL it watches."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "scrape",
                            "search",
                            "map"
                          ],
                          "description": "How each check reads the URL."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "paused",
                            "deleted",
                            "error"
                          ],
                          "description": "Whether the monitor is running."
                        },
                        "params": {
                          "description": "The request each check sends."
                        },
                        "cadence_minutes": {
                          "type": "integer",
                          "description": "Minutes between checks."
                        },
                        "credits_booked": {
                          "type": "integer",
                          "description": "Credits booked per check."
                        },
                        "consecutive_failures": {
                          "type": "integer",
                          "description": "Failed checks in a row."
                        },
                        "last_check_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it last checked, ISO 8601, or null."
                        },
                        "next_check_at": {
                          "type": "string",
                          "description": "When it checks next, ISO 8601."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the monitor was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the monitor last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "monitor_id",
                        "url",
                        "mode",
                        "status",
                        "params",
                        "cadence_minutes",
                        "credits_booked",
                        "consecutive_failures",
                        "last_check_at",
                        "next_check_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors/wm_5d1p8s3k7",
                  "data": {
                    "monitor_id": "wm_5d1p8s3k7",
                    "url": "https://example.com",
                    "mode": "scrape",
                    "status": "paused",
                    "params": {
                      "url": "https://example.com"
                    },
                    "cadence_minutes": 15,
                    "credits_booked": 1,
                    "consecutive_failures": 0,
                    "last_check_at": "2026-07-09T09:15:00.000Z",
                    "next_check_at": "2026-07-09T09:45:00.000Z",
                    "created_at": "2026-07-08T18:00:00.000Z",
                    "updated_at": "2026-07-09T09:32:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "description": "JSON body for web/monitors/{monitor_id}. Required: monitor_id.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "paused"
                    ]
                  },
                  "cadence_minutes": {
                    "type": "integer"
                  },
                  "schedule_text": {
                    "type": "string"
                  },
                  "schedule_cron": {
                    "type": "string"
                  },
                  "timezone": {
                    "type": "string"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a web monitor",
        "description": "Deletes one web monitor, stops future scheduled checks, and keeps historical check records available.",
        "tags": [
          "web"
        ],
        "operationId": "delete_web_monitors_monitor_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "monitor_id",
            "in": "path",
            "required": true,
            "description": "Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id.",
            "schema": {
              "type": "string"
            },
            "example": "wm_5d1p8s3k7"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors/{monitor_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "A web monitor.",
                      "properties": {
                        "monitor_id": {
                          "type": "string",
                          "description": "The monitor id."
                        },
                        "url": {
                          "type": "string",
                          "description": "The URL it watches."
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "scrape",
                            "search",
                            "map"
                          ],
                          "description": "How each check reads the URL."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "paused",
                            "deleted",
                            "error"
                          ],
                          "description": "Whether the monitor is running."
                        },
                        "params": {
                          "description": "The request each check sends."
                        },
                        "cadence_minutes": {
                          "type": "integer",
                          "description": "Minutes between checks."
                        },
                        "credits_booked": {
                          "type": "integer",
                          "description": "Credits booked per check."
                        },
                        "consecutive_failures": {
                          "type": "integer",
                          "description": "Failed checks in a row."
                        },
                        "last_check_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it last checked, ISO 8601, or null."
                        },
                        "next_check_at": {
                          "type": "string",
                          "description": "When it checks next, ISO 8601."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When the monitor was created, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When the monitor last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "monitor_id",
                        "url",
                        "mode",
                        "status",
                        "params",
                        "cadence_minutes",
                        "credits_booked",
                        "consecutive_failures",
                        "last_check_at",
                        "next_check_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors/wm_5d1p8s3k7",
                  "data": {
                    "monitor_id": "wm_5d1p8s3k7",
                    "url": "https://example.com",
                    "mode": "scrape",
                    "status": "deleted",
                    "params": {
                      "url": "https://example.com"
                    },
                    "cadence_minutes": 15,
                    "credits_booked": 1,
                    "consecutive_failures": 0,
                    "last_check_at": "2026-07-09T09:15:00.000Z",
                    "next_check_at": "2026-07-09T09:45:00.000Z",
                    "created_at": "2026-07-08T18:00:00.000Z",
                    "updated_at": "2026-07-09T09:35:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/monitors/{monitor_id}/checks": {
      "get": {
        "summary": "List web monitor checks",
        "description": "Lists checks recorded for one monitor.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_monitors_monitor_id_checks",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "monitor_id",
            "in": "path",
            "required": true,
            "description": "Monitor id (wm_...) returned by POST /v1/web/monitors as data.monitor_id.",
            "schema": {
              "type": "string"
            },
            "example": "wm_5d1p8s3k7"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many checks to return, newest first. Page size, capped at 100.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/monitors/{monitor_id}/checks"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "One check a monitor ran.",
                            "properties": {
                              "check_id": {
                                "type": "string",
                                "description": "The check id."
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "pending",
                                  "succeeded",
                                  "failed",
                                  "skipped"
                                ],
                                "description": "How the check ended."
                              },
                              "credits_charged": {
                                "type": "integer",
                                "description": "Credits charged for the check."
                              },
                              "result": {
                                "description": "What the check read, or null."
                              },
                              "error": {
                                "description": "Why the check failed, or null."
                              },
                              "checked_at": {
                                "type": "string",
                                "description": "When it ran, ISO 8601."
                              },
                              "created_at": {
                                "type": "string",
                                "description": "When it was recorded, ISO 8601."
                              }
                            },
                            "required": [
                              "check_id",
                              "status",
                              "credits_charged",
                              "result",
                              "error",
                              "checked_at",
                              "created_at"
                            ]
                          }
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/monitors/wm_5d1p8s3k7/checks",
                  "data": {
                    "items": [
                      {
                        "check_id": "wc_8n4b2j6h9",
                        "status": "succeeded",
                        "credits_charged": 1,
                        "result": null,
                        "error": null,
                        "checked_at": "2026-07-09T09:15:00.000Z",
                        "created_at": "2026-07-09T09:15:00.000Z"
                      }
                    ],
                    "dropped": 0
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false,
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false,
                    "page_size": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/sessions": {
      "post": {
        "summary": "Create an interactive web session",
        "description": "Creates a short-lived browser session for follow-up interactions.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_sessions",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "advanced",
        "x-credit-cost": 5,
        "x-credit-cost-formula": "20 credits per browser-hour, minimum 5. The hold is taken from `ttl_seconds` when the session is created (60s default = 5, the 3,600s maximum = 20) and settled when it closes",
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/sessions"
                    },
                    "data": {
                      "type": "object",
                      "description": "A browser session.",
                      "properties": {
                        "session_id": {
                          "type": "string",
                          "description": "The session id."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "browser",
                            "scrape"
                          ],
                          "description": "What the session drives."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "closing",
                            "closed",
                            "expired",
                            "failed"
                          ],
                          "description": "Whether the session is open."
                        },
                        "ttl_seconds": {
                          "type": "integer",
                          "description": "How long the session stays open."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when it opened."
                        },
                        "credits_billed": {
                          "type": "integer",
                          "description": "Credits billed so far."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When it closes on its own, ISO 8601."
                        },
                        "closed_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it was closed, ISO 8601, or null."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When it opened, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When it last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "session_id",
                        "kind",
                        "status",
                        "ttl_seconds",
                        "credits_hold",
                        "credits_billed",
                        "expires_at",
                        "closed_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/sessions",
                  "data": {
                    "session_id": "ws_3g7x1v5m2",
                    "kind": "browser",
                    "status": "active",
                    "ttl_seconds": 60,
                    "credits_hold": 5,
                    "credits_billed": 0,
                    "expires_at": "2026-07-09T09:31:00.000Z",
                    "closed_at": null,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z"
                  },
                  "credits_used": 5,
                  "credits_remaining": 995,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/sessions. Required: url.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "ttl_seconds": {
                    "type": "integer"
                  },
                  "activity_ttl_seconds": {
                    "type": "integer"
                  },
                  "stream_web_view": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      },
      "get": {
        "summary": "List interactive web sessions",
        "description": "Lists sessions created by the current API key.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_sessions",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer"
            },
            "example": 20
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/sessions"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Array of canonical web page wrappers ({ page })",
                          "items": {
                            "type": "object",
                            "description": "A browser session.",
                            "properties": {
                              "session_id": {
                                "type": "string",
                                "description": "The session id."
                              },
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "browser",
                                  "scrape"
                                ],
                                "description": "What the session drives."
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "active",
                                  "closing",
                                  "closed",
                                  "expired",
                                  "failed"
                                ],
                                "description": "Whether the session is open."
                              },
                              "ttl_seconds": {
                                "type": "integer",
                                "description": "How long the session stays open."
                              },
                              "credits_hold": {
                                "type": "integer",
                                "description": "Credits held when it opened."
                              },
                              "credits_billed": {
                                "type": "integer",
                                "description": "Credits billed so far."
                              },
                              "expires_at": {
                                "type": "string",
                                "description": "When it closes on its own, ISO 8601."
                              },
                              "closed_at": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "When it was closed, ISO 8601, or null."
                              },
                              "created_at": {
                                "type": "string",
                                "description": "When it opened, ISO 8601."
                              },
                              "updated_at": {
                                "type": "string",
                                "description": "When it last changed, ISO 8601."
                              }
                            },
                            "required": [
                              "session_id",
                              "kind",
                              "status",
                              "ttl_seconds",
                              "credits_hold",
                              "credits_billed",
                              "expires_at",
                              "closed_at",
                              "created_at",
                              "updated_at"
                            ]
                          }
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Pass as `cursor` for the next page."
                        },
                        "dropped": {
                          "type": "integer",
                          "description": "Number of upstream list items dropped because they could not be repaired to the endpoint schema. Valid list responses include 0.",
                          "example": 0
                        },
                        "empty_reason": {
                          "type": "string",
                          "enum": [
                            "no_matches",
                            "end_of_list",
                            "nothing_new_since",
                            "window_filtered",
                            "relevance_filtered",
                            "row_filtered",
                            "already_seen",
                            "source_empty_unverified"
                          ],
                          "description": "Present only when `items` is empty: why this page has no rows. Read it with `pagination.has_more`, which still says whether to keep paging. `no_matches`: The source answered and has no rows for this request. The same call returns the same answer, so change the query or the filters to get rows. `end_of_list`: This page was requested with a cursor and the list has no more rows. Stop paging. `nothing_new_since`: The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary. `window_filtered`: Every row on this page fell outside your date window (since, until, recent_days) or was a pinned post you excluded. Keep paging while pagination.has_more is true, or widen the window. `relevance_filtered`: relevance=filter judged every row on this page off-topic, and their ids are in data.relevance.dropped_ids. Keep paging while pagination.has_more is true, or send relevance=score to keep every row. `row_filtered`: A row filter you sent (such as country or min_views) kept no row on this page, and the report beside items counts what it dropped. Keep paging while pagination.has_more is true, or loosen the filter. `already_seen`: Every row on this page was one already returned to you on this walk, so there is nothing new here. Keep paging while pagination.has_more is true. `source_empty_unverified`: The source returned no rows and nothing confirmed it: no second source agreed, a source failed on the way, or a page mid-list came back empty. Retry later or with Cache-Control: no-cache, and keep paging while pagination.has_more is true.",
                          "example": "nothing_new_since"
                        },
                        "empty_reason_note": {
                          "type": "string",
                          "description": "Present only beside `empty_reason`: one or two sentences for a person or an agent saying what the code means and what to do next. The wording can improve; branch on `empty_reason`.",
                          "example": "The walk reached your since or stop_at_id boundary before any newer row, so nothing was published after it. Poll again later with the same boundary."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        }
                      },
                      "required": [
                        "dropped"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor state for this page. Present on every list response.",
                      "properties": {
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Opaque token to send back as `cursor` for the next page, or null at end-of-list. Pass it back verbatim; never decode or trim it.",
                          "example": "sc.eyJ2IjoyLCJjIjoiMiIsInAiOiJwYWdlIn0"
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Explicit stop signal. Prefer this over inspecting next_cursor or comparing against total.",
                          "example": true
                        },
                        "page_size": {
                          "type": "integer",
                          "description": "Number of items in THIS page.",
                          "example": 30
                        },
                        "stopped_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "since",
                            "known_id",
                            "end",
                            null
                          ],
                          "description": "Present only when the request sent `since` or `stop_at_id`. Why the walk stopped: `since` (this page reached a post older than the date), `known_id` (this page reached the post you already hold), `end` (the list ran out first), or null (not yet, keep walking with `next_cursor`).",
                          "example": "known_id"
                        },
                        "typical_page_size": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "How many items a full page of this endpoint usually holds for this request, or null when it has not been verified, as on a source whose page changes with the request's parameters. An endpoint that merges several sources can return more. A page_size below it while has_more is true is a short page from the source, not the end of the list.",
                          "example": 10
                        },
                        "next_page_credits": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "Credits the next page costs: what sending next_cursor back holds up front, priced from that request by the same pricer that charges it. Flat endpoints charge exactly this; metered endpoints refund what the page does not use; a cached page is free. Null when there is no next page.",
                          "example": 1
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more",
                        "page_size"
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached",
                    "pagination"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/sessions",
                  "data": {
                    "items": [
                      {
                        "session_id": "ws_3g7x1v5m2",
                        "kind": "browser",
                        "status": "active",
                        "ttl_seconds": 60,
                        "credits_hold": 5,
                        "credits_billed": 0,
                        "expires_at": "2026-07-09T09:31:00.000Z",
                        "closed_at": null,
                        "created_at": "2026-07-09T09:30:00.000Z",
                        "updated_at": "2026-07-09T09:30:00.000Z"
                      }
                    ],
                    "has_more": false,
                    "next_cursor": null,
                    "dropped": 0
                  },
                  "credits_used": 0,
                  "credits_remaining": 995,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false,
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false,
                    "page_size": 1
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/sessions/{session_id}": {
      "get": {
        "summary": "Get an interactive web session",
        "description": "Returns status, expiry, billing hold, and settlement metadata for one interactive web session.",
        "tags": [
          "web"
        ],
        "operationId": "get_web_sessions_session_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "description": "Session id (ws_...) returned by POST /v1/web/sessions as data.session_id.",
            "schema": {
              "type": "string"
            },
            "example": "ws_3g7x1v5m2"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/sessions/{session_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "A browser session.",
                      "properties": {
                        "session_id": {
                          "type": "string",
                          "description": "The session id."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "browser",
                            "scrape"
                          ],
                          "description": "What the session drives."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "closing",
                            "closed",
                            "expired",
                            "failed"
                          ],
                          "description": "Whether the session is open."
                        },
                        "ttl_seconds": {
                          "type": "integer",
                          "description": "How long the session stays open."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when it opened."
                        },
                        "credits_billed": {
                          "type": "integer",
                          "description": "Credits billed so far."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When it closes on its own, ISO 8601."
                        },
                        "closed_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it was closed, ISO 8601, or null."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When it opened, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When it last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "session_id",
                        "kind",
                        "status",
                        "ttl_seconds",
                        "credits_hold",
                        "credits_billed",
                        "expires_at",
                        "closed_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/sessions/ws_3g7x1v5m2",
                  "data": {
                    "session_id": "ws_3g7x1v5m2",
                    "kind": "browser",
                    "status": "active",
                    "ttl_seconds": 60,
                    "credits_hold": 5,
                    "credits_billed": 0,
                    "expires_at": "2026-07-09T09:31:00.000Z",
                    "closed_at": null,
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:00.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 995,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Close an interactive web session",
        "description": "Closes one interactive web session and settles any held credits that have not already been billed.",
        "tags": [
          "web"
        ],
        "operationId": "delete_web_sessions_session_id",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "description": "Session id (ws_...) returned by POST /v1/web/sessions as data.session_id.",
            "schema": {
              "type": "string"
            },
            "example": "ws_3g7x1v5m2"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/sessions/{session_id}"
                    },
                    "data": {
                      "type": "object",
                      "description": "A browser session.",
                      "properties": {
                        "session_id": {
                          "type": "string",
                          "description": "The session id."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "browser",
                            "scrape"
                          ],
                          "description": "What the session drives."
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "active",
                            "closing",
                            "closed",
                            "expired",
                            "failed"
                          ],
                          "description": "Whether the session is open."
                        },
                        "ttl_seconds": {
                          "type": "integer",
                          "description": "How long the session stays open."
                        },
                        "credits_hold": {
                          "type": "integer",
                          "description": "Credits held when it opened."
                        },
                        "credits_billed": {
                          "type": "integer",
                          "description": "Credits billed so far."
                        },
                        "expires_at": {
                          "type": "string",
                          "description": "When it closes on its own, ISO 8601."
                        },
                        "closed_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "When it was closed, ISO 8601, or null."
                        },
                        "created_at": {
                          "type": "string",
                          "description": "When it opened, ISO 8601."
                        },
                        "updated_at": {
                          "type": "string",
                          "description": "When it last changed, ISO 8601."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "session_id",
                        "kind",
                        "status",
                        "ttl_seconds",
                        "credits_hold",
                        "credits_billed",
                        "expires_at",
                        "closed_at",
                        "created_at",
                        "updated_at"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/sessions/ws_3g7x1v5m2",
                  "data": {
                    "session_id": "ws_3g7x1v5m2",
                    "kind": "browser",
                    "status": "closed",
                    "ttl_seconds": 60,
                    "credits_hold": 5,
                    "credits_billed": 0,
                    "expires_at": "2026-07-09T09:31:00.000Z",
                    "closed_at": "2026-07-09T09:30:40.000Z",
                    "created_at": "2026-07-09T09:30:00.000Z",
                    "updated_at": "2026-07-09T09:30:40.000Z"
                  },
                  "credits_used": 0,
                  "credits_remaining": 1000,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        }
      }
    },
    "/web/sessions/{session_id}/execute": {
      "post": {
        "summary": "Execute an interaction in a web session",
        "description": "Runs code in an existing browser session and returns the execution result.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_sessions_session_id_execute",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 0,
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "description": "SocialCrawl session id.",
            "schema": {
              "type": "string"
            },
            "example": "ws_3g7x1v5m2"
          },
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/sessions/{session_id}/execute"
                    },
                    "data": {
                      "type": "object",
                      "description": "What the code run in the session returned.",
                      "properties": {
                        "success": {
                          "type": "boolean",
                          "description": "Whether the code ran without an error."
                        },
                        "stdout": {
                          "type": "string",
                          "description": "Standard output."
                        },
                        "stderr": {
                          "type": "string",
                          "description": "Standard error."
                        },
                        "result": {
                          "description": "The value the code returned."
                        },
                        "exitCode": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "description": "The exit code."
                        },
                        "killed": {
                          "type": "boolean",
                          "description": "Whether the run was stopped for running too long."
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      }
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/sessions/ws_3g7x1v5m2/execute",
                  "data": {
                    "success": true,
                    "stdout": "Example Domain\n",
                    "stderr": "",
                    "result": null,
                    "exitCode": 0,
                    "killed": false
                  },
                  "credits_used": 0,
                  "credits_remaining": 995,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/sessions/{session_id}/execute. Required: session_id, code.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "language": {
                    "type": "string",
                    "enum": [
                      "node",
                      "python",
                      "bash"
                    ]
                  },
                  "timeout": {
                    "type": "integer"
                  }
                },
                "required": [
                  "code"
                ]
              }
            }
          }
        }
      }
    },
    "/web/parse": {
      "post": {
        "summary": "Parse an uploaded document",
        "description": "Uploads a document through multipart/form-data and returns parsed web-style content.",
        "tags": [
          "web"
        ],
        "operationId": "post_web_parse",
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "BearerAuth": []
          }
        ],
        "x-credit-tier": "standard",
        "x-credit-cost": 1,
        "parameters": [
          {
            "name": "Cache-Control",
            "in": "header",
            "required": false,
            "description": "Send `no-cache` to bypass the response cache and force a live fetch. Billed at the normal endpoint cost; the fresh result is written back to cache for the next caller. Only the `no-cache` directive triggers this. To accept an older cached copy, or to require a younger one, send the `max_age_s` query parameter instead. See the Response Schema guide for details.",
            "schema": {
              "type": "string"
            },
            "example": "no-cache"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional UUID that makes the request safely retriable. The key is reserved atomically per account before billing. A concurrent retry receives `409 IDEMPOTENCY_IN_PROGRESS` with a `Retry-After` of 1 second while the original is young, growing to a quarter of the time it has been running (at most 30); a crashed reservation expires after 5 minutes. A replay keeps the cached payload immutable except for billing metadata: `credits_used` becomes 0, `idempotent_replay` becomes true, and `credits_remaining` is refreshed to the current balance. A known current balance appears in both the body and `X-Credits-Remaining` header; no balance row resolves to 0. On a transient lookup failure, body `credits_remaining` is null and `X-Credits-Remaining` is omitted. Completed outcomes expire after 24 hours. If the claim store is unavailable, the request fails closed before billing. GET endpoints do not combine idempotent replay with Server-Sent Events: use `Accept: application/json` when sending this header. The unsupported key-plus-SSE combination is rejected before billing.",
            "schema": {
              "type": "string"
            },
            "example": "7a5e1b4c-2d8f-4a3b-9c1e-6e8b4d2a1f3c"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Credits-Used": {
                "description": "Net credits charged for this response. Idempotency replays report 0.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Credits-Remaining": {
                "description": "Current authoritative balance. Free cache hits and idempotency replays omit this header when the balance cannot be determined; body `credits_remaining` is null instead.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Key-Credits-Remaining": {
                "description": "Only when the API key has a credit limit: the credits this key may still spend under that limit, after this call (never below 0). Absent for a key with no limit. `GET /v1/keys/current` has the limit and what is used.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Idempotent-Replay": {
                "description": "Present with value `true` only when this response replays a settled idempotency record.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              },
              "Idempotent-Replayed": {
                "description": "Present with value `true` only when this response replays a settled idempotency record. Same meaning as `X-Idempotent-Replay`.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "description": "Whether the request succeeded",
                      "example": true
                    },
                    "platform": {
                      "type": "string",
                      "description": "Platform name",
                      "example": "web"
                    },
                    "endpoint": {
                      "type": "string",
                      "description": "API endpoint path",
                      "example": "/v1/web/parse"
                    },
                    "data": {
                      "type": "object",
                      "description": "Platform-specific response data",
                      "properties": {
                        "page": {
                          "type": "object",
                          "description": "Canonical web page object with identity, content, media, extraction, and fetch provenance fields.",
                          "properties": {
                            "url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Requested URL."
                            },
                            "final_url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Final resolved URL after redirects."
                            },
                            "status_code": {
                              "type": [
                                "number",
                                "null"
                              ],
                              "description": "HTTP status code observed while fetching the page."
                            },
                            "scrape_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Opaque scrape identifier for follow-up interactions."
                            },
                            "fetched_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Fetch timestamp when reported."
                            },
                            "published_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "When the page was published: an ISO 8601 instant, or a YYYY-MM-DD date when only the day is printed. From the page's own article:published_time meta tag on a scrape, and from the news date or that tag on a search row. Null when the page states none."
                            },
                            "content": {
                              "type": "object",
                              "description": "Extracted page content.",
                              "properties": {
                                "markdown": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Markdown body."
                                },
                                "html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "HTML body."
                                },
                                "raw_html": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Raw HTML body."
                                },
                                "summary": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Generated summary."
                                }
                              }
                            },
                            "media": {
                              "type": "object",
                              "description": "Short-lived media URLs returned for the page.",
                              "properties": {
                                "screenshot_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Screenshot URL when requested."
                                },
                                "audio_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Audio URL."
                                },
                                "video_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Video URL."
                                }
                              }
                            },
                            "extraction": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "description": "Structured extraction result when requested. On `web/extract` every value is checked against the page text: a placeholder such as 'Not specified', or a value the page does not state, comes back as null and is listed in `extraction_check.removed`."
                            },
                            "extraction_check": {
                              "type": "object",
                              "description": "`web/extract` only. How each extracted value was checked against the page text. A value is null in `extraction` when it was a placeholder or the page does not state it; `removed` lists those fields with the value the extractor gave, so nothing is hidden. A value kept without a check (the check was unavailable, or the page is too long to check whole) is named in `unverified` and in `_warnings`.",
                              "properties": {
                                "checked": {
                                  "type": "integer",
                                  "description": "Non-null values examined."
                                },
                                "found_in_text": {
                                  "type": "integer",
                                  "description": "Values kept because they appear word for word on the page."
                                },
                                "confirmed": {
                                  "type": "integer",
                                  "description": "Values kept because a check found the page states them, for example in another language."
                                },
                                "removed": {
                                  "type": "array",
                                  "description": "Fields set to null, with the value the extractor gave.",
                                  "items": {
                                    "type": "object",
                                    "description": "One field set to null.",
                                    "properties": {
                                      "field": {
                                        "type": "string",
                                        "description": "Path of the field, e.g. `faq[2].answer`."
                                      },
                                      "value": {
                                        "description": "The value the extractor gave."
                                      },
                                      "reason": {
                                        "type": "string",
                                        "enum": [
                                          "placeholder",
                                          "not_on_page"
                                        ],
                                        "description": "`placeholder`: the value holds no answer, either a marker such as 'Not specified' or '/', or a bare link path such as `/sub/info.php` in a field that does not ask for a link. `not_on_page`: the page does not state it."
                                      }
                                    }
                                  }
                                },
                                "unverified": {
                                  "type": "array",
                                  "description": "Fields returned as extracted without a check.",
                                  "items": {
                                    "type": "string",
                                    "description": "Path of the field."
                                  }
                                }
                              }
                            },
                            "fetch": {
                              "type": "object",
                              "description": "Vendor-neutral fetch provenance.",
                              "properties": {
                                "cache_state": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache state when reported."
                                },
                                "cached_at": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Upstream cache timestamp when reported."
                                },
                                "proxy_tier": {
                                  "type": [
                                    "string",
                                    "null"
                                  ],
                                  "description": "Proxy tier used for the fetch."
                                }
                              }
                            }
                          }
                        },
                        "_warnings": {
                          "type": "array",
                          "description": "Non-fatal notices about this response (field-map drift, clamped computed values). Advisory only: its presence never means the request failed. Omitted entirely when there is nothing to report, so treat absent as 'no warnings'.",
                          "items": {
                            "type": "string",
                            "description": "One advisory notice."
                          }
                        }
                      },
                      "required": [
                        "page"
                      ]
                    },
                    "credits_used": {
                      "type": "integer",
                      "description": "Number of credits consumed",
                      "example": 1
                    },
                    "credits_remaining": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Current account balance, or null when this response could not establish one. Free cache hits always return null because they do not establish an authoritative post-request balance. An idempotency replay also returns null when its current-balance lookup fails. It is never a fabricated zero, so a zero here means the account really is empty. The authoritative reading is the one on a billed response, which comes straight from the deduction.",
                      "example": 4999
                    },
                    "credits_remaining_note": {
                      "type": "string",
                      "description": "Present only when `credits_remaining` is null by design, as on a free cache hit: says why the balance was not read and where to read it.",
                      "example": "not read on cache hits (credits_used is 0); call GET /v1/credits/balance for the current balance"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "Unique request identifier for support",
                      "example": "req-abc123"
                    },
                    "cached": {
                      "type": "boolean",
                      "description": "Whether the response was served from cache",
                      "example": false
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Present only on a cache hit (`cached: true`): whole seconds since this copy was fetched, also sent as the `X-Cache-Age` header. Null for a copy stored before ages were recorded. Send `max_age_s` to set how old a copy you accept: a profile or post copy up to an hour old is served for 0 credits, and a lower value fetches fresh.",
                      "example": 340
                    },
                    "idempotent_replay": {
                      "type": "boolean",
                      "description": "True only when this response is an idempotency replay",
                      "example": true
                    },
                    "meta": {
                      "type": "object",
                      "description": "Optional. Present only when this call completed a pattern that one other call does more cheaply or in one step, at most once per pattern per API key per day. Send the header `X-SocialCrawl-Hints: off` to never receive it.",
                      "properties": {
                        "hint": {
                          "type": "object",
                          "description": "One suggestion: what to call instead and why, with the price when the suggested call costs more than the calls it replaces.",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "creator_card | batch_profiles | search_multi | incremental_sync | comment_labels | search_repeat | batch_comments | batch_jobs",
                              "example": "search_multi"
                            },
                            "message": {
                              "type": "string",
                              "description": "One sentence for a person or an agent to read.",
                              "example": "You searched \"matcha\" on 3 platforms one call at a time; /v1/search/multi returns each platform's own search rows in one call at the same price per platform, and a platform with no rows is not charged."
                            },
                            "method": {
                              "type": "string",
                              "description": "GET or POST.",
                              "example": "GET"
                            },
                            "path": {
                              "type": "string",
                              "description": "The call the hint points to.",
                              "example": "/v1/search/multi?query=matcha&platforms=instagram,tiktok,youtube"
                            }
                          }
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "platform",
                    "endpoint",
                    "data",
                    "credits_used",
                    "credits_remaining",
                    "request_id",
                    "cached"
                  ]
                },
                "example": {
                  "success": true,
                  "platform": "web",
                  "endpoint": "/v1/web/parse",
                  "data": {
                    "page": {
                      "url": null,
                      "final_url": null,
                      "status_code": null,
                      "scrape_id": "sc_6k9d3f8b4",
                      "fetched_at": "2026-07-09T09:30:02.000Z",
                      "title": "document.pdf",
                      "description": null,
                      "source_type": null,
                      "content": {
                        "markdown": "# Quarterly report\n\nRevenue grew 12%...",
                        "html": null,
                        "raw_html": null,
                        "summary": null
                      },
                      "media": {
                        "screenshot_url": null,
                        "audio_url": null,
                        "video_url": null
                      },
                      "extraction": null,
                      "answer": null,
                      "highlights": null,
                      "change_tracking": null,
                      "page_count": 3,
                      "total_page_count": 3,
                      "fetch": {
                        "cache_state": null,
                        "cached_at": null,
                        "proxy_tier": null
                      }
                    }
                  },
                  "credits_used": 1,
                  "credits_remaining": 999,
                  "request_id": "req-a1b2c3d4e5f6",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid request - missing or malformed parameters",
            "x-error-codes": [
              "INVALID_REQUEST",
              "COHORT_MEMBER_LIMIT_EXCEEDED",
              "COHORT_LIMIT_EXCEEDED",
              "COHORT_IDENTITY_PLATFORM_UNSUPPORTED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INVALID_REQUEST",
                    "message": "Invalid request - missing or malformed parameters",
                    "status": 400,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid API key",
            "x-error-codes": [
              "MISSING_API_KEY",
              "INVALID_API_KEY"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "MISSING_API_KEY",
                    "message": "Unauthorized - missing or invalid API key",
                    "status": 401,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "402": {
            "description": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
            "x-error-codes": [
              "INSUFFICIENT_CREDITS",
              "KEY_BUDGET_EXCEEDED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INSUFFICIENT_CREDITS",
                    "message": "Payment required - the account balance is too low, or the calling key has spent its own per-key credit limit",
                    "status": 402,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "404": {
            "description": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
            "x-error-codes": [
              "ENDPOINT_NOT_FOUND",
              "RESOURCE_NOT_FOUND",
              "RUN_NOT_FOUND",
              "SCHEDULE_NOT_FOUND",
              "STREAM_NOT_FOUND"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "ENDPOINT_NOT_FOUND",
                    "message": "Not found - the endpoint does not exist, or the requested resource was not found upstream",
                    "status": 404,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed - wrong HTTP verb for this endpoint",
            "x-error-codes": [
              "METHOD_NOT_ALLOWED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "METHOD_NOT_ALLOWED",
                    "message": "Method not allowed - wrong HTTP verb for this endpoint",
                    "status": 405,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "409": {
            "description": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_CONFLICT",
              "IDEMPOTENCY_IN_PROGRESS",
              "IDEMPOTENCY_REPLAY_UNAVAILABLE",
              "COHORT_IDENTITY_CONFLICT",
              "COHORT_QUERY_NOT_CANCELLABLE",
              "COHORT_QUERY_NOT_READY",
              "RUN_STATE_CONFLICT",
              "STREAM_STATE_CONFLICT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_CONFLICT",
                    "message": "Conflict - an idempotent request is still in flight, its completed body exceeded the replay limit, or the cohort resource is not in a state that allows this operation",
                    "status": 409,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "410": {
            "description": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
            "x-error-codes": [
              "RUN_EXPIRED",
              "CURSOR_EXPIRED"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RUN_EXPIRED",
                    "message": "Gone - the discover run's retention passed and its rows were deleted; start a new run. On the events feed, the cursor is older than the 30-day retention; restart from cursor=oldest",
                    "status": 410,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
            "x-error-codes": [
              "PAYLOAD_TOO_LARGE",
              "COHORT_RESULT_TOO_LARGE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "PAYLOAD_TOO_LARGE",
                    "message": "Payload too large: the JSON request body exceeds the 1 MB size limit and is rejected before parsing, or a single cohort result cannot fit beneath the 1 MB response-page ceiling",
                    "status": 413,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "422": {
            "description": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
            "x-error-codes": [
              "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
              "NEEDS_ASYNC"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
                    "message": "Idempotency payload mismatch - this Idempotency-Key was already used with a different request payload; or a discover request with async=false is over what one call can answer (NEEDS_ASYNC)",
                    "status": 422,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "429": {
            "description": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
            "x-error-codes": [
              "RATE_LIMITED",
              "CONCURRENCY_LIMIT"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "RATE_LIMITED",
                    "message": "Rate or concurrency limit exceeded. `RATE_LIMITED`: more than 600 requests in a 1-minute sliding window on this API key (headers `X-RateLimit-Limit`/`Remaining`/`Reset`; `Retry-After` is seconds until the window resets). `CONCURRENCY_LIMIT`: more than 50 simultaneous in-flight requests (headers `X-Concurrency-Limit`/`Remaining`; short static `Retry-After`). Both are unbilled. Honor `Retry-After`, then back off with jitter. See /docs/rate-limits.",
                    "status": 429,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
            "x-error-codes": [
              "INTERNAL_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "INTERNAL_ERROR",
                    "message": "Internal server error - an immediate refund is attempted; credits_used reports the committed net charge and exact compensation is queued if that refund cannot commit",
                    "status": 500,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "502": {
            "description": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Upstream error - the platform returned an error; an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 502,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "503": {
            "description": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
            "x-error-codes": [
              "SERVICE_UNAVAILABLE"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "SERVICE_UNAVAILABLE",
                    "message": "Service unavailable - the request is unbilled when rejected before deduction, otherwise an immediate refund is attempted and credits_used reports the committed net charge",
                    "status": 503,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          },
          "504": {
            "description": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
            "x-error-codes": [
              "UPSTREAM_ERROR"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "success": false,
                  "error": {
                    "type": "UPSTREAM_ERROR",
                    "message": "Gateway timeout - the request was still running close to the platform time limit and is answered as UPSTREAM_ERROR, the same code as 502. An immediate refund is attempted and credits_used reports the committed net charge. When details.retry_with is set, retryable is false and there is no Retry-After",
                    "status": 504,
                    "doc_url": "https://www.socialcrawl.dev/docs/errors"
                  },
                  "credits_used": 0,
                  "credits_remaining": null,
                  "request_id": "req-abc123"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "JSON body for web/parse. Required: file.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime_type": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        }
      }
    }
  },
  "x-full-spec": "https://www.socialcrawl.dev/v1/openapi.json"
}
