tickerbot-io News API

Article-level news with sentiment, queryable through the same SQL grammar as /scan.

Operations 1

GET /v2/news SQL query over the news_article archive. Returns articles or aggregate rollups #

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/tickerbot-io:tickerbot-io-news-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

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

tickerbot-io-news-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Tickerbot News API
  version: 2.69.6
  summary: The stock market API designed for agents.
  description: 'Tickerbot is the stock market API designed for agents. We serve real-time and historical market data as one queryable table. You and your agents can ask it anything, without building anything custom. Every ticker is a row. Every data point is a column. 23,000+ tickers, covering US stocks and ETFs, FX, crypto, rates and metals. 452+ columns, covering raw data, calculations and conditions: price, volume, price action, technicals, fundamentals, events, profile, analyst ratings and news. Refreshed every minute, historized back to 2003. The table is the API: /v2/tickers/ returns one ticker''s whole row, /v2/signals/ returns every ticker that matches one column, /v2/scan returns every ticker that matches a SQL WHERE clause. Every read runs live or as of any date. The power is in the columns: every ticker gets the same columns, query any of them, in any combination. Ask any question: anything you want to know about the market is a query over the table. Build any trading tool: anything you want to build is a few queries in sequence. Get a free API key, or paste our setup prompt and your agent connects over MCP. Free plan: 10,000 calls a month, no card required.


    All responses carry a top-level `as_of` ISO timestamp indicating data vintage. List and history responses use opaque cursor pagination — treat `next_cursor` as a black-box token and pass it back via `?cursor=` on the next request.'
  contact:
    name: Tickerbot Support
    url: https://tickerbot.io/support
    email: support@tickerbot.io
  license:
    name: Proprietary
    url: https://tickerbot.io/terms
servers:
- url: https://api.tickerbot.io
  description: Production
security:
- apiBearer: []
tags:
- name: News
  description: Article-level news with sentiment, queryable through the same SQL grammar as /scan.
paths:
  /v2/news:
    get:
      tags:
      - News
      summary: SQL query over the news_article archive. Returns articles or aggregate rollups
      operationId: search_news
      description: 'SQL query over the news archive. Article rows, or rollups when you group them.


        Notes:

        - Without `group_by`, each result is one article. By default it has 7 columns (`id`, `time_published`, `title`, `source`, `tickers`, `overall_sentiment_score`, `overall_sentiment_label`); name any of the 16 columns listed under `q` in `select` to get them. With `group_by` set, results become aggregate rollups and the default SELECT becomes `, COUNT(*) AS volume`. Use the `tk` alias — an auto-UNNEST of `tickers` — to roll up per ticker without writing the join.

        - Same scoping sugar as `/v2/events`: `ticker`/`tickers`/`universe` and `from`/`to` AND into the WHERE clause, and `q` is optional once any of them is present — `?ticker=NVDA&from=2026-07-01` needs no SQL at all.

        - There is no `asof` parameter here: "as of" is a WHERE filter on `time_published`, because every article already carries the moment it was published.

        - Both `GET` and `POST` are supported; the `POST` body takes the same parameter names, for queries too long to fit in a URL.

        - `/v2/news` is the canonical path — the log-shaped endpoints read uniformly (`/v2/scan`, `/v2/news`, `/v2/events`) — and the original spelling `/v2/news/scan` answers identically. The anonymous sandbox mirrors both spellings, GET and POST, with the same limits.'
      parameters:
      - name: search
        in: query
        required: false
        description: 'Full-text search over `title` + `summary` — websearch grammar: `apple earnings` (all words), `"price target"` (phrase), `chips OR semiconductors`, `-crypto` (negation). Max 200 chars. ANDs with `q` and the scoping params. Language-stemmed English.'
        schema:
          type: string
          example: apple earnings
      - name: q
        in: query
        required: false
        description: 'WHERE clause over the news_article table. Max 4000 chars. Required UNLESS `search` or a scoping param (`ticker`/`tickers`/`universe`/`from`/`to`) is present — the simplest call needs no SQL. Queryable columns: `time_published`, `title`, `summary`, `source`, `source_domain`, `category`, `authors`, `topics`, `overall_sentiment_score`, `overall_sentiment_label`, `tickers`, `ticker_data`, `banner_image`, `url`, `id`, `created_at` — plus `tk`, the per-ticker UNNEST alias. Signal/state columns are not joinable here.'
        schema:
          type: string
          example: overall_sentiment_score < -0.2
      - name: ticker
        in: query
        required: false
        description: Articles mentioning this symbol (ANDed with `q`).
        schema:
          type: string
          example: NVDA
      - name: tickers
        in: query
        required: false
        description: Comma list, up to 50 — articles mentioning ANY of them. Not combinable with `ticker` or `universe`.
        schema:
          type: array
          example: NVDA,AMD
          items:
            type: string
      - name: universe
        in: query
        required: false
        description: Universe slug — articles mentioning any member. Not combinable with `ticker`/`tickers`.
        schema:
          type: string
          example: top_100
      - name: min_relevance
        in: query
        required: false
        description: 'With `ticker`/`tickers`/`universe`: keeps an article only when it is about one of those tickers, meaning that ticker''s relevance score (`ticker_data.<TICKER>.rel`, 0 to 1) is at least this value. `0.8` drops passing mentions. Omitted: every article that mentions them. `400` without a ticker scope; inside `q`, filter `(ticker_data->''AAPL''->>''rel'')::numeric >= 0.8` directly.'
        schema:
          type: number
          example: 0.8
      - name: from
        in: query
        required: false
        description: 'Earliest `time_published` (inclusive) — strict ISO: `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM[:SS]Z`.'
        schema:
          type: string
          example: '2026-09-21'
      - name: to
        in: query
        required: false
        description: Articles strictly before this instant — same strict ISO subset, matching `/v2/events`.
        schema:
          type: string
          example: '2026-10-19'
      - name: group_by
        in: query
        required: false
        description: 'AGGREGATE MODE: comma-separated group keys, 1-6 (max 1000 chars). Switches the response to rollup rows. Use `tk` to roll up per ticker without writing the UNNEST. Name a key with `AS` to choose its JSON key; an un-named expression is named for you rather than returned as `?column?`.'
        schema:
          type: array
          example: tk
          items:
            type: string
      - name: select
        in: query
        required: false
        description: Columns/expressions to return (max 2000 chars). Defaults to article columns (no `group_by`) or `<group_by cols>, COUNT(*) AS volume` (with `group_by`).
        schema:
          type: array
          example: tk, COUNT(*) AS volume
          items:
            type: string
      - name: having
        in: query
        required: false
        description: HAVING clause on the aggregate (max 1000 chars). Requires `group_by`.
        schema:
          type: string
          example: COUNT(*) >= 10
      - name: order
        in: query
        required: false
        description: Sort — a bare column name or SELECT alias only (put expressions in `select` and order by their alias). Defaults to `time_published` (article rows) or `volume` (aggregate rows; with your own `select` that has no `volume` column, its last column's alias).
        schema:
          type: string
          example: time_published
      - name: dir
        in: query
        required: false
        description: Sort direction.
        schema:
          type: string
          enum:
          - asc
          - desc
          default: desc
      - name: limit
        in: query
        required: false
        description: Page size. Max 1000.
        schema:
          type: integer
          default: 50
          example: 5
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a prior response's `next_cursor`.
        schema:
          type: string
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                type: object
                properties:
                  as_of:
                    type:
                    - string
                    - 'null'
                    description: Server time this response was assembled (ISO 8601).
                  query:
                    type:
                    - object
                    - 'null'
                    description: Your filters, echoed.
                  count:
                    type:
                    - number
                    - 'null'
                    description: Rows in this page.
                  next_cursor:
                    type:
                    - string
                    - 'null'
                    description: Opaque token for the next page; `null` on the last page, and when a custom `select` leaves out the `order` column (keep it in `select` to page). Pass it back as `cursor`.
                  truncated:
                    description: 'Aggregate mode only (`group_by`): `true` when the rollup stopped at its row cap.'
                  results:
                    type:
                    - array
                    - 'null'
                    description: 'Article rows, or rollup rows when you passed `group_by`. Aggregate responses add `truncated: true` when `limit` cut the group list.'
        '400':
          description: '`bad_request` or `invalid_query` — missing/malformed `q`, unknown column, or invalid parameter.'
        '401':
          description: Missing or invalid API key.
        '402':
          description: No active plan on the account (rare — new signups land on the Free plan automatically).
        '429':
          description: Rate limit exceeded for your plan.
components:
  securitySchemes:
    apiBearer:
      type: http
      scheme: bearer
      bearerFormat: API key (tb_live_… or tb_test_…)
      description: 'API keys are minted in the dashboard. Send as `Authorization: Bearer <key>`.'