Signals API [BETA]

Retrieve news and data-change signals for companies.

⚠️

This API is still in beta suspect to small changes.

This document describes the two read-only signals endpoints under /api/v3/signals/:

  1. /api/v3/signals/news/ — news-style signals
  2. /api/v3/signals/data-changes/ — company data-change signals

Both endpoints share the same query and pagination model. They are intended for clients that want to stream signals across companies the user has access to, optionally constrained with VQL (Vainu Query Language) filters.

Quick decision guide

  • Use signals/news to retrieve human-authored news articles, contracts and other externally sourced events that have been linked to companies.
  • Use signals/data-changes to retrieve programmatically generated signals derived from changes in company master data (for example, a new financial figure, a registry change, or a new key person).

Authentication

Authentication Overview

Query parameters

The same parameters are accepted as JSON body (preferred), URL-encoded query string parameters, or a mix.

ParameterTypeDefaultDescription
queryVQL object or JSON stringFilter query. See filtering-company-data.
limitinteger (1–100)20Maximum number of items returned.
offsetinteger (0–100000)0Number of items to skip for pagination.
formatjson (default) or jsonljsonResponse renderer. jsonl streams one JSON object per line.

Filtering with VQL

The query parameter accepts a VQL operation tree. The most common building blocks:

  • business_ids — prefixed business ids (e.g. FI75432000).

  • countries — country codes the linked companies belong to

  • tags — signal tag ids.

  • vainu_date — ISO date or datetime string for time-windowing.

Response

  • 200 OK — a streaming JSON array (or JSON Lines, when format=jsonl). The body is produced incrementally; clients should consume it as a stream rather than buffering.
  • 400 Bad Request — invalid query, invalid order field, business-id scope mismatch (Vainu View tokens), or other validation error.
  • 401 Unauthorized — missing or invalid credentials.
  • An empty array (or empty body for JSONL) is returned when the query matches nothing or when the underlying Mongo query times out.

The tags field

tags is a list field, so a signal matches a tag filter when any of its tags matches. Values are integers;
numeric strings ("43543") are accepted and coerced.

Tag ids are global — the same id means the same thing on both endpoints, and the ids are the same ones used by the Vainu platform UI and by list/trigger definitions.

Tag reference

  • news — produced on news signals (/signals/news/)
  • data-changes — produced only on generated signals (/signals/data-changes/)
  • both — appears on either endpoint
Tag idNameEndpoint
8000080Change of Company Formdata-changes
8000072Change of Company Namedata-changes
8000069Change of Statusdata-changes
8000060New Auxiliary Namedata-changes
8000040Achieved 1MEUR Turnoverdata-changes
8000010Bankruptcynews
8000025Decline in Sales or Market Sharenews
8000024Earnings Releasenews
6967635EU fundingnews
4507212Financial (Equity) Arrangementsnews
8000018Growth in Sales or Market Sharenews
8000049Late Registration of Financial Statementdata-changes
43536Layoffsnews
8000037New Financial Statementdata-changes
2943080Protest listnews
8000016Public Decision on Financingnews
8000022Change in Strategynews
8000085Construction Projectnews
43543Fundingnews
43544Internationalizationnews
8000020Investment - Machines and equipmentnews
8000019Investment - New Place of Businessnews
8000021Investment – Softwarenews
8000077IPOnews
77883Mergers & Acquisitionsnews
8000050New Company Establisheddata-changes
8000015Eventsnews
2999389Registered web-address / domainnews
8000029Legal Disputenews
2932622New Product / Servicenews
4507192Partnershipsnews
8000002Real Estate Transactionsnews
3325073Relocationboth
8000076Stock Tradenews
43545Won Contractsnews
8000071Apartment sales noticenews
43551Awardsnews
6931739Change in Credit Ratingnews
8000090Environmental Impact Assessmentnews
8000081Granted Energy Certificatedata-changes
8000058New Vehicledata-changes
8000086Temporary exceptions in businessnews
6067468Change in Legislationnews
2940230Government & Public Contract Noticesnews
8000056Granted Alcohol Licensedata-changes
8000053Granted Cargo Transport Licensedata-changes
8000052Granted Public Transport Licensedata-changes
8000054Granted social services or health services licensedata-changes
8000051Granted Taxi Licensedata-changes
6944439Minutesnews
8000068New Trademarkdata-changes
8000013Patentsnews
7200000Permitboth
8000039Change in Board of Directorsboth
8000038Change of CEOboth
77884Changes in Personnelboth
6893088Key Person Appointmentnews
8000034Key Person Departurenews
43535Open Positionsnews
8000075New Advertising Technologydata-changes
8000066New Analytics Tooldata-changes
8000064New ATS Tooldata-changes
8000062New Chat and Support Tooldata-changes
8000063New CMS Platformdata-changes
8000083New CRM Technologydata-changes
8000061New e-commerce technologydata-changes
8000059New Marketing Automation Tooldata-changes
8000074New Payment Technologydata-changes
8000073New Reservation Technologydata-changes
8000065New Server Technologydata-changes
8000084New Social Media Channeldata-changes
4467311New Technologyboth

Filtering signals by tag

Both v3 signals endpoints classify every signal with tags — integer ids such as 43543 (Funding) or
8000038 (Change of CEO).

Including tags

Use ?IN with a list of tag ids. A signal matches if it carries at least one of them (logical OR).

{
  "query": {
    "?ALL": [
      {"?IN": {"tags": [43543, 77883]}}
    ]
  }
}

Funding or Mergers & Acquisitions signals.

?EQ matches a single tag and is equivalent to a one-element ?IN:

{"?ALL": [{"?EQ": {"tags": 43543}}]}

Requiring several tags at once

Repeat the tag operation inside ?ALL — each entry must match, so the signal has to carry all listed tags:

{
  "query": {
    "?ALL": [
      {"?IN": {"tags": [43543]}},
      {"?IN": {"tags": [43535]}}
    ]
  }
}

Signals tagged both Funding and Open Positions.

Note the difference: {"?IN": {"tags": [43543, 43535]}} is OR, two separate ?IN entries under ?ALL is AND.

Excluding tags

Wrap the tag operation in ?NOT. This is the standard way to filter out unwanted signal types:

{
  "query": {
    "?ALL": [
      {"?NOT": {"?IN": {"tags": [43535, 77884]}}}
    ]
  }
}

Everything except Open Positions and Changes in Personnel signals.

Signals with no tags at all are also returned by an exclusion filter, because an empty tags array does not
contain any of the excluded ids.

?NOT around ?EQ excludes a single tag:

{"?ALL": [{"?NOT": {"?EQ": {"tags": 43535}}}]}

Combining include and exclude

The most common real-world shape — include a topic set, subtract the noise, and window by date:

{
  "query": {
    "?ALL": [
      {"?IN": {"tags": [43543, 77883, 8000019]}},
      {"?NOT": {"?IN": {"tags": [43535]}}},
      {"?GTE": {"vainu_date": "2026-01-01"}},
      {"?IN": {"business_ids": ["FI26550449", "SE5560000009"]}}
    ]
  },
  "limit": 100,
}

Include and exclude clauses within the same ?ALL are AND-ed, so a signal must match every clause.

Signals with or without tags

?EXISTS checks whether the signal has any tags:

{"?ALL": [{"?EXISTS": {"tags": true}}]}
  • true → only signals that have at least one tag
  • false → only signals with an empty or missing tags array

Filtering Signals with Tags Example

query may be sent as a JSON object in the request body or as a URL-encoded JSON string. These are equivalent:

import requests

# JSON body (GET with body, or POST — both are treated as list requests)
response = requests.post(
    "https://api.vainu.io/api/v3/signals/news/",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "query": {
            "?ALL": [
                {"?IN": {"tags": [43543]}},
            ],
        },
        "limit": 50,
    },
)
response.raise_for_status()
signals = response.json()

1) News signals API

Base path: /api/v3/signals/news/

Returns news-style signals from the lead collection, ordered by vainu_date descending by default.

Item schema

Each item contains:

FieldTypeDescription
idstringMongo _id of the signal.
titlestringSignal title, with translation placeholders resolved.
contentstringSignal body text. May be truncated to 10 characters for GDPR-restricted domains.
linkstringSource URL of the article or document.
vainu_datedatetimeVainu-assigned timestamp of the event.
tagsarray of {id, value}Topical tags attached to the signal (e.g. funding, new key person).
organizationsarray of objectsCompanies linked to the signal. Each item contains business_id, country, logo_url, name.

Example

import requests

response = requests.post(
    "https://api.vainu.io/api/v3/signals/news/",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "query": {
            "?ALL": [
                {"?IN": {"business_ids": ["FI25578642", "FI28229966"]}},
                {"?GTE": {"vainu_date": "2026-01-01"}},
            ],
        },
        "limit": 10,
    },
)
response.raise_for_status()
signals = response.json()

Sample response (truncated):

[
  {
    "id": "65f0a1b2c3d4e5f6a7b8c9d0",
    "title": "Acme Oy raises 12 M€ Series B",
    "content": "Acme Oy announced today...",
    "link": "https://example.com/articles/acme-series-b",
    "vainu_date": "2026-04-22T08:15:00Z",
    "tags": [{"id": 4123, "value": "Funding"}],
    "organizations": [
      {
        "business_id": "FI12345678",
        "country": "FI",
        "logo_url": "https://...",
        "name": "Acme Oy"
      }
    ]
  }
]

2) Data-change signals API

Base path: /api/v3/signals/data-changes/

Returns generated signals from the generated_lead collection, ordered by vainu_date descending by default. These signals are produced when monitored fields on a company record change (for example, revenue update, CEO change, new registration data).

Item schema

Each item contains:

FieldTypeDescription
idstringMongo _id of the generated signal.
titlestringHuman-readable headline rendered in English from dynamic_values and tags.
contentstringHuman-readable body rendered in English from dynamic_values and tags.
vainu_datedatetimeVainu-assigned timestamp of the change event.
tagsarray of {id, value}Topical tags describing the kind of change.
organizationsarray of objectsCompanies linked to the signal. Each item contains business_id, country, logo_url, name.
dynamic_valuesarray of objectsRaw structured representation of the change. Each entry typically carries a key, a type, and old/new values, e.g. {"key": "revenue", "old_revenue": 1000000, "revenue": 1500000, "currency_code": "EUR"}.

Example

import requests

response = requests.post(
    "https://api.vainu.io/api/v3/signals/data-changes/",
    headers={"Authorization": f"Bearer {token}"},
    json={
        "query": {
            "?ALL": [
                {"?IN": {"business_ids": ["FI12345678"]}},
                {"?GTE": {"vainu_date": "2026-01-01"}},
            ],
        },
        "limit": 20,
    },
)
response.raise_for_status()
signals = response.json()

Sample response (truncated):

[
  {
    "id": "66001a2b3c4d5e6f7a8b9c0d",
    "title": "Vainu Finland Oy revenue increased",
    "content": "Vainu Finland Oy  reported revenue growth from 1.0 M€ to 1.5 M€.",
    "vainu_date": "2026-03-30T00:00:00Z",
    "tags": [{"id": 5567, "value": "Financial growth"}],
    "organizations": [
      {
        "business_id": "FI12345678",
        "country": "FI",
        "logo_url": "https://...",
        "name": "Acme Oy"
      }
    ],
    "dynamic_values": [
      {
        "type": "revenue_change",
        "key": "revenue",
        "old_revenue": 1000000,
        "revenue": 1500000,
        "currency_code": "EUR"
      }
    ]
  }
]

Pagination, streaming, and exports

  • Use limit and offset for normal pagination, up to a maximum offset of 100000.
  • For larger exports, prefer format=jsonl with a moderate batch_size (for example 1000) so results stream as they leave the database.
  • Sorting is required for stable pagination; the default -vainu_date is appropriate for most clients.