{
  "openapi": "3.1.0",
  "info": {
    "title": "Orbita Search API",
    "version": "1.1.0",
    "summary": "One call returns up to 100 ranked sources with page text, citation offsets and publication dates.",
    "description": "Neural search built for AI agents rather than for people. Five explicit New Search depths range from Fast at $2.99 per 1,000 searches to Ultra at $13.99; Search + Compact ranges from $4.99 to $19.99 and applies the selected context ceiling. Every mode returns ranked pages with text, citation spans and publication dates attached. Private beta: keys are self-serve from the dashboard and new accounts start with $1 of credit.",
    "contact": { "name": "Orbita", "email": "hello@orbita.ai", "url": "https://orbita.dev/docs" },
    "license": { "name": "Proprietary — Preview Terms", "identifier": "LicenseRef-Orbita-Terms", "url": "https://orbita.dev/terms.html" },
    "x-orbita-pricing-source": "/pricing.json"
  },
  "servers": [{ "url": "https://api.orbita.dev/v1", "description": "Planned production endpoint; the repository currently runs a local private prototype" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [{ "name": "search", "description": "The only endpoint." }],
  "paths": {
    "/search": {
      "post": {
        "operationId": "search",
        "tags": ["search"],
        "summary": "Search the web and return ranked sources with their content",
        "description": "Send a question in natural language; receive the pages that answer it, ranked, with their text. Cost is determined by the selected mode, not by returning fewer rows inside that mode. A request that times out is not billed.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" } } }
        },
        "responses": {
          "200": {
            "description": "Ranked results.",
            "headers": {
              "X-RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Requests per second allowed on this key." },
              "X-RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "Requests left in the current window." },
              "X-RateLimit-Reset": { "schema": { "type": "number" }, "description": "Seconds until the window resets." }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "500": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" },
          "504": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A project key, prefixed orb_live_. Server-side only — a key in a browser bundle is a key someone else is now using."
      }
    },
    "responses": {
      "Error": {
        "description": "Every failure uses this envelope. Branch on error.type, never on error.message.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } }
      }
    },
    "schemas": {
      "SearchRequest": {
        "type": "object",
        "required": ["query"],
        "additionalProperties": false,
        "properties": {
          "query": {
            "type": "string",
            "maxLength": 2000,
            "description": "A question or description in natural language. Write it the way you would ask a colleague; keyword soup ranks worse here, not better.",
            "examples": ["buildings whose cooling systems copy termite mounds"]
          },
          "mode": {
            "type": "string", "enum": ["auto", "fast", "standard", "pro", "deep", "ultra"], "default": "auto",
            "description": "Five fixed depth budgets are fast 20→5, standard 100→10, pro 200→25, deep 300→50, ultra 500→100. Auto resolves to one fixed mode and never exceeds max_mode, max_cost_usd or max_results."
          },
          "max_mode": { "type": "string", "enum": ["fast", "standard", "pro", "deep", "ultra"], "default": "ultra" },
          "max_cost_usd": { "type": "number", "exclusiveMinimum": 0, "description": "Maximum USD for this request, in the same unit as usage.cost_usd." },
          "max_results": { "type": "integer", "minimum": 1, "maximum": 100, "default": 100 },
          "limit": {
            "type": "integer", "minimum": 1, "maximum": 100,
            "description": "How many results to return, capped by the selected mode. Returning fewer rows does not change that mode's price."
          },
          "content": {
            "type": "string", "enum": ["text", "markdown", "none"], "default": "text",
            "description": "text: clean page text. markdown: structure preserved. none: titles and URLs only, producing a smaller response. Its production latency effect is not yet measured."
          },
          "max_chars": {
            "type": "integer", "minimum": 200, "maximum": 20000, "default": 2000,
            "description": "Characters of page text per result. The single biggest lever on your token bill."
          },
          "lang": {
            "type": "array", "items": { "type": "string", "pattern": "^[a-z]{2}$" },
            "description": "ISO 639-1 codes to prefer. A preference, not a filter: a decisive source in another language still outranks a weak one in yours.",
            "examples": [["uk", "en"]]
          },
          "published_after": { "type": "string", "format": "date", "description": "Publication date, not crawl date. Undated pages are excluded when this is set." },
          "published_before": { "type": "string", "format": "date", "description": "Publication date, not crawl date. Undated pages are excluded when this is set." },
          "domains": {
            "type": "object", "additionalProperties": false,
            "properties": {
              "include": { "type": "array", "maxItems": 50, "items": { "type": "string" }, "description": "Hosts to restrict to. Subdomains are included." },
              "exclude": { "type": "array", "maxItems": 50, "items": { "type": "string" }, "description": "Hosts to drop. Applied after ranking, so this promotes what was behind them rather than shortening the list." }
            }
          },
          "highlights": {
            "type": "integer", "minimum": 0, "maximum": 5, "default": 0,
            "description": "Passages per result that most directly answer the query, with character offsets into text. Cite from these rather than asking the model to find the quote again."
          },
          "timeout_ms": {
            "type": "integer", "minimum": 500, "maximum": 30000, "default": 15000,
            "description": "On timeout you get 504 and are not billed. Set it below your agent's own deadline so you control the failure."
          },
          "links": { "type": "string", "enum": ["none", "relevant", "all"], "default": "relevant", "description": "Return no links, relevant discovered links, or all discovered links." },
          "context": {
            "type": "object", "additionalProperties": false, "default": { "enabled": false },
            "properties": {
              "enabled": { "type": "boolean", "default": false },
              "strategy": { "type": "string", "enum": ["summary", "evidence"], "default": "evidence" },
              "max_tokens": { "type": "integer", "enum": [500, 1000, 2000, 4000], "default": 1000 },
              "citations": { "type": "boolean", "default": true },
              "dates": { "type": "boolean", "default": true }
            }
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": ["id", "query", "results", "usage", "took_ms"],
        "properties": {
          "id": { "type": "string", "description": "Request id. Quote it when reporting a problem." },
          "requested_mode": { "type": "string", "enum": ["auto", "fast", "standard", "pro", "deep", "ultra"] },
          "resolved_mode": { "type": "string", "enum": ["fast", "standard", "pro", "deep", "ultra"] },
          "selection_reason": { "type": "string" },
          "query": {
            "type": "object",
            "properties": {
              "text": { "type": "string" },
              "language": { "type": "string", "description": "What language we decided the question was in. A wrong guess here explains most surprising result sets." }
            }
          },
          "results": { "type": "array", "items": { "$ref": "#/components/schemas/Result" } },
          "context": { "type": ["object", "null"], "description": "Optional source-grounded summary or evidence package. Evidence blocks are assembled from originals." },
          "sources": { "type": "array", "items": { "type": "object" } },
          "links": { "type": "array", "items": { "type": "object" } },
          "coverage": { "type": "array", "items": { "type": "object" } },
          "usage": {
            "type": "object",
            "properties": {
              "searches": { "type": "integer" },
              "results": { "type": "integer" },
              "context_input_tokens": { "type": "integer" },
              "context_output_tokens": { "type": "integer" },
              "cost_usd": { "type": "number", "description": "What this call actually cost. No end-of-month arithmetic." }
            }
          },
          "took_ms": { "type": "integer", "description": "Server-side time, excluding your round trip." }
        }
      },
      "Result": {
        "type": "object",
        "required": ["url", "title", "site", "published", "retrieved", "score"],
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "title": { "type": "string" },
          "site": { "type": "string", "description": "Registrable domain, for grouping and display." },
          "published": {
            "type": ["string", "null"], "format": "date",
            "description": "Null when the page carries no trustworthy date. We would rather admit that than invent one from the crawl time."
          },
          "retrieved": { "type": "string", "format": "date-time", "description": "When we last fetched the page. The gap from published tells you how stale the text may be." },
          "language": { "type": "string" },
          "score": {
            "type": "number", "minimum": 0, "maximum": 1,
            "description": "Comparable within this response only. Do not persist it, do not threshold on it, do not compare it across queries."
          },
          "text": { "type": ["string", "null"], "description": "Page text conditioned by Orbita Gravity: repeated chrome and boilerplate removed while evidence blocks retain source provenance; truncated to max_chars on a word boundary. Null when content is none." },
          "highlights": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "text": { "type": "string" },
                "start": { "type": "integer", "description": "Offset into result.text, so text.slice(start, end) is the exact quote." },
                "end": { "type": "integer" }
              }
            }
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["type", "message", "retryable", "request_id"],
            "properties": {
              "type": {
                "type": "string",
                "enum": ["invalid_request", "invalid_key", "quota_exhausted", "query_too_long", "unsupported_filter", "rate_limited", "internal", "capacity", "timeout"],
                "description": "Stable machine-readable cause. Messages get reworded; these do not."
              },
              "message": { "type": "string", "description": "Human-readable, names the offending field where there is one." },
              "retryable": { "type": "boolean", "description": "True for rate_limited, internal, capacity and timeout. Retry those with exponential backoff and jitter; the rest will fail again." },
              "retry_after": { "type": "number", "description": "Seconds to wait, when the server knows. Honour it in preference to your own backoff." },
              "request_id": { "type": "string" },
              "docs": { "type": "string", "format": "uri" }
            }
          }
        }
      }
    }
  }
}
