{
  "openapi": "3.0.3",
  "info": {
    "title": "Ujeebu API",
    "description": "Web scraping, search, AI extraction, and LLM passthrough — one credit pool, one API key, every endpoint. Source of truth for /docs/apis and the Postman collection. See https://ujeebu.com/docs.",
    "termsOfService": "https://ujeebu.com/terms-of-service",
    "contact": {
      "email": "support@ujeebu.com",
      "url": "https://ujeebu.com/contact"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://ujeebu.com/terms-of-service"
    },
    "version": "2.0.0"
  },
  "servers": [
    { "url": "https://api.ujeebu.com", "description": "Production" }
  ],
  "tags": [
    { "name": "Scrape",     "description": "Render any URL with JS, return HTML / PDF / screenshot." },
    { "name": "Extract",    "description": "Auto-detect article / product / listing and return clean structured data." },
    { "name": "AI Scraper", "description": "Natural-language or JSON-schema-driven structured extraction." },
    { "name": "SERP",       "description": "Google search results: organic, ads, news, images, maps." },
    { "name": "Markdown",   "description": "Convert any page to clean, LLM-optimized markdown." },
    { "name": "ChatGPT",    "description": "Forward a prompt to ChatGPT and return its reply." },
    { "name": "Gemini",     "description": "Forward a prompt to Gemini and return its reply." },
    { "name": "Account",    "description": "Plan, quota, used credits, balance, next billing date." }
  ],
  "security": [{ "ApiKeyAuth": [] }],
  "paths": {
    "/scrape": {
      "get": {
        "tags": ["Scrape"],
        "summary": "Render and scrape a URL",
        "description": "Render any URL through a headless Chrome browser and return final HTML (or PDF / screenshot via `response_type`). Supports JS execution, CSS extraction rules, proxies (rotating, premium, residential, mobile, custom), automatic CAPTCHA solving, and scroll behaviour. Failed fetches are not billed.",
        "operationId": "scrape",
        "parameters": [
          { "name": "url", "in": "query", "required": true, "description": "URL to render.", "schema": { "type": "string", "format": "uri" }, "example": "https://example.com" },
          { "name": "response_type", "in": "query", "description": "What to return. One of: html, raw, pdf, screenshot.", "schema": { "type": "string", "enum": ["html", "raw", "pdf", "screenshot"], "default": "html" } },
          { "name": "json", "in": "query", "description": "Wrap the response as JSON instead of raw content.", "schema": { "type": "boolean", "default": false } },
          { "name": "js", "in": "query", "description": "Execute JavaScript on the page.", "schema": { "type": "boolean", "default": false } },
          { "name": "js_timeout", "in": "query", "description": "Seconds to wait for the browser to load the page when js=true.", "schema": { "type": "integer", "default": 30 } },
          { "name": "custom_js", "in": "query", "description": "Base64-encoded JavaScript to execute in the page context (when js=true).", "schema": { "type": "string", "format": "byte" } },
          { "name": "wait_for", "in": "query", "description": "ms to wait, a CSS selector to wait for, or a JS expression. Requires js=true.", "schema": { "type": "string", "default": "0" } },
          { "name": "wait_for_timeout", "in": "query", "description": "Timeout (seconds) for wait_for. Falls back to timeout if 0.", "schema": { "type": "integer", "default": 0 } },
          { "name": "timeout", "in": "query", "description": "Request timeout in seconds.", "schema": { "type": "integer", "default": 60 } },
          { "name": "useragent", "in": "query", "description": "Override the default headless browser user agent.", "schema": { "type": "string" } },
          { "name": "cookies", "in": "query", "description": "Cookies to send with the request (header string or JSON object).", "schema": { "type": "string" } },
          { "name": "device", "in": "query", "description": "Device profile to render as.", "schema": { "type": "string", "enum": ["desktop", "mobile"], "default": "desktop" } },
          { "name": "window_width", "in": "query", "description": "Browser viewport width in px.", "schema": { "type": "integer" } },
          { "name": "window_height", "in": "query", "description": "Browser viewport height in px.", "schema": { "type": "integer" } },
          { "name": "screenshot_fullpage", "in": "query", "description": "When response_type=screenshot, capture the full page (vs. viewport).", "schema": { "type": "boolean", "default": true } },
          { "name": "screenshot_partial", "in": "query", "description": "CSS selector or JSON `{x,y,width,height}` to capture a region when response_type=screenshot.", "schema": { "type": "string" } },
          { "name": "scroll_down", "in": "query", "description": "Scroll down the page (requires js=true).", "schema": { "type": "boolean", "default": false } },
          { "name": "scroll_wait", "in": "query", "description": "ms between scrolls when scroll_down=true.", "schema": { "type": "integer", "default": 100 } },
          { "name": "progressive_scroll", "in": "query", "description": "Progressive scroll until page height stops growing.", "schema": { "type": "boolean", "default": false } },
          { "name": "scroll_callback", "in": "query", "description": "Base64-encoded JS callback returning boolean to control stop.", "schema": { "type": "string", "format": "byte" } },
          { "name": "scroll_to_selector", "in": "query", "description": "CSS selector to scroll to on each scroll step.", "schema": { "type": "string" } },
          { "name": "proxy_type", "in": "query", "description": "Proxy type.", "schema": { "type": "string", "enum": ["rotating", "advanced", "premium", "residential", "mobile", "custom"], "default": "rotating" } },
          { "name": "proxy_country", "in": "query", "description": "ISO 3166-1 alpha-2 code (premium / residential only).", "schema": { "type": "string", "default": "US" } },
          { "name": "auto_proxy", "in": "query", "description": "Auto-escalate proxy tier on failure. Billed only on the top tier attempted.", "schema": { "type": "boolean", "default": false } },
          { "name": "proxy_session", "in": "query", "description": "Alphanumeric (1-16) identifier — sticky proxy for 30 min.", "schema": { "type": "string" } },
          { "name": "custom_proxy", "in": "query", "description": "scheme://host:port — required when proxy_type=custom.", "schema": { "type": "string" } },
          { "name": "custom_proxy_username", "in": "query", "description": "Custom proxy username, if needed.", "schema": { "type": "string" } },
          { "name": "custom_proxy_password", "in": "query", "description": "Custom proxy password, if needed.", "schema": { "type": "string" } },
          { "name": "block_ads", "in": "query", "description": "Block ad resources.", "schema": { "type": "boolean", "default": false } },
          { "name": "block_resources", "in": "query", "description": "Block images/css/fonts. Defaults to false for screenshot/pdf.", "schema": { "type": "boolean", "default": true } },
          { "name": "extract_rules", "in": "query", "description": "JSON object defining extraction rules.", "schema": { "type": "string" } },
          { "name": "strip_tags", "in": "query", "description": "Comma-separated tag names/selectors to strip from result.", "schema": { "type": "string" } },
          { "name": "http_method", "in": "query", "description": "HTTP method used to request the target URL.", "schema": { "type": "string", "enum": ["GET", "POST", "PUT"], "default": "GET" } },
          { "name": "post_data", "in": "query", "description": "Body to forward to target URL when http_method=POST/PUT.", "schema": { "type": "string" } },
          { "name": "auto_captcha_solve", "in": "query", "description": "Auto-solve reCAPTCHA v2/v3, hCaptcha, Turnstile, FunCaptcha, GeeTest, and image CAPTCHAs.", "schema": { "type": "boolean", "default": false } },
          { "name": "auto_captcha_solve_timeout", "in": "query", "description": "Seconds before giving up on CAPTCHA solve.", "schema": { "type": "integer", "default": 120 } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/ScrapeOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Scrape"],
        "summary": "Render and scrape (POST body)",
        "description": "Same as GET /scrape but with parameters in the JSON body. Required when extract_rules or custom_js exceed query-string length.",
        "operationId": "scrapePost",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScrapeRequest" } } }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/ScrapeOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/extract": {
      "get": {
        "tags": ["Extract"],
        "summary": "Article / page extraction",
        "description": "Extract the main text and metadata of an article: clean body HTML, plain text, author, publish date, media, RSS feeds, and images. Auto-detects whether a URL is an article via the `is_article` probability score.",
        "operationId": "extract",
        "parameters": [
          { "name": "url", "in": "query", "required": true, "description": "URL of the article.", "schema": { "type": "string", "format": "uri" }, "example": "https://example.com/article" },
          { "name": "raw_html", "in": "query", "description": "Inline HTML to extract from. When set, the URL is only used for relative-link resolution.", "schema": { "type": "string" } },
          { "name": "js", "in": "query", "description": "Execute JavaScript. Set to `auto` to let the extractor decide.", "schema": { "type": "string", "default": "false" } },
          { "name": "text", "in": "query", "description": "Return extracted plain text.", "schema": { "type": "boolean", "default": true } },
          { "name": "html", "in": "query", "description": "Return extracted HTML body.", "schema": { "type": "boolean", "default": true } },
          { "name": "media", "in": "query", "description": "Return embedded media (YouTube, Twitter cards, etc.).", "schema": { "type": "boolean", "default": false } },
          { "name": "feeds", "in": "query", "description": "Return RSS / Atom feed URLs.", "schema": { "type": "boolean", "default": false } },
          { "name": "images", "in": "query", "description": "Return extracted images.", "schema": { "type": "boolean", "default": true } },
          { "name": "author", "in": "query", "description": "Return extracted author.", "schema": { "type": "boolean", "default": true } },
          { "name": "pub_date", "in": "query", "description": "Return extracted publish date.", "schema": { "type": "boolean", "default": true } },
          { "name": "partial", "in": "query", "description": "Char count or percentage (with %) to return. 0 means all.", "schema": { "type": "integer", "default": 0 } },
          { "name": "is_article", "in": "query", "description": "Include probability [0-1] of being an article.", "schema": { "type": "boolean", "default": true } },
          { "name": "quick_mode", "in": "query", "description": "Faster, less thorough parsing.", "schema": { "type": "boolean", "default": false } },
          { "name": "strip_tags", "in": "query", "description": "Comma-separated tag names / selectors to strip.", "schema": { "type": "string", "default": "form" } },
          { "name": "timeout", "in": "query", "description": "Request timeout in seconds.", "schema": { "type": "integer", "default": 60 } },
          { "name": "js_timeout", "in": "query", "description": "Seconds to wait for JS render. Defaults to timeout/2.", "schema": { "type": "integer" } },
          { "name": "scroll_down", "in": "query", "description": "Scroll the page (requires js).", "schema": { "type": "boolean", "default": false } },
          { "name": "scroll_wait", "in": "query", "description": "ms between scroll actions.", "schema": { "type": "integer", "default": 100 } },
          { "name": "scroll_percent", "in": "query", "description": "Percent of page to scroll (0-100).", "schema": { "type": "integer" } },
          { "name": "progressive_scroll", "in": "query", "description": "Progressive scroll for dynamic content.", "schema": { "type": "boolean", "default": false } },
          { "name": "wait_until", "in": "query", "description": "When page load is considered complete.", "schema": { "type": "string", "enum": ["load", "domcontentloaded", "networkidle", "commit"], "default": "load" } },
          { "name": "image_analysis", "in": "query", "description": "Filter out small images via min_image_width / _height.", "schema": { "type": "boolean", "default": true } },
          { "name": "min_image_width", "in": "query", "description": "Minimum image width in px (when image_analysis=true).", "schema": { "type": "integer", "default": 200 } },
          { "name": "min_image_height", "in": "query", "description": "Minimum image height in px (when image_analysis=true).", "schema": { "type": "integer", "default": 100 } },
          { "name": "image_timeout", "in": "query", "description": "Per-image fetch timeout in seconds.", "schema": { "type": "integer", "default": 2 } },
          { "name": "return_only_enclosed_text_images", "in": "query", "description": "Only return images enclosed within the article body.", "schema": { "type": "boolean", "default": true } },
          { "name": "main_image_in_html", "in": "query", "description": "Inline the main image into the extracted HTML body.", "schema": { "type": "boolean", "default": false } },
          { "name": "publisher_country", "in": "query", "description": "Extract publisher country.", "schema": { "type": "boolean", "default": false } },
          { "name": "publisher_tz", "in": "query", "description": "Extract publisher timezone.", "schema": { "type": "boolean", "default": false } },
          { "name": "heavy_mode", "in": "query", "description": "Deeper content analysis (slower).", "schema": { "type": "boolean", "default": false } },
          { "name": "text_length", "in": "query", "description": "Text-selection algorithm mode.", "schema": { "type": "string", "enum": ["conservative", "auto", "optimistic", "priority"], "default": "priority" } },
          { "name": "proxy_type", "in": "query", "description": "Proxy tier.", "schema": { "type": "string", "enum": ["rotating", "advanced", "premium", "residential", "mobile", "custom"], "default": "rotating" } },
          { "name": "proxy_country", "in": "query", "description": "ISO 3166-1 alpha-2 code for premium/residential.", "schema": { "type": "string", "default": "US" } },
          { "name": "custom_proxy", "in": "query", "description": "scheme://user:pass@host:port (when proxy_type=custom).", "schema": { "type": "string" } },
          { "name": "custom_proxy_username", "in": "query", "description": "Username for custom_proxy.", "schema": { "type": "string" } },
          { "name": "custom_proxy_password", "in": "query", "description": "Password for custom_proxy.", "schema": { "type": "string" } },
          { "name": "auto_proxy", "in": "query", "description": "Auto-escalate proxy tier on failure.", "schema": { "type": "boolean", "default": false } },
          { "name": "auto_premium_proxy", "in": "query", "description": "Auto-fallback to premium on rotating failure.", "schema": { "type": "boolean", "default": false } },
          { "name": "session_id", "in": "query", "description": "Sticky proxy session id (1-16 alphanumeric).", "schema": { "type": "string" } },
          { "name": "pagination", "in": "query", "description": "Concatenate multi-page articles.", "schema": { "type": "boolean", "default": true } },
          { "name": "pagination_max_pages", "in": "query", "description": "Max pages to follow when pagination=true.", "schema": { "type": "integer", "default": 30 } },
          { "name": "cookies", "in": "query", "description": "Cookies to send with the request.", "schema": { "type": "string" } },
          { "name": "html_timeout", "in": "query", "description": "Seconds for HTML fetch alone. Defaults to timeout.", "schema": { "type": "integer" } },
          { "name": "block_ads", "in": "query", "description": "Block ads during page render.", "schema": { "type": "boolean", "default": false } },
          { "name": "no_html_cache", "in": "query", "description": "Disable HTML caching for this request.", "schema": { "type": "boolean", "default": false } },
          { "name": "auto_captcha_solve", "in": "query", "description": "Auto-solve CAPTCHAs.", "schema": { "type": "boolean", "default": false } },
          { "name": "auto_captcha_solve_timeout", "in": "query", "description": "Timeout (ms) for CAPTCHA solve.", "schema": { "type": "integer", "default": 120000 } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/ExtractOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/ai-scraper": {
      "get": {
        "tags": ["AI Scraper"],
        "summary": "AI-driven structured extraction",
        "description": "Extract structured data from any page using a natural-language `prompt`, a JSON `schema`, or both. Inherits standard scraping parameters from /scrape (proxies, JS, captcha solve, etc.). Note: `auto_captcha_solve` defaults to `true` on this endpoint.",
        "operationId": "aiScraper",
        "parameters": [
          { "name": "url", "in": "query", "required": true, "description": "URL of the page to extract from.", "schema": { "type": "string", "format": "uri" }, "example": "https://example.com" },
          { "name": "prompt", "in": "query", "description": "Natural-language instruction. Required unless `schema` is provided.", "schema": { "type": "string" }, "example": "Extract the product name, price, and availability." },
          { "name": "schema", "in": "query", "description": "JSON schema defining the expected output. Stringified JSON.", "schema": { "type": "string" } },
          { "name": "temperature", "in": "query", "description": "LLM temperature (0.0-1.0).", "schema": { "type": "number", "default": 0.0, "minimum": 0.0, "maximum": 1.0 } },
          { "name": "js", "in": "query", "description": "Enable JavaScript rendering.", "schema": { "type": "boolean", "default": false } },
          { "name": "proxy_type", "in": "query", "description": "Proxy type (auto if unset).", "schema": { "type": "string", "enum": ["rotating", "advanced", "premium", "residential", "residential_us", "residential_geo"] } },
          { "name": "proxy_country", "in": "query", "description": "ISO 3166-1 alpha-2 code for premium/residential.", "schema": { "type": "string" } },
          { "name": "timeout", "in": "query", "description": "Request timeout in seconds.", "schema": { "type": "integer", "default": 120 } },
          { "name": "wait_for", "in": "query", "description": "CSS selector or ms to wait.", "schema": { "type": "string" } },
          { "name": "auto_captcha_solve", "in": "query", "description": "Auto-solve CAPTCHAs. Note: defaults to `true` here, unlike other endpoints.", "schema": { "type": "boolean", "default": true } },
          { "name": "auto_captcha_solve_timeout", "in": "query", "description": "ms before giving up on CAPTCHA solve.", "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/AIScraperOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["AI Scraper"],
        "summary": "AI-driven structured extraction (POST body)",
        "description": "Same as GET /ai-scraper but with JSON body. Recommended when passing a large `schema`.",
        "operationId": "aiScraperPost",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AIScraperRequest" } } }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/AIScraperOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/serp": {
      "get": {
        "tags": ["SERP"],
        "summary": "Google search results",
        "description": "Structured Google search results: organic, ads, AI overview, news, images, videos, maps. Supply either a `search` query OR a full Google `url`.",
        "operationId": "serp",
        "parameters": [
          { "name": "search", "in": "query", "description": "Search query. Either this or `url` is required.", "schema": { "type": "string" }, "example": "react component library" },
          { "name": "url", "in": "query", "description": "Full Google search page URL. Either this or `search` is required.", "schema": { "type": "string", "format": "uri" } },
          { "name": "search_type", "in": "query", "description": "Type of search.", "schema": { "type": "string", "enum": ["search", "images", "news", "videos", "maps", "ai"], "default": "search" } },
          { "name": "lang", "in": "query", "description": "ISO 639-1 language code.", "schema": { "type": "string", "default": "en" } },
          { "name": "location", "in": "query", "description": "Geographic location code (e.g. 'us', 'fr', 'uk').", "schema": { "type": "string", "default": "US" } },
          { "name": "device", "in": "query", "description": "Device profile.", "schema": { "type": "string", "enum": ["desktop", "mobile"], "default": "desktop" } },
          { "name": "results_count", "in": "query", "description": "Max results per page.", "schema": { "type": "integer", "default": 10 } },
          { "name": "page", "in": "query", "description": "Results page number.", "schema": { "type": "integer", "default": 1 } },
          { "name": "extra_params", "in": "query", "description": "Additional Google query params (e.g. '&safe=active').", "schema": { "type": "string" } },
          { "name": "with_html", "in": "query", "description": "Also return the raw HTML of the results page.", "schema": { "type": "boolean", "default": false } },
          { "name": "html_only", "in": "query", "description": "Return only the raw HTML, no parsing.", "schema": { "type": "boolean", "default": false } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/SerpOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/markdown": {
      "get": {
        "tags": ["Markdown"],
        "summary": "Convert any page to LLM-optimized markdown",
        "description": "Render a page and return clean, chunked, RAG-ready markdown. Supports raw / fit / BM25 content filters.",
        "operationId": "markdown",
        "parameters": [
          { "name": "url", "in": "query", "required": true, "description": "URL to convert.", "schema": { "type": "string", "format": "uri" }, "example": "https://example.com" },
          { "name": "filter", "in": "query", "description": "Content filter: raw (no filter), fit (default), or bm25 (query-driven).", "schema": { "type": "string", "enum": ["raw", "fit", "bm25"], "default": "fit" } },
          { "name": "query", "in": "query", "description": "Query string for BM25 filtering.", "schema": { "type": "string" } },
          { "name": "citations", "in": "query", "description": "Generate numbered citations. Defaults to false on POST.", "schema": { "type": "boolean", "default": true } },
          { "name": "js", "in": "query", "description": "Execute JavaScript.", "schema": { "type": "boolean", "default": true } },
          { "name": "proxy_type", "in": "query", "description": "Proxy type (auto if unset).", "schema": { "type": "string", "enum": ["rotating", "advanced", "premium", "residential", "residential_us", "residential_geo"] } },
          { "name": "proxy", "in": "query", "description": "Custom proxy URL.", "schema": { "type": "string" } },
          { "name": "wait", "in": "query", "description": "ms to wait after page load.", "schema": { "type": "integer" } },
          { "name": "wait_for_selector", "in": "query", "description": "CSS selector to wait for.", "schema": { "type": "string" } },
          { "name": "timeout", "in": "query", "description": "Request timeout in seconds.", "schema": { "type": "integer", "default": 60 } },
          { "name": "auto_captcha_solve", "in": "query", "description": "Auto-solve CAPTCHAs.", "schema": { "type": "boolean", "default": true } },
          { "name": "auto_captcha_solve_timeout", "in": "query", "description": "ms before giving up on CAPTCHA solve.", "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/MarkdownOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/chatgpt": {
      "get": {
        "tags": ["ChatGPT"],
        "summary": "Forward a prompt to ChatGPT",
        "description": "Send a prompt to ChatGPT and return its reply. URL-encode special characters in the prompt.",
        "operationId": "chatgpt",
        "parameters": [
          { "name": "prompt", "in": "query", "required": true, "description": "The prompt to send to ChatGPT.", "schema": { "type": "string" }, "example": "Explain rotating proxies in one paragraph." },
          { "name": "wait_for_load", "in": "query", "description": "Wait for the page to fully load before interaction.", "schema": { "type": "boolean", "default": true } },
          { "name": "timeout", "in": "query", "description": "Max time in ms to wait for the response.", "schema": { "type": "integer", "default": 60000 } },
          { "name": "enable_web_search", "in": "query", "description": "Enable ChatGPT's web search for up-to-date info.", "schema": { "type": "boolean", "default": false } },
          { "name": "return_html", "in": "query", "description": "Include the full page HTML in the response.", "schema": { "type": "boolean", "default": false } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/LLMOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/gemini": {
      "get": {
        "tags": ["Gemini"],
        "summary": "Forward a prompt to Gemini",
        "description": "Send a prompt to Gemini and return its reply. URL-encode special characters in the prompt.",
        "operationId": "gemini",
        "parameters": [
          { "name": "prompt", "in": "query", "required": true, "description": "The prompt to send to Gemini.", "schema": { "type": "string" }, "example": "Summarize OpenAPI 3.1 in 3 bullets." },
          { "name": "wait_for_load", "in": "query", "description": "Wait for the page to fully load before interaction.", "schema": { "type": "boolean", "default": true } },
          { "name": "timeout", "in": "query", "description": "Max time in ms to wait for the response.", "schema": { "type": "integer", "default": 60000 } },
          { "name": "proxy_type", "in": "query", "description": "Proxy type to use.", "schema": { "type": "string", "enum": ["premium", "rotating", "datacenter"] } },
          { "name": "proxy_country", "in": "query", "description": "ISO 3166-1 alpha-2 code.", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/LLMOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/account": {
      "get": {
        "tags": ["Account"],
        "summary": "Account usage",
        "description": "Read current plan, quota, credits used, balance, and next billing date. No parameters. Capped at 10 calls / minute / key.",
        "operationId": "account",
        "responses": {
          "200": { "$ref": "#/components/responses/AccountOK" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "ApiKey",
        "description": "Your Ujeebu API key. Get one at https://ujeebu.com/signup (free 14-day trial, 5,000 credits)."
      }
    },
    "schemas": {
      "ScrapeRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri", "example": "https://example.com" },
          "response_type": { "type": "string", "enum": ["html", "raw", "pdf", "screenshot"], "default": "html" },
          "json": { "type": "boolean", "default": false },
          "js": { "type": "boolean", "default": false },
          "wait_for": { "type": "string" },
          "proxy_type": { "type": "string", "enum": ["rotating", "advanced", "premium", "residential", "mobile", "custom"], "default": "rotating" },
          "proxy_country": { "type": "string", "default": "US" },
          "extract_rules": { "type": "object", "additionalProperties": true },
          "auto_captcha_solve": { "type": "boolean", "default": false }
        },
        "additionalProperties": true
      },
      "AIScraperRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri", "example": "https://example.com" },
          "prompt": { "type": "string", "example": "Extract product name, price, and availability." },
          "schema": { "type": "object", "additionalProperties": true },
          "temperature": { "type": "number", "default": 0.0 },
          "js": { "type": "boolean", "default": false },
          "proxy_type": { "type": "string" },
          "proxy_country": { "type": "string" },
          "timeout": { "type": "integer", "default": 120 },
          "auto_captcha_solve": { "type": "boolean", "default": true }
        },
        "additionalProperties": true
      },
      "AccountResponse": {
        "type": "object",
        "properties": {
          "userid": { "type": "string", "example": "90834083" },
          "plan": { "type": "string", "example": "Pro" },
          "quota": { "type": "integer", "example": 3000000 },
          "used": { "type": "integer", "example": 412050 },
          "used_percent": { "type": "number", "example": 13.74 },
          "total_requests": { "type": "integer", "example": 38917 },
          "balance": { "type": "number", "example": 0 },
          "concurrent_requests": { "type": "integer", "example": 100 },
          "next_billing_date": { "type": "string", "nullable": true, "example": "2026-07-03 00:00:00" },
          "days_till_next_billing": { "type": "integer", "example": 30 }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "code": { "type": "string" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid ApiKey header.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limit or budget cap hit. See `Retry-After` header.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ScrapeOK": {
        "description": "Rendered HTML (or PDF / screenshot / JSON wrapper depending on response_type).",
        "content": {
          "text/html": { "schema": { "type": "string" } },
          "application/json": { "schema": { "type": "object", "additionalProperties": true } },
          "application/pdf": { "schema": { "type": "string", "format": "binary" } },
          "image/png": { "schema": { "type": "string", "format": "binary" } }
        }
      },
      "ExtractOK": {
        "description": "Extracted article object.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "AIScraperOK": {
        "description": "AI-extracted structured data.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "SerpOK": {
        "description": "Structured search results.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "MarkdownOK": {
        "description": "Markdown body, JSON-wrapped with metadata.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "LLMOK": {
        "description": "LLM reply.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "AccountOK": {
        "description": "Current account state.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountResponse" } } }
      }
    }
  }
}
