SerpKite Monitors API

The Monitors API from SerpKite — 4 operation(s) for monitors.

Operations 7

POST /v1/monitors Create a monitor — a saved /v1/search or /v1/news request (new results) or a… #
GET /v1/monitors List the account's monitors #
GET /v1/monitors/{id} Get a monitor #
PATCH /v1/monitors/{id} Update a monitor: name, interval, webhook_url (empty string removes it)… #
DELETE /v1/monitors/{id} Delete a monitor #
GET /v1/monitors/{id}/runs A monitor's run history, newest first (runs are kept 30 days, their results 24… #
POST /v1/monitors/{id}/run Run a monitor now (it becomes due at the next scheduler poll, within about 30… #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/serpkite-com:serpkite-com-monitors-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

serpkite-com-monitors-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: SerpKite SERP Monitors API
  version: 1.12.4
  description: 'Public search API (api.serpkite.com). Google results as clean JSON or Markdown

    for AI agents, plus a Google Custom Search–compatible endpoint. Official SDKs:

    `serpkite` on npm and PyPI, `github.com/serpkite/serpkite-go`. **Auth:** `Authorization: Bearer skt_live_…`. A bare `Authorization: skt_live_…`

    (no scheme) is also accepted, for MCP hosts that can''t add one. GET requests

    may pass `?api_key=` instead. `/customsearch/v1` also accepts Google''s `?key=`. **Bodies:** every vertical accepts `POST` with a JSON object or `GET` with the

    same parameters in the query string (only `GET /v1/search` is spelled out

    here). Unknown parameters are rejected with 400, and so is a parameter the

    endpoint would ignore (e.g. `time` on `/v1/scholar`, `ll` on `/v1/search`). To run many queries at once,

    use `POST /v1/batches`. **Formats:** `format=json` (default) returns the schemas below. `format=markdown` returns `text/markdown` for every vertical. `format=compact` and `fields=` return a reduced JSON object (same top-level

    keys, fewer item fields, `meta` always kept), so required item fields may be

    absent there. **Responses** share one envelope: `request` (the normalised request),

    `results` (the vertical''s primary list), vertical-specific extras and `meta`. All keys are snake_case. **Credits** are decimal numbers (e.g. `0.5`). Failed, empty and blocked

    responses are refunded. Every billed response carries `X-Credits-Used`,

    `X-Credits-Remaining`, `X-Cost-USD`, `X-Cache` (`HIT`|`MISS`), `X-Latency-Ms`,

    `X-Tokens-Estimate` and `X-Request-Id`. **Errors** share one body: `{"error":{"code","message","request_id"}}`. Upstream failures (`upstream_error`, `upstream_blocked`, `upstream_timeout`)

    are `503` with `Retry-After`, never billed and safe to retry. The API never

    returns `502` or `504`. `429` also carries `Retry-After`: `rate_limited` is

    the per-second limit; `daily_limit_reached` is the free daily limit of an

    account that never bought a pack, which resets at 00:00 UTC (the message

    names the limit and the billing page; buying any pack removes it). `403 account_restricted` means the account''s free use is paused because it

    appears to be one of several accounts run by the same person (same device

    or network, or requests in step with other accounts) or uses a disposable

    email address; buying any pack lifts it. **CORS:** `Access-Control-Allow-Origin: *` without credentials; the `X-*`

    headers above are exposed, so a browser playground can call the API with a key. The keyless `/playground/search` is the exception: it only answers serpkite.com

    and app.serpkite.com (other `Origin`s get 403). Credit table: search/news/images/videos/maps/places/shopping/scholar/patents/

    webpage/customsearch = 1 per page; place details = 1; `num=100` depth bundle = 7; reviews = 1 per 10;

    autocomplete = 0.5; `include_content` = +1 per fetched page;

    batches ×0.5 (from 2026-11-23 a batch depth request is 0.5 per page with

    results, without the bundle cap: `num=100` = 5); cache hit via `max_age` ×0.5. `engine=consensus` (search only) =

    the sum of one page per provider that returned results. **AI** (Claude writes, OpenAI''s Decisions API runs typed checks; a failed

    model step is never billed): an answer (`include_answer` on `/v1/search` and

    `/v1/news`, the model part of `/v1/answer`) = +2 over the result page, +4

    when it read pages (`include_content`, `/v1/answer` depth=standard);

    `/v1/extract` `summary` = +4 and `output_schema` = +4 per page;

    `/v1/research` = lite 30, standard 75, deep 200. Responses that used a

    model carry `meta.llm`. **AI assistants** (`/v1/chatgpt`, `/v1/gemini`, `/v1/copilot`,

    `/v1/ai_mode`, `/v1/brave_ai`): a `prompt` sent to the public assistant

    as a logged-out user; the response is its answer

    (`text`, `markdown`) with the `sources` it cited or listed,

    `search_queries`, `products` and `places` where the assistant shows them,

    and `mentions` for the brands and domains in `track`.'
servers:
- url: https://api.serpkite.com
- url: http://localhost:8080
security:
- bearer: []
tags:
- name: Monitors
paths:
  /v1/monitors:
    post:
      summary: Create a monitor — a saved /v1/search or /v1/news request (new results) or a…
      operationId: createMonitor
      description: 'Runs hourly to monthly (`interval` or `interval_seconds`, default daily); the

        first run is due right away (unless `active: false`). With `endpoint: webpage`

        the monitor watches `url` and reports the page (Markdown, `change: new|changed`)

        whenever its content changes (1 credit per check). Each run is billed like the

        search it makes (1 credit per 10 results, `num: 100` is 7; empty and failed runs

        are free) on the monitor''s key: the key that created it, or last changed or ran

        it, under that key''s limits and the account''s spend cap. Only the fields below

        are kept; any other search field is a `400`. Results among the

        top `num` not seen in earlier runs are POSTed as a `monitor.results` webhook when

        the monitor has a `webhook_url`, and are always listed in its run history

        (`GET /v1/monitors/{id}/runs`, results kept 24 hours). At most 100 monitors per

        account (403 `forbidden`). A monitor pauses itself after 10 failed runs in a row

        (search errors, insufficient credits or webhook failures) or when its key is

        revoked; set `active: true` to resume.


        With an AI assistant `endpoint` (chatgpt, gemini, copilot, ai_mode,

        brave_ai) the monitor asks `prompt` every run and reports every answer

        (AI visibility is tracked over time): `text`, `markdown`, `sources`,

        `new_sources` (links not seen in earlier runs), `search_queries`,

        `products` and, with `track`, `mentions`. Each run costs the endpoint''s

        price.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorCreateRequest'
            example:
              q: ai agents
              endpoint: news
              interval: hourly
              name: agents news
              webhook_url: https://example.com/hooks/serpkite
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Monitor'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
    get:
      summary: List the account's monitors
      operationId: listMonitors
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorList'
        '401':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
  /v1/monitors/{id}:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    get:
      summary: Get a monitor
      operationId: getMonitor
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Monitor'
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
    patch:
      summary: 'Update a monitor: name, interval, webhook_url (empty string removes it)…'
      operationId: updateMonitor
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorUpdateRequest'
            example:
              active: false
              interval: daily
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Monitor'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
    delete:
      summary: Delete a monitor
      operationId: deleteMonitor
      responses:
        '204':
          description: Deleted
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
  /v1/monitors/{id}/runs:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    - name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    - name: before
      in: query
      description: '`next_before` of the previous page'
      schema:
        type: string
        format: uuid
    get:
      summary: A monitor's run history, newest first (runs are kept 30 days, their results 24…
      operationId: listMonitorRuns
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorRunList'
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
  /v1/monitors/{id}/run:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    post:
      summary: Run a monitor now (it becomes due at the next scheduler poll, within about 30…
      description: 'The monitor must be active. A paused monitor returns 409; resume it with

        PATCH {"active": true} first (resuming also makes it due immediately).

        The monitor is bound to the calling key, which this and later runs are billed to.'
      operationId: runMonitor
      responses:
        '202':
          description: Scheduled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Monitor'
        '401':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '409':
          $ref: '#/components/responses/Error'
        '429':
          $ref: '#/components/responses/Error'
      tags:
      - Monitors
webhooks:
  crawl.completed:
    post:
      operationId: crawlCompletedWebhook
      summary: A crawl finished (completed, failed or canceled)
      description: 'Sent to the crawl''s `webhook_url` (or the account webhook). Signed like batch

        webhooks: `X-SerpKite-Event: crawl.completed`, `X-SerpKite-Delivery` (the task id),

        `X-SerpKite-Timestamp` and `X-SerpKite-Signature: v1=.")>`. Retried with backoff until a 2xx.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCompletedEvent'
      responses:
        '200':
          description: Any 2xx acknowledges the delivery
      tags:
      - Monitors
  research.completed:
    post:
      operationId: researchCompletedWebhook
      summary: A research task finished (completed, failed or canceled)
      description: 'Sent to the task''s `webhook_url` (or the account webhook), signed like

        `crawl.completed` with `X-SerpKite-Event: research.completed`. `result` is the

        ResearchResult.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCompletedEvent'
      responses:
        '200':
          description: Any 2xx acknowledges the delivery
      tags:
      - Monitors
  monitor.results:
    post:
      operationId: monitorResultsWebhook
      summary: A monitor run found results it had not seen before
      description: 'Sent to the monitor''s `webhook_url` (when it has one) only when a run finds new results (the first

        run sends everything it found, with `first_run: true`). Same signature headers as

        the other webhooks, with `X-SerpKite-Event: monitor.results` and

        `X-SerpKite-Delivery: `. Tried up to 3 times per run; results that could

        not be delivered stay new for the next run (and are in the run history either way).'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MonitorResultsEvent'
      responses:
        '200':
          description: Any 2xx acknowledges the delivery
      tags:
      - Monitors
components:
  schemas:
    DomainList:
      description: 'Domains as an array or a comma-separated string, at most 20: a host (`example.com`, subdomains match), a host with a path prefix (`github.com/org`) or a TLD (`.gov`).'
      oneOf:
      - type: string
        examples:
        - github.com,en.wikipedia.org
      - type: array
        maxItems: 20
        items:
          type: string
        examples:
        - - github.com/serpkite
          - .gov
    TrackList:
      type: array
      maxItems: 20
      items:
        type: string
        maxLength: 100
      description: Brands ("Acme") and domains ("acme.com", subdomains match) to check the answer for; each comes back in `mentions`
    MonitorSearch:
      type: object
      description: 'The saved request a monitor reruns: a search (q and options), for webpage monitors url (and country), or for AI assistant monitors prompt (with country, language, web_search and track)'
      properties:
        q:
          type: string
        url:
          type: string
        prompt:
          type: string
          description: AI assistant monitors
        web_search:
          type: boolean
          description: endpoint=chatgpt
        track:
          type: array
          items:
            type: string
        country:
          type: string
        language:
          type: string
        location:
          type: string
        time:
          type: string
          enum:
          - hour
          - day
          - week
          - month
          - year
        num:
          type: integer
        device:
          type: string
        safe:
          type: string
        include_domains:
          type: array
          items:
            type: string
        exclude_domains:
          type: array
          items:
            type: string
        engine:
          type: array
          items:
            type: string
    MonitorList:
      type: object
      required:
      - results
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/Monitor'
    TaskCompletedEvent:
      type: object
      description: Body of `crawl.completed` and `research.completed`
      required:
      - event
      - id
      - kind
      - status
      - created_at
      - credits_used
      properties:
        event:
          type: string
          enum:
          - crawl.completed
          - research.completed
        id:
          type: string
          format: uuid
        kind:
          type: string
          enum:
          - crawl
          - research
        status:
          $ref: '#/components/schemas/TaskStatus'
        created_at:
          type: string
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
        credits_used:
          type: number
        poll_url:
          type: string
          description: Where the task (and its full result) can be fetched for 24 hours
        result:
          description: 'The CrawlResult or ResearchResult (null when the task failed without one, or when it is larger than 4 MB: see result_omitted)'
        result_omitted:
          type: boolean
          description: Set when the result was too large for the webhook; GET poll_url for it
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
    MonitorRunList:
      type: object
      required:
      - results
      - next_before
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/MonitorRun'
        next_before:
          type:
          - string
          - 'null'
          format: uuid
          description: Pass as `before` for the next page; null on the last page
    Monitor:
      type: object
      required:
      - id
      - name
      - endpoint
      - request
      - interval_seconds
      - webhook_url
      - active
      - next_run_at
      - runs
      - consecutive_failures
      - credits_used
      - created_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        endpoint:
          type: string
          enum:
          - search
          - news
          - webpage
          - chatgpt
          - gemini
          - copilot
          - ai_mode
          - brave_ai
        request:
          $ref: '#/components/schemas/MonitorSearch'
        metadata:
          type:
          - object
          - 'null'
          additionalProperties: true
        interval_seconds:
          type: integer
        webhook_url:
          type:
          - string
          - 'null'
          description: Null when new results are only kept in the run history
        active:
          type: boolean
        next_run_at:
          type: string
          format: date-time
        last_run_at:
          type:
          - string
          - 'null'
          format: date-time
        last_status:
          type:
          - string
          - 'null'
          enum:
          - ok
          - error
          - webhook_failed
          - paused
          - null
        last_error:
          type:
          - string
          - 'null'
        last_new_results:
          type: integer
          description: New results found by the last run
        runs:
          type: integer
        consecutive_failures:
          type: integer
          description: Failed runs in a row; the monitor pauses itself at 10
        credits_used:
          type: number
          description: Total over all runs
        created_at:
          type: string
          format: date-time
    EngineParam:
      description: 'Which search providers may answer. `google` (the default) is Google only; Google requests are retried internally before failing. `auto` allows falling back to other enabled providers, in route order, when Google is blocked or times out. A single other provider (`brave`) or a list (`["google","brave"]`, or `google,brave` in a query string) restricts the request to those providers. `meta.engine` always names the provider that answered and credits follow that provider''s price. A key with "Allow fallback to other search engines" on treats a request without `engine` as `auto`. `consensus` (`/v1/search` only; 400 elsewhere, and it can''t be combined with other names) queries several independent search indexes in parallel (at most one Bing-backed provider, so Yahoo or DuckDuckGo never count as agreeing with Bing), merges the results by URL and ranks them by how many providers returned each one. Each result lists its `sources`; `meta.engine` is `consensus` and `meta.route` lists every provider attempt. Up to three providers answer: a provider that fails or comes back empty hands its turn to the next one in the route. It stops once `num` unique results are in hand or after a few seconds, and costs the sum of one page at each provider that returned results (providers that failed, came back empty or were never started are free). `yandex` serves `/v1/search` from Yandex web search (no `time` filter or `safe: active`: a request naming it gets a 400 with them) and `/v1/maps`, `/v1/places` and `/v1/place` from Yandex Maps, where the deployment enables it (otherwise a 503 `unavailable`, not charged); with `auto` it is the last fallback there.'
      oneOf:
      - type: string
        default: google
        examples:
        - google
        - auto
        - consensus
        - brave
        - google,brave
      - type: array
        items:
          $ref: '#/components/schemas/Provider'
        minItems: 1
    Provider:
      type: string
      description: A search provider (docs/providers). Which verticals each serves depends on the deployment.
      enum:
      - google
      - brave
      - bing
      - yahoo
      - duckduckgo
      - mojeek
      - wikipedia
      - yandex
    MonitorUpdateRequest:
      type: object
      description: Only the fields sent change. Search fields are merged into the saved search; a changed search (or endpoint) makes the next run report every result as new.
      properties:
        name:
          type: string
          maxLength: 200
        interval:
          type: string
          enum:
          - hourly
          - daily
          - weekly
        interval_seconds:
          type: integer
          minimum: 3600
          maximum: 2592000
        webhook_url:
          type: string
          description: A webhook URL, or an empty string to remove it
        active:
          type: boolean
          description: true resumes a paused monitor (due now, failure streak cleared)
        metadata:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: Replaces the metadata (null clears it)
        endpoint:
          type: string
          enum:
          - search
          - news
          - webpage
          - chatgpt
          - gemini
          - copilot
          - ai_mode
          - brave_ai
          description: Switching between a search, a page and an AI prompt drops the other kinds' fields
        q:
          type: string
          minLength: 1
          maxLength: 2048
        prompt:
          type: string
          minLength: 1
          maxLength: 4000
        web_search:
          type: boolean
        track:
          $ref: '#/components/schemas/TrackList'
        url:
          type: string
          format: uri
          description: endpoint=webpage only
        country:
          type: string
        language:
          type: string
        location:
          type: string
        time:
          type: string
          enum:
          - hour
          - day
          - week
          - month
          - year
        num:
          type: integer
          minimum: 1
          maximum: 100
        device:
          type: string
          enum:
          - desktop
          - mobile
        safe:
          type: string
          enum:
          - active
          - 'off'
        include_domains:
          $ref: '#/components/schemas/DomainList'
        exclude_domains:
          $ref: '#/components/schemas/DomainList'
        engine:
          $ref: '#/components/schemas/EngineParam'
    MonitorRun:
      type: object
      required:
      - id
      - status
      - new_results
      - credits_used
      - created_at
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
          - ok
          - error
          - webhook_failed
          - paused
        error:
          type:
          - string
          - 'null'
        new_results:
          type: integer
        results:
          type:
          - array
          - 'null'
          description: The new results (OrganicResult-shaped for search, NewsResult for news, MonitorPageChange for webpage, one MonitorAnswer per run for AI assistants); null when there were none or after 24 hours
          items:
            type: object
            additionalProperties: true
        credits_used:
          type: number
        webhook_status:
          type:
          - string
          - 'null'
          enum:
          - delivered
          - failed
          - none
          - null
          description: none = the monitor has no webhook; null = nothing to send
        created_at:
          type: string
          format: date-time
    MonitorCreateRequest:
      type: object
      description: Search and news monitors need `q`; webpage monitors need `url` and take only `country` besides; AI assistant monitors need `prompt` and take `country`, `language` (not chatgpt), `web_search` (chatgpt) and `track`
      properties:
        q:
          type: string
          minLength: 1
          maxLength: 2048
        url:
          type: string
          format: uri
          description: 'endpoint=webpage: the page to watch for content changes'
        prompt:
          type: string
          minLength: 1
          maxLength: 4000
          description: 'AI assistant monitors: the prompt asked every run'
        web_search:
          type: boolean
          description: endpoint=chatgpt
        track:
          $ref: '#/components/schemas/TrackList'
        endpoint:
          type: string
          enum:
          - search
          - news
          - webpage
          - chatgpt
          - gemini
          - copilot
          - ai_mode
          - brave_ai
          default: search
        metadata:
          type: object
          additionalProperties: true
          description: Your own JSON object (at most 2 KB), echoed on the monitor and in its webhooks
        name:
          type: string
          maxLength: 200
        interval:
          type: string
          enum:
          - hourly
          - daily
          - weekly
          default: daily
        interval_seconds:
          type: integer
          minimum: 3600
          maximum: 2592000
          description: Overrides interval
        webhook_url:
          type: string
          format: uri
          description: Receives signed `monitor.results` events. Without one, read new results from the run history
        active:
          type: boolean
          default: true
          description: false creates the monitor paused
        country:
          type: string
        language:
          type: string
        location:
          type: string
        time:
          type: string
          enum:
          - hour
          - day
          - week
          - month
          - year
        num:
          type: integer
          minimum: 1
          maximum: 100
        device:
          type: string
          enum:
          - desktop
          - mobile
        safe:
          type: string
          enum:
          - active
          - 'off'
        include_domains:
          $ref: '#/components/schemas/DomainList'
        exclude_domains:
          $ref: '#/components/schemas/DomainList'
        engine:
          $ref: '#/components/schemas/EngineParam'
    TaskStatus:
      type: string
      enum:
      - queued
      - running
      - completed
      - failed
      - canceled
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: '403: spend_cap_reached, key_limit_reached, account_restricted, forbidden. 409/422: idempotency_key_reused. 422: target_unavailable (the URL you asked for failed: its site answered 404, its domain doesn''t resolve; resending won''t help), no_pages. 429: rate_limited, daily_limit_reached. 503: upstream_error, upstream_blocked, upstream_timeout, unavailable (the API never sends 502 or 504)'
              examples:
              - unauthorized
              - invalid_request
              - insufficient_credits
              - rate_limited
              - daily_limit_reached
              - spend_cap_reached
              - key_limit_reached
              - account_restricted
              - idempotency_key_reused
              - forbidden
              - not_found
              - upstream_error
              - upstream_blocked
              - upstream_timeout
              - unavailable
              - internal
            message:
              type: string
            request_id:
              type: string
    MonitorResultsEvent:
      type: object
      description: Body of `monitor.results`
      required:
      - event
      - monitor_id
      - run_id
      - name
      - endpoint
      - first_run
      - run_at
      - new_results
      - credits_used
      properties:
        event:
          type: string
          const: monitor.results
        monitor_id:
          type: string
          format: uuid
        run_id:
          type: string
          format: uuid
          description: Also the X-SerpKite-Delivery header; use it to deduplicate retries
        name:
          type: string
        endpoint:
          type: string
          enum:
          - search
          - news
          - webpage
          - chatgpt
          - gemini
          - copilot
          - ai_mode
          - brave_ai
        q:
          type: string
          description: The saved query (empty for webpage monitors
          absent for AI assistant monitors): null
        url:
          type: string
          description: webpage monitors only
        prompt:
          type: string
          description: AI assistant monitors only (instead of q)
        metadata:
          type:
          - object
          - 'null'
          additionalProperties: true
          description: The monitor's metadata
        first_run:
          type: boolean
          description: The first run since the monitor was created or its search changed
        run_at:
          type: string
          format: date-time
        new_results:
          type: array
          description: Results not seen in earlier runs (OrganicResult-shaped for search, NewsResult for news, MonitorPageChange for webpage); for AI assistant monitors the run's answer (one MonitorAnswer, sent every run)
          items:
            type: object
            additionalProperties: true
        credits_used:
          type: number
          description: This run's price
  responses:
    Error:
      description: Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
    oauth:
      type: oauth2
      description: OAuth 2.1 for the remote MCP server only (authorization code + PKCE S256; app-api.serpkite.com is the authorization server).
      flows:
        authorizationCode:
          authorizationUrl: https://app-api.serpkite.com/oauth/authorize
          tokenUrl: https://app-api.serpkite.com/oauth/token
          refreshUrl: https://app-api.serpkite.com/oauth/token
          scopes:
            mcp: Use the SerpKite MCP tools, billed to your account
            offline_access: Stay connected with refresh tokens